Skip to main content

libsurfer/
wave_data.rs

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    /// Root items (variables, dividers, ...) to display
59    pub items_tree: DisplayedItemTree,
60    pub displayed_items: HashMap<DisplayedItemRef, DisplayedItem>,
61    /// Tracks the consecutive displayed item refs
62    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    /// These are just stored during operation, so no need to serialize
73    #[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    /// Generation counter for analog cache invalidation on waveform reload.
82    #[serde(skip)]
83    pub cache_generation: u64,
84    /// Registry of in-flight analog cache builds for sharing.
85    /// Cleared on waveform reload when generation changes.
86    #[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        // For a single bit that has other preferred translators in addition to "Bit", like enum,
111        // we would like to select the other one.
112        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    // make sure we always pick the same translator, at least
127    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, // Invalidate all existing caches
205            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    /// Go through all signals and update the metadata for all signals
238    ///
239    /// Used after loading new waves, signals or switching a bunch of translators
240    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    /// Get the underlying wave container to load all signals that are being displayed
268    ///
269    /// This is needed for wave containers that lazy-load signals.
270    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    /// Needs to be called after `update_with`, once the new number of timestamps is available in
283    /// the inner `WaveContainer`.
284    pub fn update_viewports(&mut self) {
285        if let Some(old_num_timestamps) = std::mem::take(&mut self.old_num_timestamps) {
286            // FIXME: I'm not sure if Defaulting to 1 time step is the right thing to do if we
287            // have none, but it does avoid some potentially nasty division by zero problems
288            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                    // keep without a change
314                    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        // load variables from waveform
426        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        // initialize translator and add display item
440        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    /// Remove a single item, it's legal to call this function with an invalid ID
489    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    /// Return an insert position based on item
686    ///
687    /// If an item is passed, and it is
688    /// - an unfolded group, insert index is to the first element of the group
689    /// - a folded group, insert index is to before the next sibling (if exists)
690    /// - otherwise insert index is past it on the same level
691    #[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        // TODO add get_next_sibling to tree?
699        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    /// Return insert position as last item
717    #[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    /// Insert item after item vidx if Some(vidx).
742    /// If None, insert in relation to focused item (see [`Self::focused_insert_position()`]).
743    /// If nothing is selected, fall back to appending.
744    /// Focus on the inserted item if there was a focused item.
745    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    /// Find the top-most of the currently visible items.
820    #[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.) // Subtract a bit of margin to avoid floating-point errors
831            .map_or(default, |(idx, _)| idx)
832    }
833
834    /// Find the item at a given y-location.
835    #[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        // Set scroll_offset to different between requested element and first element
859        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        // only scroll if new location is outside of visible area
866        if self.scroll_offset > self.total_height {
867            self.scroll_offset = item_y - first_element_y;
868        }
869    }
870
871    /// Set cursor at next (or previous, if `next` is false) transition of `variable`. If `skip_zero` is true,
872    /// use the next transition to a non-zero value.
873    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                    // No next transition, go to end
898                    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                // Check if we are on a transition
909                if stime == *cursor && *cursor >= bigone {
910                    // If so, subtract cursor position by one
911                    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 zero edges should be skipped
927            if skip_zero {
928                // check if the next transition is 0, if so and requested, go to
929                // next positive transition
930                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    /// Returns the number of timestamps in the current waves. For now, this adjusts the
956    /// number of timestamps as returned by wave sources if they specify 0 timestamps. This is
957    /// done to avoid having to consider what happens with the viewport.
958    #[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    /// Returns the number of timestamps in the current waves. This is like `num_timestamps` but
967    /// will always return at least 1.
968    #[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        // TODO check where this is called since it could now fail...
979        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    /// Spawn async worker to build analog cache. Worker holds Arc clone.
992    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}