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::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()
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 );
181
182 let old_num_timestamps = self.num_timestamps();
183 let mut new_wavedata = WaveData {
184 inner: DataContainer::Waves(*new_waves),
185 source,
186 format,
187 active_scope,
188 items_tree: self.items_tree,
189 displayed_items: display_items,
190 display_item_ref_counter: self.display_item_ref_counter,
191 viewports: self.viewports,
192 cursor: self.cursor.clone(),
193 markers: self.markers.clone(),
194 focused_item: self.focused_item,
195 focused_transaction: self.focused_transaction,
196 default_variable_name_type: self.default_variable_name_type,
197 display_variable_indices: self.display_variable_indices,
198 scroll_offset: self.scroll_offset,
199 drawing_infos: vec![],
200 top_item_draw_offset: 0.,
201 graphics: HashMap::new(),
202 total_height: 0.,
203 old_num_timestamps,
204 cache_generation: self.cache_generation + 1, inflight_caches: HashMap::new(),
206 };
207
208 new_wavedata.update_metadata(translators);
209 let load_commands = new_wavedata.load_waves();
210 (new_wavedata, load_commands)
211 }
212
213 pub fn update_with_items(
214 &mut self,
215 new_items: &HashMap<DisplayedItemRef, DisplayedItem>,
216 items_tree: DisplayedItemTree,
217 translators: &TranslatorList,
218 ) -> Option<LoadSignalsCmd> {
219 self.items_tree = items_tree;
220 self.displayed_items = self.update_displayed_items(
221 self.inner.as_waves().unwrap(),
222 new_items,
223 true,
224 translators,
225 );
226 self.display_item_ref_counter = self
227 .displayed_items
228 .keys()
229 .map(|dir| dir.0)
230 .max()
231 .unwrap_or(0);
232
233 self.update_metadata(translators);
234 self.load_waves()
235 }
236
237 fn update_metadata(&mut self, translators: &TranslatorList) {
241 for di in self.displayed_items.values_mut() {
242 let DisplayedItem::Variable(displayed_variable) = di else {
243 continue;
244 };
245
246 let meta = self
247 .inner
248 .as_waves()
249 .unwrap()
250 .variable_meta(&displayed_variable.variable_ref.clone())
251 .unwrap();
252 let translator =
253 variable_translator(displayed_variable.get_format(&[]), &[], translators, || {
254 Ok(meta.clone())
255 });
256 let info = translator.variable_info(&meta).ok();
257
258 match info {
259 Some(info) => displayed_variable
260 .field_formats
261 .retain(|ff| info.has_subpath(&ff.field)),
262 _ => displayed_variable.field_formats.clear(),
263 }
264 }
265 }
266
267 fn load_waves(&mut self) -> Option<LoadSignalsCmd> {
271 let variables = self.displayed_items.values().filter_map(|item| match item {
272 DisplayedItem::Variable(r) => Some(&r.variable_ref),
273 _ => None,
274 });
275 self.inner
276 .as_waves_mut()
277 .unwrap()
278 .load_variables(variables)
279 .expect("internal error: failed to load variables")
280 }
281
282 pub fn update_viewports(&mut self) {
285 if let Some(old_num_timestamps) = std::mem::take(&mut self.old_num_timestamps) {
286 let new_num_timestamps = self
289 .inner
290 .max_timestamp()
291 .unwrap_or_else(BigUint::one)
292 .to_bigint()
293 .unwrap();
294 if new_num_timestamps != old_num_timestamps {
295 for viewport in &mut self.viewports {
296 *viewport = viewport.clip_to(&old_num_timestamps, &new_num_timestamps);
297 }
298 }
299 }
300 }
301
302 fn update_displayed_items(
303 &self,
304 waves: &WaveContainer,
305 items: &HashMap<DisplayedItemRef, DisplayedItem>,
306 keep_unavailable: bool,
307 translators: &TranslatorList,
308 ) -> HashMap<DisplayedItemRef, DisplayedItem> {
309 items
310 .iter()
311 .filter_map(|(id, i)| {
312 match i {
313 DisplayedItem::Divider(_)
315 | DisplayedItem::Marker(_)
316 | DisplayedItem::TimeLine(_)
317 | DisplayedItem::Stream(_)
318 | DisplayedItem::Group(_) => Some((*id, i.clone())),
319 DisplayedItem::Variable(s) => {
320 s.update(waves, keep_unavailable).map(|r| (*id, r))
321 }
322 DisplayedItem::Placeholder(p) => {
323 match waves.update_variable_ref(&p.variable_ref) {
324 None => {
325 if keep_unavailable {
326 Some((*id, DisplayedItem::Placeholder(p.clone())))
327 } else {
328 None
329 }
330 }
331 Some(new_variable_ref) => {
332 let Ok(meta) = waves
333 .variable_meta(&new_variable_ref)
334 .context("When updating")
335 .map_err(|e| error!("{e:#?}"))
336 else {
337 return Some((*id, DisplayedItem::Placeholder(p.clone())));
338 };
339 let translator = variable_translator(
340 p.format.as_ref(),
341 &[],
342 translators,
343 || Ok(meta.clone()),
344 );
345 let info = translator.variable_info(&meta).unwrap();
346 Some((
347 *id,
348 DisplayedItem::Variable(
349 p.clone().into_variable(info, new_variable_ref),
350 ),
351 ))
352 }
353 }
354 }
355 }
356 })
357 .collect()
358 }
359
360 #[must_use]
361 pub fn select_preferred_translator(
362 &self,
363 var: &VariableMeta,
364 translators: &TranslatorList,
365 ) -> String {
366 select_preferred_translator(var, translators)
367 }
368
369 #[must_use]
370 pub fn variable_translator<'a>(
371 &'a self,
372 field: &DisplayedFieldRef,
373 translators: &'a TranslatorList,
374 ) -> &'a DynTranslator {
375 let Some(DisplayedItem::Variable(displayed_variable)) =
376 self.displayed_items.get(&field.item)
377 else {
378 panic!("asking for translator for a non DisplayItem::Variable item")
379 };
380
381 variable_translator(
382 displayed_variable.get_format(&field.field),
383 &field.field,
384 translators,
385 || {
386 self.inner
387 .as_waves()
388 .unwrap()
389 .variable_meta(&displayed_variable.variable_ref)
390 },
391 )
392 }
393
394 #[must_use]
395 pub fn variable_translator_with_meta<'a>(
396 &'a self,
397 field: &DisplayedFieldRef,
398 translators: &'a TranslatorList,
399 meta: &VariableMeta,
400 ) -> &'a DynTranslator {
401 let Some(DisplayedItem::Variable(displayed_variable)) =
402 self.displayed_items.get(&field.item)
403 else {
404 panic!("asking for translator for a non DisplayItem::Variable item")
405 };
406
407 variable_translator(
408 displayed_variable.get_format(&field.field),
409 &field.field,
410 translators,
411 || Ok(meta.clone()),
412 )
413 }
414
415 pub fn add_variables(
416 &mut self,
417 translators: &TranslatorList,
418 variables: Vec<VariableRef>,
419 target_position: Option<TargetPosition>,
420 update_display_names: bool,
421 ignore_failures: bool,
422 variable_name_type: Option<VariableNameType>,
423 ) -> (Option<LoadSignalsCmd>, Vec<DisplayedItemRef>) {
424 let mut indices = vec![];
425 let res = match self
427 .inner
428 .as_waves_mut()
429 .unwrap()
430 .load_variables(variables.iter())
431 {
432 Err(e) => {
433 error!("{e:#?}");
434 return (None, indices);
435 }
436 Ok(res) => res,
437 };
438
439 let mut target_position = target_position
441 .or_else(|| self.insert_position(self.focused_item))
442 .unwrap_or(self.end_insert_position());
443 for variable in variables {
444 let Ok(meta) = self
445 .inner
446 .as_waves()
447 .unwrap()
448 .variable_meta(&variable)
449 .context("When adding variable")
450 .map_err(|e| error!("{e:#?}"))
451 else {
452 if ignore_failures {
453 continue;
454 }
455 return (res, indices);
456 };
457
458 let translator = variable_translator(None, &[], translators, || Ok(meta.clone()));
459 let info = translator.variable_info(&meta).unwrap();
460
461 let new_variable = DisplayedItem::Variable(DisplayedVariable {
462 variable_ref: variable.clone(),
463 info,
464 color: None,
465 background_color: None,
466 display_name: variable.name.clone(),
467 display_name_type: variable_name_type.unwrap_or(self.default_variable_name_type),
468 manual_name: None,
469 format: None,
470 field_formats: vec![],
471 height_scaling_factor: None,
472 analog: None,
473 });
474
475 indices.push(self.insert_item(new_variable, Some(target_position), true));
476 target_position = TargetPosition {
477 before: ItemIndex(target_position.before.0 + 1),
478 level: target_position.level,
479 }
480 }
481
482 if update_display_names {
483 self.compute_variable_display_names();
484 }
485 (res, indices)
486 }
487
488 pub fn remove_displayed_item(&mut self, id: DisplayedItemRef) {
490 let Some(idx) = self
491 .items_tree
492 .iter()
493 .enumerate()
494 .find(|(_, node)| node.item_ref == id)
495 .map(|(idx, _)| ItemIndex(idx))
496 else {
497 return;
498 };
499
500 let focused_item_ref = self
501 .focused_item
502 .and_then(|vidx| self.items_tree.get_visible(vidx))
503 .map(|node| node.item_ref);
504
505 for removed_ref in self.items_tree.remove_recursive(idx) {
506 if let Some(DisplayedItem::Marker(m)) = self.displayed_items.remove(&removed_ref) {
507 self.markers.remove(&m.idx);
508 }
509 }
510
511 self.focused_item = focused_item_ref.and_then(|focused_item_ref| {
512 match self
513 .items_tree
514 .iter_visible()
515 .find_position(|node| node.item_ref == focused_item_ref)
516 .map(|(vidx, _)| VisibleItemIndex(vidx))
517 {
518 Some(vidx) => Some(vidx),
519 None if self
520 .focused_item
521 .and_then(|focused_vidx| self.items_tree.to_displayed(focused_vidx))
522 .is_some() =>
523 {
524 Some(self.focused_item.unwrap())
525 }
526 None => self
527 .items_tree
528 .iter_visible()
529 .count()
530 .checked_sub(1)
531 .map(VisibleItemIndex),
532 }
533 });
534 }
535
536 pub fn add_divider(&mut self, name: Option<String>, vidx: Option<VisibleItemIndex>) {
537 self.insert_item(
538 DisplayedItem::Divider(DisplayedDivider {
539 color: None,
540 background_color: None,
541 name,
542 }),
543 self.insert_position(vidx),
544 true,
545 );
546 }
547
548 pub fn add_timeline(&mut self, vidx: Option<VisibleItemIndex>) {
549 self.insert_item(
550 DisplayedItem::TimeLine(DisplayedTimeLine {
551 color: None,
552 background_color: None,
553 name: None,
554 }),
555 self.insert_position(vidx),
556 true,
557 );
558 }
559
560 pub fn add_group(
561 &mut self,
562 name: String,
563 target_position: Option<TargetPosition>,
564 ) -> DisplayedItemRef {
565 self.insert_item(
566 DisplayedItem::Group(DisplayedGroup {
567 name,
568 color: None,
569 background_color: None,
570 content: vec![],
571 is_open: false,
572 }),
573 target_position,
574 true,
575 )
576 }
577
578 pub fn add_generator(&mut self, gen_ref: TransactionStreamRef) {
579 let Some(gen_id) = gen_ref.gen_id else { return };
580 let Some(transactions) = self.inner.as_transactions_mut() else {
581 return;
582 };
583 let is_empty = {
584 let Some(generator) = transactions.get_generator(gen_id) else {
585 return;
586 };
587 generator.transactions.is_empty()
588 };
589 if is_empty {
590 info!("(Generator {gen_id}) Loading transactions into memory!");
591 match transactions
592 .inner
593 .load_stream_into_memory(gen_ref.stream_id)
594 {
595 Ok(()) => info!("(Generator {gen_id}) Finished loading transactions!"),
596 Err(_) => return,
597 }
598 }
599
600 let mut last_times_on_row = vec![(BigUint::ZERO, BigUint::ZERO)];
601 let Some(generator) = transactions.get_generator(gen_id) else {
602 return;
603 };
604 calculate_rows_of_stream(&generator.transactions, &mut last_times_on_row);
605
606 let new_gen = DisplayedItem::Stream(DisplayedStream {
607 display_name: gen_ref.name.clone(),
608 transaction_stream_ref: gen_ref,
609 color: None,
610 background_color: None,
611 manual_name: None,
612 rows: last_times_on_row.len(),
613 });
614
615 self.insert_item(new_gen, None, true);
616 }
617
618 pub fn add_stream(&mut self, stream_ref: TransactionStreamRef) {
619 if self
620 .inner
621 .as_transactions_mut()
622 .unwrap()
623 .get_stream(stream_ref.stream_id)
624 .unwrap()
625 .transactions_loaded
626 .not()
627 {
628 info!("(Stream) Loading transactions into memory!");
629 match self
630 .inner
631 .as_transactions_mut()
632 .unwrap()
633 .inner
634 .load_stream_into_memory(stream_ref.stream_id)
635 {
636 Ok(()) => info!(
637 "(Stream {}) Finished loading transactions!",
638 stream_ref.stream_id
639 ),
640 Err(_) => return,
641 }
642 }
643
644 let stream = self
645 .inner
646 .as_transactions()
647 .unwrap()
648 .get_stream(stream_ref.stream_id)
649 .unwrap();
650 let mut last_times_on_row = vec![(BigUint::ZERO, BigUint::ZERO)];
651
652 for gen_id in &stream.generators {
653 let generator = self
654 .inner
655 .as_transactions()
656 .unwrap()
657 .get_generator(*gen_id)
658 .unwrap();
659 calculate_rows_of_stream(&generator.transactions, &mut last_times_on_row);
660 }
661
662 let new_stream = DisplayedItem::Stream(DisplayedStream {
663 display_name: stream_ref.name.clone(),
664 transaction_stream_ref: stream_ref,
665 color: None,
666 background_color: None,
667 manual_name: None,
668 rows: last_times_on_row.len(),
669 });
670
671 self.insert_item(new_stream, None, true);
672 }
673
674 pub fn add_all_streams(&mut self) {
675 let mut streams: Vec<(usize, String)> = vec![];
676 for stream in self.inner.as_transactions().unwrap().get_streams() {
677 streams.push((stream.id, stream.name.clone()));
678 }
679
680 for (id, name) in streams {
681 self.add_stream(TransactionStreamRef::new_stream(id, name));
682 }
683 }
684
685 #[must_use]
692 pub fn insert_position(&self, vidx: Option<VisibleItemIndex>) -> Option<TargetPosition> {
693 let vidx = vidx?;
694 let item_index = self.items_tree.to_displayed(vidx)?;
695 let node = self.items_tree.get(item_index)?;
696 let item = self.displayed_items.get(&node.item_ref)?;
697
698 let (before, level) = match item {
700 DisplayedItem::Group(..) if node.unfolded => (item_index.0 + 1, node.level + 1),
701 DisplayedItem::Group(..) => {
702 let next_idx = self.items_tree.to_displayed(VisibleItemIndex(vidx.0 + 1));
703 match next_idx {
704 Some(idx) => (idx.0, node.level),
705 None => (self.items_tree.len(), node.level),
706 }
707 }
708 _ => (item_index.0 + 1, node.level),
709 };
710 Some(TargetPosition {
711 before: ItemIndex(before),
712 level,
713 })
714 }
715
716 #[must_use]
718 pub fn end_insert_position(&self) -> TargetPosition {
719 TargetPosition {
720 before: ItemIndex(self.items_tree.len()),
721 level: 0,
722 }
723 }
724
725 #[must_use]
726 pub fn index_for_ref_or_focus(&self, item_ref: Option<DisplayedItemRef>) -> Option<ItemIndex> {
727 if let Some(item_ref) = item_ref {
728 self.items_tree
729 .iter()
730 .enumerate()
731 .find_map(|(idx, node)| (node.item_ref == item_ref).then_some(ItemIndex(idx)))
732 } else if let Some(focused_item) = self.focused_item {
733 self.items_tree
734 .get_visible_extra(focused_item)
735 .map(|info| info.idx)
736 } else {
737 None
738 }
739 }
740
741 pub(crate) fn insert_item(
746 &mut self,
747 new_item: DisplayedItem,
748 target_position: Option<TargetPosition>,
749 move_focus: bool,
750 ) -> DisplayedItemRef {
751 let target_position = target_position
752 .or_else(|| self.insert_position(self.focused_item))
753 .unwrap_or_else(|| self.end_insert_position());
754
755 let item_ref = self.next_displayed_item_ref();
756 let insert_index = self
757 .items_tree
758 .insert_item(item_ref, target_position)
759 .unwrap();
760 self.displayed_items.insert(item_ref, new_item);
761 if move_focus {
762 self.focused_item = self.focused_item.and_then(|_| {
763 self.items_tree
764 .iter_visible_extra()
765 .find_map(|info| (info.idx == insert_index).then_some(info.vidx))
766 });
767 }
768 self.items_tree.xselect_all_visible(false);
769 item_ref
770 }
771
772 pub fn go_to_cursor_if_not_in_view(&mut self) -> bool {
773 if let Some(cursor) = &self.cursor {
774 let num_timestamps = self.safe_num_timestamps();
775 self.viewports[0].go_to_cursor_if_not_in_view(cursor, &num_timestamps)
776 } else {
777 false
778 }
779 }
780
781 #[inline]
782 pub fn numbered_marker_location(&self, idx: u8, viewport: &Viewport, view_width: f32) -> f32 {
783 viewport.pixel_from_time(
784 self.numbered_marker_time(idx),
785 view_width,
786 &self.safe_num_timestamps(),
787 )
788 }
789
790 #[inline]
791 #[must_use]
792 pub fn numbered_marker_time(&self, idx: u8) -> &BigInt {
793 self.markers.get(&idx).unwrap()
794 }
795
796 #[must_use]
797 pub fn viewport_all(&self) -> Viewport {
798 Viewport::new()
799 }
800
801 pub fn remove_placeholders(&mut self) {
802 let removed_refs = self.items_tree.drain_recursive_if(|node| {
803 matches!(
804 self.displayed_items.get(&node.item_ref),
805 Some(DisplayedItem::Placeholder(_))
806 )
807 });
808 for removed_ref in removed_refs {
809 self.displayed_items.remove(&removed_ref);
810 }
811 }
812
813 #[inline]
814 #[must_use]
815 pub fn any_displayed(&self) -> bool {
816 !self.displayed_items.is_empty()
817 }
818
819 #[must_use]
821 pub fn get_top_item(&self) -> usize {
822 let default = if self.drawing_infos.is_empty() {
823 0
824 } else {
825 self.drawing_infos.len() - 1
826 };
827 self.drawing_infos
828 .iter()
829 .enumerate()
830 .find(|(_, di)| di.top() >= self.top_item_draw_offset - 1.) .map_or(default, |(idx, _)| idx)
832 }
833
834 #[must_use]
836 pub fn get_item_at_y(&self, y: f32) -> Option<VisibleItemIndex> {
837 if self.drawing_infos.is_empty() {
838 return None;
839 }
840 let first_element_top = self.drawing_infos.first().unwrap().top();
841 let first_element_bottom = self.drawing_infos.last().unwrap().bottom();
842 let threshold = y + first_element_top + self.scroll_offset;
843 if first_element_bottom <= threshold {
844 return None;
845 }
846 self.drawing_infos
847 .iter()
848 .enumerate()
849 .rev()
850 .find(|(_, di)| di.top() <= threshold)
851 .map(|(vidx, _)| VisibleItemIndex(vidx))
852 }
853
854 pub fn scroll_to_item(&mut self, idx: usize) {
855 if self.drawing_infos.is_empty() {
856 return;
857 }
858 let first_element_y = self.drawing_infos.first().unwrap().top();
860 let item_y = self
861 .drawing_infos
862 .get(idx)
863 .unwrap_or_else(|| self.drawing_infos.last().unwrap())
864 .top();
865 if self.scroll_offset > self.total_height {
867 self.scroll_offset = item_y - first_element_y;
868 }
869 }
870
871 pub fn set_cursor_at_transition(
874 &mut self,
875 next: bool,
876 variable: Option<VisibleItemIndex>,
877 skip_zero: bool,
878 ) {
879 if let Some(vidx) = variable.or(self.focused_item)
880 && let Some(cursor) = &self.cursor
881 && let Some(DisplayedItem::Variable(variable)) = &self
882 .items_tree
883 .get_visible(vidx)
884 .and_then(|node| self.displayed_items.get(&node.item_ref))
885 && let Ok(Some(res)) = self.inner.as_waves().unwrap().query_variable(
886 &variable.variable_ref,
887 &cursor.to_biguint().unwrap_or_default(),
888 )
889 {
890 if next {
891 if let Some(ref time) = res.next {
892 let stime = time.to_bigint();
893 if stime.is_some() {
894 self.cursor.clone_from(&stime);
895 }
896 } else {
897 if let Some(end_time) = self.num_timestamps() {
899 self.cursor = Some(end_time);
900 } else {
901 warn!(
902 "Set cursor at transition: No timestamp count even though waveforms should be loaded"
903 );
904 }
905 }
906 } else if let Some(stime) = res.current.unwrap().0.to_bigint() {
907 let bigone = BigInt::from(1);
908 if stime == *cursor && *cursor >= bigone {
910 if let Ok(Some(newres)) = self.inner.as_waves().unwrap().query_variable(
912 &variable.variable_ref,
913 &(cursor - bigone).to_biguint().unwrap_or_default(),
914 ) && let Some(current) = newres.current
915 {
916 let newstime = current.0.to_bigint();
917 if newstime.is_some() {
918 self.cursor.clone_from(&newstime);
919 }
920 }
921 } else {
922 self.cursor = Some(stime);
923 }
924 }
925
926 if skip_zero {
928 if let Some(time) = &self.cursor {
931 let next_value = self.inner.as_waves().unwrap().query_variable(
932 &variable.variable_ref,
933 &time.to_biguint().unwrap_or_default(),
934 );
935 if next_value.is_ok_and(|r| {
936 r.is_some_and(|r| {
937 r.current.is_some_and(|v| match v.1 {
938 VariableValue::BigUint(v) => v.is_zero(),
939 VariableValue::String(_) => false,
940 })
941 })
942 }) {
943 self.set_cursor_at_transition(next, Some(vidx), false);
944 }
945 }
946 }
947 }
948 }
949
950 pub fn next_displayed_item_ref(&mut self) -> DisplayedItemRef {
951 self.display_item_ref_counter += 1;
952 self.display_item_ref_counter.into()
953 }
954
955 #[must_use]
959 pub fn num_timestamps(&self) -> Option<BigInt> {
960 self.inner
961 .max_timestamp()
962 .and_then(|r| if r.is_zero() { None } else { Some(r) })
963 .and_then(|r| r.to_bigint())
964 }
965
966 #[must_use]
969 pub fn safe_num_timestamps(&self) -> BigInt {
970 self.num_timestamps().unwrap_or_else(BigInt::one)
971 }
972
973 #[must_use]
974 pub fn get_displayed_item_index(
975 &self,
976 item_ref: &DisplayedItemRef,
977 ) -> Option<VisibleItemIndex> {
978 self.items_tree
980 .iter_visible()
981 .enumerate()
982 .find_map(|(vidx, node)| {
983 if node.item_ref == *item_ref {
984 Some(VisibleItemIndex(vidx))
985 } else {
986 None
987 }
988 })
989 }
990
991 pub fn build_analog_cache_async(
993 &self,
994 entry: std::sync::Arc<crate::analog_signal_cache::AnalogCacheEntry>,
995 variable_ref: &VariableRef,
996 translator: crate::translation::AnyTranslator,
997 sender: &std::sync::mpsc::Sender<crate::message::Message>,
998 ) -> Option<()> {
999 let wave_container = self.inner.as_waves()?;
1000 let meta = wave_container.variable_meta(variable_ref).ok()?.clone();
1001
1002 let num_timestamps = self.num_timestamps()?.to_u64()?;
1003
1004 let accessor = wave_container.signal_accessor(entry.cache_key.0).ok()?;
1005
1006 let sender_clone = sender.clone();
1007 crate::async_util::perform_work(move || {
1008 let result = crate::analog_signal_cache::AnalogSignalCache::build(
1009 accessor,
1010 &translator,
1011 &meta,
1012 num_timestamps,
1013 None,
1014 );
1015
1016 let msg = match result {
1017 Some(cache) => crate::message::Message::AnalogCacheBuilt {
1018 entry: entry.clone(),
1019 result: Ok(cache),
1020 },
1021 None => crate::message::Message::AnalogCacheBuilt {
1022 entry: entry.clone(),
1023 result: Err("Failed to build analog cache".into()),
1024 },
1025 };
1026
1027 crate::OUTSTANDING_TRANSACTIONS.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
1028 let _ = sender_clone.send(msg);
1029
1030 if let Some(ctx) = crate::EGUI_CONTEXT.read().unwrap().as_ref() {
1031 ctx.request_repaint();
1032 }
1033 });
1034
1035 Some(())
1036 }
1037}