1use std::collections::HashMap;
2
3use egui::{Id, Pos2};
4use eyre::{Result, WrapErr as _};
5use num::bigint::ToBigInt as _;
6use num::{BigInt, BigUint, One, ToPrimitive, Zero};
7use serde::{Deserialize, Serialize};
8use surfer_translation_types::{TranslationPreference, Translator, VariableValue};
9use tracing::{error, info, warn};
10
11use crate::annotation::{Annotatable, Annotation};
12use crate::annotation_list::AnnotationGroup;
13use crate::data_container::DataContainer;
14use crate::displayed_item::{
15 DisplayedDivider, DisplayedFieldRef, DisplayedGroup, DisplayedItem, DisplayedItemRef,
16 DisplayedStream, DisplayedTimeLine, DisplayedVariable,
17};
18use crate::displayed_item_tree::{DisplayedItemTree, ItemIndex, TargetPosition, VisibleItemIndex};
19use crate::graphics::{Graphic, GraphicId};
20use crate::item_drawing_info::ItemDrawingInfo;
21use crate::transaction_container::{StreamScopeRef, TransactionRef, TransactionStreamRef};
22use crate::transactions::calculate_rows_of_stream;
23use crate::translation::{DynTranslator, TranslatorList, VariableInfoExt};
24use crate::variable_name_type::VariableNameType;
25use crate::view::DrawingContext;
26use crate::viewport::Viewport;
27use crate::wave_container::{
28 AnalogCacheKey, ScopeRef, ScopeRefExt as _, VariableMeta, VariableRef, VariableRefExt,
29 WaveContainer,
30};
31use crate::wave_source::{WaveFormat, WaveSource};
32use crate::wellen::LoadSignalsCmd;
33use ftr_parser::types::{StreamId, Transaction};
34use itertools::Itertools;
35use std::fmt::Formatter;
36use std::ops::Not;
37
38pub const PER_SCROLL_EVENT: f32 = 50.0;
39pub const SCROLL_EVENTS_PER_PAGE: f32 = 20.0;
40
41#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
42pub enum ScopeType {
43 WaveScope(ScopeRef),
44 StreamScope(StreamScopeRef),
45}
46
47impl std::fmt::Display for ScopeType {
48 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
49 match self {
50 ScopeType::WaveScope(w) => w.fmt(f),
51 ScopeType::StreamScope(s) => s.fmt(f),
52 }
53 }
54}
55
56#[derive(Serialize, Deserialize)]
57pub struct WaveData {
58 #[serde(skip, default = "DataContainer::__new_empty")]
59 pub inner: DataContainer,
60 pub source: WaveSource,
61 pub format: WaveFormat,
62 pub active_scope: Option<ScopeType>,
63 pub items_tree: DisplayedItemTree,
65 pub displayed_items: HashMap<DisplayedItemRef, DisplayedItem>,
66 pub display_item_ref_counter: usize,
68 pub viewports: Vec<Viewport>,
69 pub cursor: Option<BigInt>,
70 pub markers: HashMap<u8, BigInt>,
71 #[serde(default)]
72 pub selected_annotation: Option<Id>,
73
74 #[serde(default)]
75 pub annotations: Vec<Annotation>,
76 pub annotation_groups: Vec<AnnotationGroup>, pub annotation_list_visible: bool,
78 #[serde(default)]
79 pub annotation_counter: i32,
80 pub last_active_viewport_idx: usize,
81 #[serde(skip, default)]
82 pub(crate) annotation_menu_pos: Option<Pos2>,
83 #[serde(skip, default)]
84 pub annotation_menu_time: Option<BigInt>,
85
86 pub focused_item: Option<VisibleItemIndex>,
87 pub focused_transaction: (Option<TransactionRef>, Option<Transaction>),
88 pub default_variable_name_type: VariableNameType,
89 pub scroll_offset: f32,
90 pub display_variable_indices: bool,
91 pub graphics: HashMap<GraphicId, Graphic>,
92 #[serde(skip)]
94 pub drawing_infos: Vec<ItemDrawingInfo>,
95 #[serde(skip)]
96 pub top_item_draw_offset: f32,
97 #[serde(skip)]
98 pub total_height: f32,
99 #[serde(skip)]
100 pub old_max_timestamp: Option<BigInt>,
101 #[serde(skip)]
103 pub cache_generation: u64,
104 #[serde(skip)]
107 pub inflight_caches:
108 HashMap<AnalogCacheKey, std::sync::Arc<crate::analog_signal_cache::AnalogCacheEntry>>,
109 #[serde(skip, default)]
111 pub(crate) cached_time_offset: BigInt,
112}
113
114fn select_preferred_translator(var: &VariableMeta, translators: &TranslatorList) -> String {
115 let mut preferred: Vec<_> = translators
116 .all_translators()
117 .iter()
118 .filter_map(|t| match t.translates(var) {
119 Ok(TranslationPreference::Prefer) => Some(t.name()),
120 Ok(TranslationPreference::Yes) => None,
121 Ok(TranslationPreference::No) => None,
122 Err(e) => {
123 error!(
124 "Failed to check if {} translates {}\n{e:#?}",
125 t.name(),
126 var.var.full_path_string_no_index()
127 );
128 None
129 }
130 })
131 .collect();
132 if preferred.len() > 1 {
133 if var.num_bits == Some(1) {
136 let bit = "Bit".to_string();
137 preferred.retain(|x| x != &bit);
138 } else {
139 let signed = "Signed".to_string();
141 preferred.retain(|x| x != &signed);
142 if preferred.len() > 1 {
143 let enum_translator = "Enum".to_string();
146 preferred.retain(|x| x != &enum_translator);
147 }
148 }
149 if preferred.len() > 1 {
150 warn!(
151 "More than one preferred translator for variable {} in scope {}: {}",
152 var.var.name,
153 var.var.path.full_name(),
154 preferred.join(", ")
155 );
156 preferred.sort();
157 }
158 }
159 preferred
161 .pop()
162 .unwrap_or_else(|| translators.default.clone())
163}
164
165pub fn variable_translator<'a, F>(
166 translator: Option<&String>,
167 field: &[String],
168 translators: &'a TranslatorList,
169 meta: F,
170) -> &'a DynTranslator
171where
172 F: FnOnce() -> Result<VariableMeta>,
173{
174 let translator_name = translator.cloned().unwrap_or_else(|| {
175 if field.is_empty() {
176 meta().as_ref().map_or_else(
177 |e| {
178 warn!("{e:#?}");
179 translators.default.clone()
180 },
181 |meta| select_preferred_translator(meta, translators).clone(),
182 )
183 } else {
184 translators.default.clone()
185 }
186 });
187
188 (translators.get_translator(&translator_name)) as _
189}
190
191impl WaveData {
192 #[must_use]
193 pub fn update_with_waves(
194 mut self,
195 new_waves: Box<WaveContainer>,
196 source: WaveSource,
197 format: WaveFormat,
198 translators: &TranslatorList,
199 keep_unavailable: bool,
200 ) -> (WaveData, Option<LoadSignalsCmd>) {
201 let active_scope = self.active_scope.take().filter(|m| {
202 if let ScopeType::WaveScope(w) = m {
203 new_waves.scope_exists(w)
204 } else {
205 false
206 }
207 });
208 let display_items = Self::update_displayed_items(
209 &new_waves,
210 &self.displayed_items,
211 keep_unavailable,
212 translators,
213 &mut self.items_tree,
214 );
215
216 let old_max_timestamp = self.max_timestamp();
217 let mut new_wavedata = WaveData {
218 inner: DataContainer::Waves(*new_waves),
219 source,
220 format,
221 active_scope,
222 items_tree: self.items_tree,
223 displayed_items: display_items,
224 display_item_ref_counter: self.display_item_ref_counter,
225 viewports: self.viewports,
226 cursor: self.cursor.clone(),
227 markers: self.markers.clone(),
228 annotations: self.annotations.clone(),
229 selected_annotation: None,
230 annotation_groups: Vec::new(), annotation_list_visible: false,
232 annotation_counter: self.annotation_counter,
233 last_active_viewport_idx: 0,
234 annotation_menu_pos: None,
235 annotation_menu_time: None,
236 focused_item: self.focused_item,
237 focused_transaction: self.focused_transaction,
238 default_variable_name_type: self.default_variable_name_type,
239 display_variable_indices: self.display_variable_indices,
240 scroll_offset: self.scroll_offset,
241 drawing_infos: vec![],
242 top_item_draw_offset: 0.,
243 graphics: HashMap::new(),
244 total_height: 0.,
245 old_max_timestamp,
246 cache_generation: self.cache_generation + 1, inflight_caches: HashMap::new(),
248 cached_time_offset: BigInt::zero(),
249 };
250
251 new_wavedata.update_metadata(translators);
252 let load_commands = new_wavedata.load_waves();
253 (new_wavedata, load_commands)
254 }
255
256 pub fn update_with_items(
257 &mut self,
258 new_items: &HashMap<DisplayedItemRef, DisplayedItem>,
259 mut items_tree: DisplayedItemTree,
260 translators: &TranslatorList,
261 ) -> Option<LoadSignalsCmd> {
262 self.displayed_items = Self::update_displayed_items(
263 self.inner.as_waves().unwrap(),
264 new_items,
265 true,
266 translators,
267 &mut items_tree,
268 );
269 self.items_tree = items_tree;
270
271 self.display_item_ref_counter = self
272 .displayed_items
273 .keys()
274 .map(|dir| dir.0)
275 .max()
276 .unwrap_or(0);
277
278 self.update_metadata(translators);
279 self.load_waves()
280 }
281
282 fn update_metadata(&mut self, translators: &TranslatorList) {
286 for di in self.displayed_items.values_mut() {
287 let DisplayedItem::Variable(displayed_variable) = di else {
288 continue;
289 };
290
291 let meta = self
292 .inner
293 .as_waves()
294 .unwrap()
295 .variable_meta(&displayed_variable.variable_ref.clone())
296 .unwrap();
297 let translator =
298 variable_translator(displayed_variable.get_format(&[]), &[], translators, || {
299 Ok(meta.clone())
300 });
301 let info = translator.variable_info(&meta).ok();
302
303 match info {
304 Some(info) => displayed_variable
305 .field_formats
306 .retain(|ff| info.has_subpath(&ff.field)),
307 _ => displayed_variable.field_formats.clear(),
308 }
309
310 displayed_variable.downgrade_type_limits_if_unsupported(translator, &meta);
311 }
312 }
313
314 fn load_waves(&mut self) -> Option<LoadSignalsCmd> {
318 let variables = self.displayed_items.values().filter_map(|item| match item {
319 DisplayedItem::Variable(r) => Some(&r.variable_ref),
320 _ => None,
321 });
322 self.inner
323 .as_waves_mut()
324 .unwrap()
325 .load_variables(variables)
326 .expect("internal error: failed to load variables")
327 }
328
329 pub fn update_viewports(&mut self) {
332 if let Some(old_max_timestamp) = std::mem::take(&mut self.old_max_timestamp) {
333 let new_max_timestamp = self
336 .inner
337 .max_timestamp()
338 .unwrap_or_else(BigUint::one)
339 .to_bigint()
340 .unwrap();
341 if new_max_timestamp != old_max_timestamp {
342 let time_offset = &self.cached_time_offset;
343 for viewport in &mut self.viewports {
344 *viewport =
345 viewport.clip_to(&old_max_timestamp, &new_max_timestamp, time_offset);
346 }
347 }
348 }
349 }
350
351 fn update_displayed_items(
352 waves: &WaveContainer,
353 items: &HashMap<DisplayedItemRef, DisplayedItem>,
354 keep_unavailable: bool,
355 translators: &TranslatorList,
356 items_tree: &mut DisplayedItemTree,
357 ) -> HashMap<DisplayedItemRef, DisplayedItem> {
358 items
359 .iter()
360 .filter_map(|(&id, i)| {
361 let new = match i {
362 DisplayedItem::Divider(_)
364 | DisplayedItem::Marker(_)
365 | DisplayedItem::TimeLine(_)
366 | DisplayedItem::Stream(_)
367 | DisplayedItem::Group(_) => Some((id, i.clone())),
368 DisplayedItem::Variable(s) => {
369 s.update(waves, keep_unavailable).map(|r| (id, r))
370 }
371 DisplayedItem::Placeholder(p) => {
372 match waves.update_variable_ref(&p.variable_ref) {
373 None => {
374 if keep_unavailable {
375 Some((id, DisplayedItem::Placeholder(p.clone())))
376 } else {
377 None
378 }
379 }
380 Some(new_variable_ref) => {
381 let Ok(meta) = waves
382 .variable_meta(&new_variable_ref)
383 .context("When updating")
384 .map_err(|e| error!("{e:#?}"))
385 else {
386 return Some((id, DisplayedItem::Placeholder(p.clone())));
387 };
388 let translator = variable_translator(
389 p.format.as_ref(),
390 &[],
391 translators,
392 || Ok(meta.clone()),
393 );
394 let info = translator.variable_info(&meta).unwrap();
395 Some((
396 id,
397 DisplayedItem::Variable(
398 p.clone().into_variable(info, new_variable_ref),
399 ),
400 ))
401 }
402 }
403 }
404 };
405
406 if new.is_none() {
409 let removed = items_tree.drain_recursive_if(|n| n.item_ref == id);
410 assert!(
411 removed.len() <= 1,
412 "more elements removed then should be possible"
413 );
414 }
415
416 new
417 })
418 .collect()
419 }
420
421 #[must_use]
422 pub fn select_preferred_translator(
423 &self,
424 var: &VariableMeta,
425 translators: &TranslatorList,
426 ) -> String {
427 select_preferred_translator(var, translators)
428 }
429
430 #[must_use]
431 pub fn variable_translator<'a>(
432 &'a self,
433 field: &DisplayedFieldRef,
434 translators: &'a TranslatorList,
435 ) -> &'a DynTranslator {
436 let Some(DisplayedItem::Variable(displayed_variable)) =
437 self.displayed_items.get(&field.item)
438 else {
439 panic!("asking for translator for a non DisplayItem::Variable item")
440 };
441
442 variable_translator(
443 displayed_variable.get_format(&field.field),
444 &field.field,
445 translators,
446 || {
447 self.inner
448 .as_waves()
449 .unwrap()
450 .variable_meta(&displayed_variable.variable_ref)
451 },
452 )
453 }
454
455 #[must_use]
456 pub fn variable_translator_with_meta<'a>(
457 &'a self,
458 field: &DisplayedFieldRef,
459 translators: &'a TranslatorList,
460 meta: &VariableMeta,
461 ) -> &'a DynTranslator {
462 let Some(DisplayedItem::Variable(displayed_variable)) =
463 self.displayed_items.get(&field.item)
464 else {
465 panic!("asking for translator for a non DisplayItem::Variable item")
466 };
467
468 variable_translator(
469 displayed_variable.get_format(&field.field),
470 &field.field,
471 translators,
472 || Ok(meta.clone()),
473 )
474 }
475
476 pub fn add_variables(
477 &mut self,
478 translators: &TranslatorList,
479 variables: Vec<VariableRef>,
480 target_position: Option<TargetPosition>,
481 update_display_names: bool,
482 ignore_failures: bool,
483 variable_name_type: Option<VariableNameType>,
484 ) -> (Option<LoadSignalsCmd>, Vec<DisplayedItemRef>) {
485 let mut indices = vec![];
486 let res = match self
488 .inner
489 .as_waves_mut()
490 .unwrap()
491 .load_variables(variables.iter())
492 {
493 Err(e) => {
494 error!("{e:#?}");
495 return (None, indices);
496 }
497 Ok(res) => res,
498 };
499
500 let mut target_position = target_position
502 .or_else(|| self.insert_position(self.focused_item))
503 .unwrap_or(self.end_insert_position());
504 for variable in variables {
505 let Ok(meta) = self
506 .inner
507 .as_waves()
508 .unwrap()
509 .variable_meta(&variable)
510 .context("When adding variable")
511 .map_err(|e| error!("{e:#?}"))
512 else {
513 if ignore_failures {
514 continue;
515 }
516 return (res, indices);
517 };
518
519 let translator = variable_translator(None, &[], translators, || Ok(meta.clone()));
520 let info = translator.variable_info(&meta).unwrap();
521
522 let new_variable = DisplayedItem::Variable(DisplayedVariable {
523 variable_ref: variable.clone(),
524 info,
525 color: None,
526 background_color: None,
527 display_name: variable.name.clone(),
528 display_name_type: variable_name_type.unwrap_or(self.default_variable_name_type),
529 manual_name: None,
530 format: None,
531 field_formats: vec![],
532 height_scaling_factor: None,
533 analog: None,
534 });
535
536 indices.push(self.insert_item(new_variable, Some(target_position), true));
537 target_position = TargetPosition {
538 before: ItemIndex(target_position.before.0 + 1),
539 level: target_position.level,
540 }
541 }
542
543 if update_display_names {
544 self.compute_variable_display_names();
545 }
546 (res, indices)
547 }
548
549 pub fn remove_displayed_item(&mut self, id: DisplayedItemRef) {
551 let Some(idx) = self
552 .items_tree
553 .iter()
554 .enumerate()
555 .find(|(_, node)| node.item_ref == id)
556 .map(|(idx, _)| ItemIndex(idx))
557 else {
558 return;
559 };
560
561 let focused_item_ref = self
562 .focused_item
563 .and_then(|vidx| self.items_tree.get_visible(vidx))
564 .map(|node| node.item_ref);
565
566 for removed_ref in self.items_tree.remove_recursive(idx) {
567 if let Some(DisplayedItem::Marker(m)) = self.displayed_items.remove(&removed_ref) {
568 self.markers.remove(&m.idx);
569 }
570
571 self.annotations
572 .retain(|annotation| !annotation.is_attached(&removed_ref));
573 }
574
575 self.focused_item = focused_item_ref.and_then(|focused_item_ref| {
576 match self
577 .items_tree
578 .iter_visible()
579 .find_position(|node| node.item_ref == focused_item_ref)
580 .map(|(vidx, _)| VisibleItemIndex(vidx))
581 {
582 Some(vidx) => Some(vidx),
583 None if self
584 .focused_item
585 .and_then(|focused_vidx| self.items_tree.to_displayed(focused_vidx))
586 .is_some() =>
587 {
588 Some(self.focused_item.unwrap())
589 }
590 None => self
591 .items_tree
592 .iter_visible()
593 .count()
594 .checked_sub(1)
595 .map(VisibleItemIndex),
596 }
597 });
598 }
599
600 pub fn add_divider(&mut self, name: Option<String>, vidx: Option<VisibleItemIndex>) {
601 self.insert_item(
602 DisplayedItem::Divider(DisplayedDivider {
603 color: None,
604 background_color: None,
605 name,
606 }),
607 self.insert_position(vidx),
608 true,
609 );
610 }
611
612 pub fn add_timeline(&mut self, vidx: Option<VisibleItemIndex>) {
613 self.insert_item(
614 DisplayedItem::TimeLine(DisplayedTimeLine {
615 color: None,
616 background_color: None,
617 name: None,
618 }),
619 self.insert_position(vidx),
620 true,
621 );
622 }
623
624 pub fn add_group(
625 &mut self,
626 name: String,
627 target_position: Option<TargetPosition>,
628 ) -> DisplayedItemRef {
629 self.insert_item(
630 DisplayedItem::Group(DisplayedGroup {
631 name,
632 color: None,
633 background_color: None,
634 content: vec![],
635 is_open: false,
636 }),
637 target_position,
638 true,
639 )
640 }
641
642 pub fn select_annotation(&mut self, id: Option<Id>) {
643 self.selected_annotation = id;
644 }
645
646 pub fn add_generator(&mut self, gen_ref: TransactionStreamRef) {
647 let Some(gen_id) = gen_ref.gen_id else { return };
648 let Some(transactions) = self.inner.as_transactions_mut() else {
649 return;
650 };
651 let is_empty = {
652 let Some(generator) = transactions.get_generator(gen_id) else {
653 return;
654 };
655 generator.transactions.is_empty()
656 };
657 if is_empty {
658 info!("(Generator {gen_id}) Loading transactions into memory!");
659 match transactions
660 .inner
661 .load_stream_into_memory(gen_ref.stream_id)
662 {
663 Ok(()) => info!("(Generator {gen_id}) Finished loading transactions!"),
664 Err(_) => return,
665 }
666 }
667
668 let mut last_times_on_row = vec![(BigUint::ZERO, BigUint::ZERO)];
669 let Some(generator) = transactions.get_generator(gen_id) else {
670 return;
671 };
672 calculate_rows_of_stream(&generator.transactions, &mut last_times_on_row);
673
674 let new_gen = DisplayedItem::Stream(DisplayedStream {
675 display_name: gen_ref.name.clone(),
676 transaction_stream_ref: gen_ref,
677 color: None,
678 background_color: None,
679 manual_name: None,
680 rows: last_times_on_row.len(),
681 });
682
683 self.insert_item(new_gen, None, true);
684 }
685
686 pub fn add_stream(&mut self, stream_ref: TransactionStreamRef) {
687 if self
688 .inner
689 .as_transactions_mut()
690 .unwrap()
691 .get_stream(stream_ref.stream_id)
692 .unwrap()
693 .transactions_loaded
694 .not()
695 {
696 info!("(Stream) Loading transactions into memory!");
697 match self
698 .inner
699 .as_transactions_mut()
700 .unwrap()
701 .inner
702 .load_stream_into_memory(stream_ref.stream_id)
703 {
704 Ok(()) => info!(
705 "(Stream {}) Finished loading transactions!",
706 stream_ref.stream_id
707 ),
708 Err(_) => return,
709 }
710 }
711
712 let stream = self
713 .inner
714 .as_transactions()
715 .unwrap()
716 .get_stream(stream_ref.stream_id)
717 .unwrap();
718 let mut last_times_on_row = vec![(BigUint::ZERO, BigUint::ZERO)];
719
720 for gen_id in &stream.generators {
721 let generator = self
722 .inner
723 .as_transactions()
724 .unwrap()
725 .get_generator(*gen_id)
726 .unwrap();
727 calculate_rows_of_stream(&generator.transactions, &mut last_times_on_row);
728 }
729
730 let new_stream = DisplayedItem::Stream(DisplayedStream {
731 display_name: stream_ref.name.clone(),
732 transaction_stream_ref: stream_ref,
733 color: None,
734 background_color: None,
735 manual_name: None,
736 rows: last_times_on_row.len(),
737 });
738
739 self.insert_item(new_stream, None, true);
740 }
741
742 pub fn add_all_streams(&mut self) {
743 let mut streams: Vec<(StreamId, String)> = vec![];
744 for stream in self.inner.as_transactions().unwrap().get_streams() {
745 streams.push((stream.id, stream.name.clone()));
746 }
747
748 for (id, name) in streams
749 .into_iter()
750 .sorted_by(|a, b| numeric_sort::cmp(&a.1, &b.1))
751 {
752 self.add_stream(TransactionStreamRef::new_stream(id, name));
753 }
754 }
755
756 #[must_use]
763 pub fn insert_position(&self, vidx: Option<VisibleItemIndex>) -> Option<TargetPosition> {
764 let vidx = vidx?;
765 let item_index = self.items_tree.to_displayed(vidx)?;
766 let node = self.items_tree.get(item_index)?;
767 let item = self.displayed_items.get(&node.item_ref)?;
768
769 let (before, level) = match item {
771 DisplayedItem::Group(..) if node.unfolded => (item_index.0 + 1, node.level + 1),
772 DisplayedItem::Group(..) => {
773 let next_idx = self.items_tree.to_displayed(VisibleItemIndex(vidx.0 + 1));
774 match next_idx {
775 Some(idx) => (idx.0, node.level),
776 None => (self.items_tree.len(), node.level),
777 }
778 }
779 _ => (item_index.0 + 1, node.level),
780 };
781 Some(TargetPosition {
782 before: ItemIndex(before),
783 level,
784 })
785 }
786
787 #[must_use]
789 pub fn end_insert_position(&self) -> TargetPosition {
790 TargetPosition {
791 before: ItemIndex(self.items_tree.len()),
792 level: 0,
793 }
794 }
795
796 #[must_use]
797 pub fn index_for_ref_or_focus(&self, item_ref: Option<DisplayedItemRef>) -> Option<ItemIndex> {
798 if let Some(item_ref) = item_ref {
799 self.items_tree
800 .iter()
801 .enumerate()
802 .find_map(|(idx, node)| (node.item_ref == item_ref).then_some(ItemIndex(idx)))
803 } else if let Some(focused_item) = self.focused_item {
804 self.items_tree
805 .get_visible_extra(focused_item)
806 .map(|info| info.idx)
807 } else {
808 None
809 }
810 }
811
812 pub(crate) fn insert_item(
817 &mut self,
818 new_item: DisplayedItem,
819 target_position: Option<TargetPosition>,
820 move_focus: bool,
821 ) -> DisplayedItemRef {
822 let target_position = target_position
823 .or_else(|| self.insert_position(self.focused_item))
824 .unwrap_or_else(|| self.end_insert_position());
825
826 let item_ref = self.next_displayed_item_ref();
827 let insert_index = self
828 .items_tree
829 .insert_item(item_ref, target_position)
830 .unwrap();
831 self.displayed_items.insert(item_ref, new_item);
832 if move_focus {
833 self.focused_item = self.focused_item.and_then(|_| {
834 self.items_tree
835 .iter_visible_extra()
836 .find_map(|info| (info.idx == insert_index).then_some(info.vidx))
837 });
838 }
839 self.items_tree.xselect_all_visible(false);
840 item_ref
841 }
842
843 pub fn go_to_cursor_if_not_in_view(&mut self) -> bool {
844 if let Some(cursor) = &self.cursor {
845 let max_timestamp = self.safe_max_timestamp();
846 self.viewports[0].go_to_cursor_if_not_in_view(
847 cursor,
848 &max_timestamp,
849 &self.cached_time_offset,
850 )
851 } else {
852 false
853 }
854 }
855
856 #[inline]
857 #[must_use]
858 pub fn numbered_marker_location(&self, idx: u8, viewport: &Viewport, view_width: f32) -> f32 {
859 let time_offset = self.time_offset();
860 viewport.pixel_from_time(
861 self.numbered_marker_time(idx),
862 view_width,
863 &self.safe_max_timestamp(),
864 time_offset,
865 )
866 }
867
868 #[inline]
869 #[must_use]
870 pub fn numbered_marker_time(&self, idx: u8) -> &BigInt {
871 self.markers.get(&idx).unwrap()
872 }
873
874 #[must_use]
875 pub fn viewport_all(&self) -> Viewport {
876 Viewport::new()
877 }
878
879 pub fn remove_placeholders(&mut self) {
880 let removed_refs = self.items_tree.drain_recursive_if(|node| {
881 matches!(
882 self.displayed_items.get(&node.item_ref),
883 Some(DisplayedItem::Placeholder(_))
884 )
885 });
886 for removed_ref in removed_refs {
887 self.displayed_items.remove(&removed_ref);
888 }
889 }
890
891 #[inline]
892 #[must_use]
893 pub fn any_displayed(&self) -> bool {
894 !self.displayed_items.is_empty()
895 }
896
897 fn drawing_top(&self) -> Option<f32> {
898 self.drawing_infos
899 .iter()
900 .map(ItemDrawingInfo::top)
901 .min_by(f32::total_cmp)
902 }
903
904 fn drawing_bottom(&self) -> Option<f32> {
905 self.drawing_infos
906 .iter()
907 .map(ItemDrawingInfo::bottom)
908 .max_by(f32::total_cmp)
909 }
910
911 #[must_use]
913 pub fn get_top_item(&self) -> usize {
915 if self.drawing_infos.is_empty() {
916 return 0;
917 }
918 let first_element_y = self.drawing_top().unwrap();
921 let visible_top = first_element_y + self.scroll_offset;
922
923 self.drawing_infos
924 .iter()
925 .enumerate()
926 .find(|(_, di)| di.top() >= visible_top - 1.) .map_or(self.drawing_infos.len() - 1, |(idx, _)| idx)
928 }
929
930 pub fn get_content_start(&self, ctx: &mut DrawingContext<'_>) -> f32 {
932 let first_element_top = self.drawing_top().unwrap();
933 let y = (ctx.to_screen)(0., 0.).y;
934 first_element_top - y
935 }
936
937 pub fn get_content_height(&self, ctx: &mut DrawingContext<'_>) -> f32 {
939 let last_element_bottom = self.drawing_bottom().unwrap();
940 let y = (ctx.to_screen)(0., 0.).y;
941 last_element_bottom - y
942 }
943
944 #[must_use]
946 pub fn get_item_at_y(&self, y: f32) -> Option<VisibleItemIndex> {
947 if self.drawing_infos.is_empty() {
948 return None;
949 }
950 let threshold = y + self.top_item_draw_offset;
951 if self.drawing_bottom().unwrap() <= threshold {
952 return None;
953 }
954
955 self.drawing_infos
956 .iter()
957 .rev()
958 .find(|di| di.top() <= threshold)
959 .map(ItemDrawingInfo::vidx)
960 }
961
962 pub fn scroll_to_item(&mut self, idx: usize) {
963 if self.drawing_infos.is_empty() {
964 return;
965 }
966 let first_element_y = self.drawing_top().unwrap();
967 let last_element_bottom = self.drawing_bottom().unwrap();
968 let content_height = last_element_bottom - first_element_y;
969
970 let max_scroll = content_height - self.total_height;
972 if max_scroll <= 0.0 {
973 return;
974 }
975
976 let item_y = self
977 .drawing_infos
978 .get(idx)
979 .unwrap_or_else(|| self.drawing_infos.last().unwrap())
980 .top();
981 let target_scroll = item_y - first_element_y;
982
983 self.scroll_offset = target_scroll.clamp(0.0, max_scroll);
985 }
986
987 pub fn set_cursor_at_transition(
991 &mut self,
992 next: bool,
993 variable: Option<VisibleItemIndex>,
994 skip_zero: bool,
995 ) {
996 if let Some(vidx) = variable.or(self.focused_item)
997 && let Some(cursor) = &self.cursor
998 && let Some(DisplayedItem::Variable(variable)) = &self
999 .items_tree
1000 .get_visible(vidx)
1001 .and_then(|node| self.displayed_items.get(&node.item_ref))
1002 && let Ok(Some(res)) = self.inner.as_waves().unwrap().query_variable(
1003 &variable.variable_ref,
1004 &cursor.to_biguint().unwrap_or_default(),
1005 )
1006 {
1007 if next {
1008 if let Some(ref time) = res.next {
1009 let stime = time.to_bigint();
1010 if stime.is_some() {
1011 self.cursor.clone_from(&stime);
1012 }
1013 } else {
1014 if let Some(end_time) = self.max_timestamp() {
1016 self.cursor = Some(end_time);
1017 } else {
1018 warn!(
1019 "Set cursor at transition: No timestamp count even though waveforms should be loaded"
1020 );
1021 }
1022 }
1023 } else if let Some(stime) = res.current.unwrap().0.to_bigint() {
1024 let bigone = BigInt::from(1);
1025 if stime == *cursor && *cursor >= bigone {
1027 if let Ok(Some(newres)) = self.inner.as_waves().unwrap().query_variable(
1029 &variable.variable_ref,
1030 &(cursor - bigone).to_biguint().unwrap_or_default(),
1031 ) && let Some(current) = newres.current
1032 {
1033 let newstime = current.0.to_bigint();
1034 if newstime.is_some() {
1035 self.cursor.clone_from(&newstime);
1036 }
1037 }
1038 } else {
1039 self.cursor = Some(stime);
1040 }
1041 }
1042
1043 if skip_zero {
1045 if let Some(time) = &self.cursor {
1048 let next_value = self.inner.as_waves().unwrap().query_variable(
1049 &variable.variable_ref,
1050 &time.to_biguint().unwrap_or_default(),
1051 );
1052 if next_value.is_ok_and(|r| {
1053 r.is_some_and(|r| {
1054 r.current.is_some_and(|v| match v.1 {
1055 VariableValue::BigUint(v) => v.is_zero(),
1056 VariableValue::String(_) => false,
1057 })
1058 })
1059 }) {
1060 self.set_cursor_at_transition(next, Some(vidx), false);
1061 }
1062 }
1063 }
1064 }
1065 }
1066
1067 pub fn next_displayed_item_ref(&mut self) -> DisplayedItemRef {
1068 self.display_item_ref_counter += 1;
1069 self.display_item_ref_counter.into()
1070 }
1071
1072 #[must_use]
1078 pub fn max_timestamp(&self) -> Option<BigInt> {
1079 self.inner
1080 .max_timestamp()
1081 .filter(|r| !r.is_zero())
1082 .and_then(|r| r.to_bigint())
1083 }
1084
1085 #[must_use]
1089 pub fn safe_max_timestamp(&self) -> BigInt {
1090 self.max_timestamp().unwrap_or_else(BigInt::one)
1091 }
1092
1093 #[must_use]
1095 pub fn time_offset(&self) -> &BigInt {
1096 &self.cached_time_offset
1097 }
1098
1099 pub fn refresh_time_offset(&mut self, enable_time_offset: bool) {
1101 self.cached_time_offset = if enable_time_offset {
1102 self.inner
1103 .min_timestamp()
1104 .map(|ts| ts.to_bigint().unwrap())
1105 .unwrap_or_else(BigInt::zero)
1106 } else {
1107 BigInt::zero()
1108 };
1109 }
1110
1111 #[must_use]
1112 pub fn get_displayed_item_index(
1113 &self,
1114 item_ref: &DisplayedItemRef,
1115 ) -> Option<VisibleItemIndex> {
1116 self.items_tree
1118 .iter_visible()
1119 .enumerate()
1120 .find_map(|(vidx, node)| {
1121 if node.item_ref == *item_ref {
1122 Some(VisibleItemIndex(vidx))
1123 } else {
1124 None
1125 }
1126 })
1127 }
1128
1129 pub fn build_analog_cache_async(
1133 &self,
1134 entry: std::sync::Arc<crate::analog_signal_cache::AnalogCacheEntry>,
1135 variable_ref: &VariableRef,
1136 translator: crate::translation::AnyTranslator,
1137 sender: &std::sync::mpsc::Sender<crate::message::Message>,
1138 ) -> Option<()> {
1139 let wave_container = self.inner.as_waves()?;
1140 let meta = wave_container.variable_meta(variable_ref).ok()?.clone();
1141
1142 let max_timestamp = self.max_timestamp()?.to_u64()?;
1143
1144 let accessor = wave_container.signal_accessor(entry.cache_key.0).ok()?;
1145
1146 let sender_clone = sender.clone();
1147 crate::async_util::perform_work(move || {
1148 let result = crate::analog_signal_cache::AnalogSignalCache::build(
1149 accessor,
1150 &translator,
1151 &meta,
1152 max_timestamp,
1153 None,
1154 );
1155
1156 let msg = match result {
1157 Some(cache) => crate::message::Message::AnalogCacheBuilt {
1158 entry: entry.clone(),
1159 result: Ok(cache),
1160 },
1161 None => crate::message::Message::AnalogCacheBuilt {
1162 entry: entry.clone(),
1163 result: Err("Failed to build analog cache".into()),
1164 },
1165 };
1166
1167 crate::OUTSTANDING_TRANSACTIONS.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
1168 let _ = sender_clone.send(msg);
1169
1170 if let Some(ctx) = crate::EGUI_CONTEXT.read().unwrap().as_ref() {
1171 ctx.request_repaint();
1172 }
1173 });
1174
1175 Some(())
1176 }
1177
1178 pub fn set_active_scope(&mut self, scope: Option<ScopeType>) -> Option<()> {
1179 if let Some(scope) = scope {
1180 let scope = if let ScopeType::StreamScope(StreamScopeRef::Empty(name)) = scope {
1181 let inner = self.inner.as_transactions()?;
1182 ScopeType::StreamScope(StreamScopeRef::new_stream_from_name(inner, name))
1183 } else {
1184 scope
1185 };
1186
1187 if self.inner.scope_exists(&scope) {
1188 self.active_scope = Some(scope);
1189 } else {
1190 warn!("Setting active scope to {scope} which does not exist");
1191 }
1192 } else {
1193 self.active_scope = None;
1195 }
1196 Some(())
1197 }
1198}
1199
1200#[cfg(test)]
1201mod tests {
1202 use super::*;
1203 use crate::data_container::DataContainer;
1204 use crate::displayed_item_tree::DisplayedItemTree;
1205 use crate::item_drawing_info::{DividerDrawingInfo, ItemDrawingInfo};
1206 use crate::viewport::Viewport;
1207 use crate::wave_source::{WaveFormat, WaveSource};
1208
1209 fn wave_data_with_rows(top_item_draw_offset: f32) -> WaveData {
1210 WaveData {
1211 inner: DataContainer::Empty,
1212 source: WaveSource::Data,
1213 format: WaveFormat::Vcd,
1214 active_scope: None,
1215 items_tree: DisplayedItemTree::new(),
1216 displayed_items: HashMap::new(),
1217 display_item_ref_counter: 0,
1218 viewports: vec![Viewport::new()],
1219 cursor: None,
1220 markers: HashMap::new(),
1221 selected_annotation: None,
1222 annotations: Vec::new(),
1223 annotation_groups: Vec::new(),
1224 annotation_list_visible: false,
1225 annotation_counter: 0,
1226 last_active_viewport_idx: 0,
1227 annotation_menu_pos: None,
1228 annotation_menu_time: None,
1229 focused_item: None,
1230 focused_transaction: (None, None),
1231 default_variable_name_type: VariableNameType::Local,
1232 scroll_offset: 80.0,
1233 display_variable_indices: false,
1234 graphics: HashMap::new(),
1235 drawing_infos: vec![
1236 ItemDrawingInfo::Divider(DividerDrawingInfo {
1237 vidx: VisibleItemIndex(0),
1238 top: 120.0,
1239 bottom: 140.0,
1240 }),
1241 ItemDrawingInfo::Divider(DividerDrawingInfo {
1242 vidx: VisibleItemIndex(1),
1243 top: 140.0,
1244 bottom: 160.0,
1245 }),
1246 ],
1247 top_item_draw_offset,
1248 total_height: 40.0,
1249 old_max_timestamp: None,
1250 cache_generation: 0,
1251 inflight_caches: HashMap::new(),
1252 cached_time_offset: BigInt::from(0),
1253 }
1254 }
1255
1256 #[test]
1257 fn get_item_at_y_uses_top_item_draw_offset_space() {
1258 let waves = wave_data_with_rows(120.0);
1259
1260 assert_eq!(waves.get_item_at_y(5.0), Some(VisibleItemIndex(0)));
1261 assert_eq!(waves.get_item_at_y(25.0), Some(VisibleItemIndex(1)));
1262 assert_eq!(waves.get_item_at_y(45.0), None);
1263 }
1264
1265 #[test]
1266 fn get_item_at_y_is_not_shifted_by_scroll_offset() {
1267 let waves = wave_data_with_rows(150.0);
1268
1269 assert_eq!(waves.get_item_at_y(-25.0), Some(VisibleItemIndex(0)));
1270 assert_eq!(waves.get_item_at_y(5.0), Some(VisibleItemIndex(1)));
1271 }
1272}