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