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