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