libsurfer/
wave_data.rs

1use std::collections::HashMap;
2
3use eyre::{Result, WrapErr};
4use log::{error, info, warn};
5use num::bigint::ToBigInt as _;
6use num::{BigInt, BigUint, Zero};
7use serde::{Deserialize, Serialize};
8use surfer_translation_types::{TranslationPreference, Translator, VariableValue};
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::translation::{DynTranslator, TranslatorList, VariableInfoExt};
19use crate::variable_name_type::VariableNameType;
20use crate::view::ItemDrawingInfo;
21use crate::viewport::Viewport;
22use crate::wave_container::{ScopeRef, VariableMeta, VariableRef, VariableRefExt, WaveContainer};
23use crate::wave_source::{WaveFormat, WaveSource};
24use crate::wellen::LoadSignalsCmd;
25use ftr_parser::types::Transaction;
26use itertools::Itertools;
27use std::fmt::Formatter;
28use std::ops::Not;
29
30pub const PER_SCROLL_EVENT: f32 = 50.0;
31pub const SCROLL_EVENTS_PER_PAGE: f32 = 20.0;
32
33#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
34pub enum ScopeType {
35    WaveScope(ScopeRef),
36    StreamScope(StreamScopeRef),
37}
38
39impl std::fmt::Display for ScopeType {
40    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
41        match self {
42            ScopeType::WaveScope(w) => w.fmt(f),
43            ScopeType::StreamScope(s) => s.fmt(f),
44        }
45    }
46}
47
48#[derive(Serialize, Deserialize)]
49pub struct WaveData {
50    #[serde(skip, default = "DataContainer::__new_empty")]
51    pub inner: DataContainer,
52    pub source: WaveSource,
53    pub format: WaveFormat,
54    pub active_scope: Option<ScopeType>,
55    /// Root items (variables, dividers, ...) to display
56    pub items_tree: DisplayedItemTree,
57    pub displayed_items: HashMap<DisplayedItemRef, DisplayedItem>,
58    /// Tracks the consecutive displayed item refs
59    pub display_item_ref_counter: usize,
60    pub viewports: Vec<Viewport>,
61    pub cursor: Option<BigInt>,
62    pub markers: HashMap<u8, BigInt>,
63    pub focused_item: Option<VisibleItemIndex>,
64    pub focused_transaction: (Option<TransactionRef>, Option<Transaction>),
65    pub default_variable_name_type: VariableNameType,
66    pub scroll_offset: f32,
67    pub display_variable_indices: bool,
68    pub graphics: HashMap<GraphicId, Graphic>,
69    /// These are just stored during operation, so no need to serialize
70    #[serde(skip)]
71    pub drawing_infos: Vec<ItemDrawingInfo>,
72    #[serde(skip)]
73    pub top_item_draw_offset: f32,
74    #[serde(skip)]
75    pub total_height: f32,
76    /// used by the `update_viewports` method after loading a new file
77    #[serde(skip)]
78    pub old_num_timestamps: Option<BigInt>,
79}
80
81fn select_preferred_translator(var: &VariableMeta, translators: &TranslatorList) -> String {
82    let mut preferred: Vec<_> = translators
83        .all_translators()
84        .iter()
85        .filter_map(|t| match t.translates(var) {
86            Ok(TranslationPreference::Prefer) => Some(t.name()),
87            Ok(TranslationPreference::Yes) => None,
88            Ok(TranslationPreference::No) => None,
89            Err(e) => {
90                error!(
91                    "Failed to check if {} translates {}\n{e:#?}",
92                    t.name(),
93                    var.var.full_path_string()
94                );
95                None
96            }
97        })
98        .collect();
99    if preferred.len() > 1 {
100        // For a single bit that has other preferred translators in addition to "Bit", like enum,
101        // we would like to select the other one.
102        let bit = "Bit".to_string();
103        if var.num_bits == Some(1) {
104            preferred.retain(|x| x != &bit);
105        }
106        if preferred.len() > 1 {
107            warn!(
108                "More than one preferred translator for variable {} in scope {}: {}",
109                var.var.name,
110                var.var.path.strs.join("."),
111                preferred.join(", ")
112            );
113            preferred.sort();
114        }
115    }
116    // make sure we always pick the same translator, at least
117    preferred
118        .pop()
119        .unwrap_or_else(|| translators.default.clone())
120}
121
122pub fn variable_translator<'a, F>(
123    translator: Option<&String>,
124    field: &[String],
125    translators: &'a TranslatorList,
126    meta: F,
127) -> &'a DynTranslator
128where
129    F: FnOnce() -> Result<VariableMeta>,
130{
131    let translator_name = translator
132        .cloned()
133        .or_else(|| {
134            Some(if field.is_empty() {
135                meta()
136                    .as_ref()
137                    .map(|meta| select_preferred_translator(meta, translators).clone())
138                    .unwrap_or_else(|e| {
139                        warn!("{e:#?}");
140                        translators.default.clone()
141                    })
142            } else {
143                translators.default.clone()
144            })
145        })
146        .unwrap();
147
148    let translator = translators.get_translator(&translator_name);
149    translator
150}
151
152impl WaveData {
153    pub fn update_with_waves(
154        mut self,
155        new_waves: Box<WaveContainer>,
156        source: WaveSource,
157        format: WaveFormat,
158        translators: &TranslatorList,
159        keep_unavailable: bool,
160    ) -> (WaveData, Option<LoadSignalsCmd>) {
161        let active_scope = self.active_scope.take().filter(|m| {
162            if let ScopeType::WaveScope(w) = m {
163                new_waves.scope_exists(w)
164            } else {
165                false
166            }
167        });
168        let display_items = self.update_displayed_items(
169            &new_waves,
170            &self.displayed_items,
171            keep_unavailable,
172            translators,
173        );
174
175        let old_num_timestamps = self.num_timestamps();
176        let mut new_wavedata = WaveData {
177            inner: DataContainer::Waves(*new_waves),
178            source,
179            format,
180            active_scope,
181            items_tree: self.items_tree,
182            displayed_items: display_items,
183            display_item_ref_counter: self.display_item_ref_counter,
184            viewports: self.viewports,
185            cursor: self.cursor.clone(),
186            markers: self.markers.clone(),
187            focused_item: self.focused_item,
188            focused_transaction: self.focused_transaction,
189            default_variable_name_type: self.default_variable_name_type,
190            display_variable_indices: self.display_variable_indices,
191            scroll_offset: self.scroll_offset,
192            drawing_infos: vec![],
193            top_item_draw_offset: 0.,
194            graphics: HashMap::new(),
195            total_height: 0.,
196            old_num_timestamps,
197        };
198
199        new_wavedata.update_metadata(translators);
200        let load_commands = new_wavedata.load_waves();
201        (new_wavedata, load_commands)
202    }
203
204    pub fn update_with_items(
205        &mut self,
206        new_items: &HashMap<DisplayedItemRef, DisplayedItem>,
207        items_tree: DisplayedItemTree,
208        translators: &TranslatorList,
209    ) -> Option<LoadSignalsCmd> {
210        self.items_tree = items_tree;
211        self.displayed_items = self.update_displayed_items(
212            self.inner.as_waves().unwrap(),
213            new_items,
214            true,
215            translators,
216        );
217        self.display_item_ref_counter = self
218            .displayed_items
219            .keys()
220            .map(|dir| dir.0)
221            .max()
222            .unwrap_or(0);
223
224        self.update_metadata(translators);
225        self.load_waves()
226    }
227
228    /// Go through all signals and update the metadata for all signals
229    ///
230    /// Used after loading new waves, signals or switching a bunch of translators
231    fn update_metadata(&mut self, translators: &TranslatorList) {
232        for (_vidx, di) in self.displayed_items.iter_mut() {
233            let DisplayedItem::Variable(displayed_variable) = di else {
234                continue;
235            };
236
237            let meta = self
238                .inner
239                .as_waves()
240                .unwrap()
241                .variable_meta(&displayed_variable.variable_ref.clone())
242                .unwrap();
243            let translator =
244                variable_translator(displayed_variable.get_format(&[]), &[], translators, || {
245                    Ok(meta.clone())
246                });
247            let info = translator.variable_info(&meta).ok();
248
249            match info {
250                Some(info) => displayed_variable
251                    .field_formats
252                    .retain(|ff| info.has_subpath(&ff.field)),
253                _ => displayed_variable.field_formats.clear(),
254            }
255        }
256    }
257
258    /// Get the underlying wave container to load all signals that are being displayed
259    ///
260    /// This is needed for wave containers that lazy-load signals.
261    fn load_waves(&mut self) -> Option<LoadSignalsCmd> {
262        let variables = self.displayed_items.values().filter_map(|item| match item {
263            DisplayedItem::Variable(r) => Some(&r.variable_ref),
264            _ => None,
265        });
266        self.inner
267            .as_waves_mut()
268            .unwrap()
269            .load_variables(variables)
270            .expect("internal error: failed to load variables")
271    }
272
273    /// Needs to be called after update_with, once the new number of timestamps is available in
274    /// the inner WaveContainer.
275    pub fn update_viewports(&mut self) {
276        if let Some(old_num_timestamps) = std::mem::take(&mut self.old_num_timestamps) {
277            // FIXME: I'm not sure if Defaulting to 1 time step is the right thing to do if we
278            // have none, but it does avoid some potentially nasty division by zero problems
279            let new_num_timestamps = self
280                .inner
281                .max_timestamp()
282                .unwrap_or_else(|| BigUint::from(1u32))
283                .to_bigint()
284                .unwrap();
285            if new_num_timestamps != old_num_timestamps {
286                for viewport in self.viewports.iter_mut() {
287                    *viewport = viewport.clip_to(&old_num_timestamps, &new_num_timestamps);
288                }
289            }
290        }
291    }
292
293    fn update_displayed_items(
294        &self,
295        waves: &WaveContainer,
296        items: &HashMap<DisplayedItemRef, DisplayedItem>,
297        keep_unavailable: bool,
298        translators: &TranslatorList,
299    ) -> HashMap<DisplayedItemRef, DisplayedItem> {
300        items
301            .iter()
302            .filter_map(|(id, i)| {
303                match i {
304                    // keep without a change
305                    DisplayedItem::Divider(_)
306                    | DisplayedItem::Marker(_)
307                    | DisplayedItem::TimeLine(_)
308                    | DisplayedItem::Stream(_)
309                    | DisplayedItem::Group(_) => Some((*id, i.clone())),
310                    DisplayedItem::Variable(s) => {
311                        s.update(waves, keep_unavailable).map(|r| (*id, r))
312                    }
313                    DisplayedItem::Placeholder(p) => {
314                        match waves.update_variable_ref(&p.variable_ref) {
315                            None => {
316                                if keep_unavailable {
317                                    Some((*id, DisplayedItem::Placeholder(p.clone())))
318                                } else {
319                                    None
320                                }
321                            }
322                            Some(new_variable_ref) => {
323                                let Ok(meta) = waves
324                                    .variable_meta(&new_variable_ref)
325                                    .context("When updating")
326                                    .map_err(|e| error!("{e:#?}"))
327                                else {
328                                    return Some((*id, DisplayedItem::Placeholder(p.clone())));
329                                };
330                                let translator = variable_translator(
331                                    p.format.as_ref(),
332                                    &[],
333                                    translators,
334                                    || Ok(meta.clone()),
335                                );
336                                let info = translator.variable_info(&meta).unwrap();
337                                Some((
338                                    *id,
339                                    DisplayedItem::Variable(
340                                        p.clone().into_variable(info, new_variable_ref),
341                                    ),
342                                ))
343                            }
344                        }
345                    }
346                }
347            })
348            .collect()
349    }
350
351    pub fn select_preferred_translator(
352        &self,
353        var: VariableMeta,
354        translators: &TranslatorList,
355    ) -> String {
356        select_preferred_translator(&var, translators)
357    }
358
359    pub fn variable_translator<'a>(
360        &'a self,
361        field: &DisplayedFieldRef,
362        translators: &'a TranslatorList,
363    ) -> &'a DynTranslator {
364        let Some(DisplayedItem::Variable(displayed_variable)) =
365            self.displayed_items.get(&field.item)
366        else {
367            panic!("asking for translator for a non DisplayItem::Variable item")
368        };
369
370        variable_translator(
371            displayed_variable.get_format(&field.field),
372            &field.field,
373            translators,
374            || {
375                self.inner
376                    .as_waves()
377                    .unwrap()
378                    .variable_meta(&displayed_variable.variable_ref)
379            },
380        )
381    }
382
383    pub fn add_variables(
384        &mut self,
385        translators: &TranslatorList,
386        variables: Vec<VariableRef>,
387        target_position: Option<TargetPosition>,
388        update_display_names: bool,
389    ) -> (Option<LoadSignalsCmd>, Vec<DisplayedItemRef>) {
390        let mut indices = vec![];
391        // load variables from waveform
392        let res = match self
393            .inner
394            .as_waves_mut()
395            .unwrap()
396            .load_variables(variables.iter())
397        {
398            Err(e) => {
399                error!("{e:#?}");
400                return (None, indices);
401            }
402            Ok(res) => res,
403        };
404
405        // initialize translator and add display item
406        let mut target_position = target_position
407            .or_else(|| self.focused_insert_position())
408            .unwrap_or(self.end_insert_position());
409        for variable in variables {
410            let Ok(meta) = self
411                .inner
412                .as_waves()
413                .unwrap()
414                .variable_meta(&variable)
415                .context("When adding variable")
416                .map_err(|e| error!("{e:#?}"))
417            else {
418                return (res, indices);
419            };
420
421            let translator = variable_translator(None, &[], translators, || Ok(meta.clone()));
422            let info = translator.variable_info(&meta).unwrap();
423
424            let new_variable = DisplayedItem::Variable(DisplayedVariable {
425                variable_ref: variable.clone(),
426                info,
427                color: None,
428                background_color: None,
429                display_name: variable.name.clone(),
430                display_name_type: self.default_variable_name_type,
431                manual_name: None,
432                format: None,
433                field_formats: vec![],
434                height_scaling_factor: None,
435            });
436
437            indices.push(self.insert_item(new_variable, Some(target_position), true));
438            target_position = TargetPosition {
439                before: ItemIndex(target_position.before.0 + 1),
440                level: target_position.level,
441            }
442        }
443
444        if update_display_names {
445            self.compute_variable_display_names();
446        }
447        (res, indices)
448    }
449
450    pub fn remove_displayed_item(&mut self, id: DisplayedItemRef) {
451        let Some(idx) = self
452            .items_tree
453            .iter()
454            .enumerate()
455            .find(|(_, node)| node.item_ref == id)
456            .map(|(idx, _)| ItemIndex(idx))
457        else {
458            return;
459        };
460
461        let focused_item_ref = self
462            .focused_item
463            .and_then(|vidx| self.items_tree.get_visible(vidx))
464            .map(|node| node.item_ref);
465
466        for removed_ref in self.items_tree.remove_recursive(idx) {
467            if let Some(DisplayedItem::Marker(m)) = self.displayed_items.remove(&removed_ref) {
468                self.markers.remove(&m.idx);
469            }
470        }
471
472        self.focused_item = focused_item_ref.and_then(|focused_item_ref| {
473            match self
474                .items_tree
475                .iter_visible()
476                .find_position(|node| node.item_ref == focused_item_ref)
477                .map(|(vidx, _)| VisibleItemIndex(vidx))
478            {
479                Some(vidx) => Some(vidx),
480                None if self
481                    .focused_item
482                    .and_then(|focused_vidx| self.items_tree.to_displayed(focused_vidx))
483                    .is_some() =>
484                {
485                    Some(self.focused_item.unwrap())
486                }
487                None => self
488                    .items_tree
489                    .iter_visible()
490                    .count()
491                    .checked_sub(1)
492                    .map(VisibleItemIndex),
493            }
494        })
495    }
496
497    pub fn add_divider(&mut self, name: Option<String>, vidx: Option<VisibleItemIndex>) {
498        self.insert_item(
499            DisplayedItem::Divider(DisplayedDivider {
500                color: None,
501                background_color: None,
502                name,
503            }),
504            self.vidx_insert_position(vidx),
505            true,
506        );
507    }
508
509    pub fn add_timeline(&mut self, vidx: Option<VisibleItemIndex>) {
510        self.insert_item(
511            DisplayedItem::TimeLine(DisplayedTimeLine {
512                color: None,
513                background_color: None,
514                name: None,
515            }),
516            self.vidx_insert_position(vidx),
517            true,
518        );
519    }
520
521    pub fn add_group(
522        &mut self,
523        name: String,
524        target_position: Option<TargetPosition>,
525    ) -> DisplayedItemRef {
526        self.insert_item(
527            DisplayedItem::Group(DisplayedGroup {
528                name,
529                color: None,
530                background_color: None,
531                content: vec![],
532                is_open: false,
533            }),
534            target_position,
535            true,
536        )
537    }
538
539    pub fn add_generator(&mut self, gen_ref: TransactionStreamRef) {
540        let Some(gen_id) = gen_ref.gen_id else { return };
541
542        if self
543            .inner
544            .as_transactions()
545            .unwrap()
546            .get_generator(gen_id)
547            .unwrap()
548            .transactions
549            .is_empty()
550        {
551            info!("(Generator {})Loading transactions into memory!", gen_id);
552            match self
553                .inner
554                .as_transactions_mut()
555                .unwrap()
556                .inner
557                .load_stream_into_memory(gen_ref.stream_id)
558            {
559                Ok(_) => info!("(Generator {}) Finished loading transactions!", gen_id),
560                Err(_) => return,
561            }
562        }
563
564        let gen = self
565            .inner
566            .as_transactions()
567            .unwrap()
568            .get_generator(gen_id)
569            .unwrap();
570        let mut last_times_on_row = vec![(BigUint::ZERO, BigUint::ZERO)];
571        calculate_rows_of_stream(&gen.transactions, &mut last_times_on_row);
572
573        let new_gen = DisplayedItem::Stream(DisplayedStream {
574            display_name: gen_ref.name.clone(),
575            transaction_stream_ref: gen_ref,
576            color: None,
577            background_color: None,
578            manual_name: None,
579            rows: last_times_on_row.len(),
580        });
581
582        self.insert_item(new_gen, None, true);
583    }
584
585    pub fn add_stream(&mut self, stream_ref: TransactionStreamRef) {
586        if self
587            .inner
588            .as_transactions_mut()
589            .unwrap()
590            .get_stream(stream_ref.stream_id)
591            .unwrap()
592            .transactions_loaded
593            .not()
594        {
595            info!("(Stream)Loading transactions into memory!");
596            match self
597                .inner
598                .as_transactions_mut()
599                .unwrap()
600                .inner
601                .load_stream_into_memory(stream_ref.stream_id)
602            {
603                Ok(_) => info!(
604                    "(Stream {}) Finished loading transactions!",
605                    stream_ref.stream_id
606                ),
607                Err(_) => return,
608            }
609        }
610
611        let stream = self
612            .inner
613            .as_transactions()
614            .unwrap()
615            .get_stream(stream_ref.stream_id)
616            .unwrap();
617        let mut last_times_on_row = vec![(BigUint::ZERO, BigUint::ZERO)];
618
619        for gen_id in &stream.generators {
620            let gen = self
621                .inner
622                .as_transactions()
623                .unwrap()
624                .get_generator(*gen_id)
625                .unwrap();
626            calculate_rows_of_stream(&gen.transactions, &mut last_times_on_row);
627        }
628
629        let new_stream = DisplayedItem::Stream(DisplayedStream {
630            display_name: stream_ref.name.clone(),
631            transaction_stream_ref: stream_ref,
632            color: None,
633            background_color: None,
634            manual_name: None,
635            rows: last_times_on_row.len(),
636        });
637
638        self.insert_item(new_stream, None, true);
639    }
640
641    pub fn add_all_streams(&mut self) {
642        let mut streams: Vec<(usize, String)> = vec![];
643        for stream in self.inner.as_transactions().unwrap().get_streams() {
644            streams.push((stream.id, stream.name.clone()));
645        }
646
647        for (id, name) in streams {
648            self.add_stream(TransactionStreamRef::new_stream(id, name));
649        }
650    }
651
652    fn vidx_insert_position(&self, vidx: Option<VisibleItemIndex>) -> Option<TargetPosition> {
653        let vidx = vidx?;
654        let info = self.items_tree.get_visible_extra(vidx)?;
655        let level = match self.displayed_items.get(&info.node.item_ref)? {
656            DisplayedItem::Group(_) if info.node.unfolded => info.node.level + 1,
657            _ => info.node.level,
658        };
659        Some(TargetPosition {
660            before: ItemIndex(info.idx.0 + 1),
661            level,
662        })
663    }
664
665    /// Return an insert position based on the focused item
666    ///
667    /// If an item is focused, and it is
668    /// - an unfolded group, insert index is to the first element of the group
669    /// - a folded group, insert index is to before the next sibling (if exists)
670    /// - otherwise insert index is past it on the same level
671    pub fn focused_insert_position(&self) -> Option<TargetPosition> {
672        let vidx = self.focused_item?;
673        let item_index = self.items_tree.to_displayed(vidx)?;
674        let node = self.items_tree.get(item_index)?;
675        let item = self.displayed_items.get(&node.item_ref)?;
676
677        // TODO add get_next_sibling to tree?
678        let (before, level) = match item {
679            DisplayedItem::Group(..) if node.unfolded => (item_index.0 + 1, node.level + 1),
680            DisplayedItem::Group(..) => {
681                let next_idx = self.items_tree.to_displayed(VisibleItemIndex(vidx.0 + 1));
682                match next_idx {
683                    Some(idx) => (idx.0, node.level),
684                    None => (self.items_tree.len(), node.level),
685                }
686            }
687            _ => (item_index.0 + 1, node.level),
688        };
689        Some(TargetPosition {
690            before: ItemIndex(before),
691            level,
692        })
693    }
694
695    pub fn end_insert_position(&self) -> TargetPosition {
696        TargetPosition {
697            before: ItemIndex(self.items_tree.len()),
698            level: 0,
699        }
700    }
701
702    pub fn index_for_ref_or_focus(&self, item_ref: Option<DisplayedItemRef>) -> Option<ItemIndex> {
703        if let Some(item_ref) = item_ref {
704            self.items_tree
705                .iter()
706                .enumerate()
707                .find_map(|(idx, node)| (node.item_ref == item_ref).then_some(ItemIndex(idx)))
708        } else if let Some(focused_item) = self.focused_item {
709            self.items_tree
710                .get_visible_extra(focused_item)
711                .map(|info| info.idx)
712        } else {
713            None
714        }
715    }
716
717    /// Insert item after item vidx if Some(vidx).
718    /// If None, insert in relation to focused item (see [`Self::focused_insert_position()`]).
719    /// If nothing is selected, fall back to appending.
720    /// Focus on the inserted item if there was a focused item.
721    pub(crate) fn insert_item(
722        &mut self,
723        new_item: DisplayedItem,
724        target_position: Option<TargetPosition>,
725        move_focus: bool,
726    ) -> DisplayedItemRef {
727        let target_position = target_position
728            .or_else(|| self.focused_insert_position())
729            .unwrap_or_else(|| self.end_insert_position());
730
731        let item_ref = self.next_displayed_item_ref();
732        let insert_index = self
733            .items_tree
734            .insert_item(item_ref, target_position)
735            .unwrap();
736        self.displayed_items.insert(item_ref, new_item);
737        if move_focus {
738            self.focused_item = self.focused_item.and_then(|_| {
739                self.items_tree
740                    .iter_visible_extra()
741                    .find_map(|info| (info.idx == insert_index).then_some(info.vidx))
742            });
743        }
744        self.items_tree.xselect_all_visible(false);
745        item_ref
746    }
747
748    pub fn go_to_cursor_if_not_in_view(&mut self) -> bool {
749        if let Some(cursor) = &self.cursor {
750            let num_timestamps = self.num_timestamps().unwrap_or(1.into());
751            self.viewports[0].go_to_cursor_if_not_in_view(cursor, &num_timestamps)
752        } else {
753            false
754        }
755    }
756
757    #[inline]
758    pub fn numbered_marker_location(&self, idx: u8, viewport: &Viewport, view_width: f32) -> f32 {
759        viewport.pixel_from_time(
760            self.numbered_marker_time(idx),
761            view_width,
762            &self.num_timestamps().unwrap_or(1.into()),
763        )
764    }
765
766    #[inline]
767    pub fn numbered_marker_time(&self, idx: u8) -> &BigInt {
768        self.markers.get(&idx).unwrap()
769    }
770
771    pub fn viewport_all(&self) -> Viewport {
772        Viewport::new()
773    }
774
775    pub fn remove_placeholders(&mut self) {
776        let removed_refs = self.items_tree.drain_recursive_if(|node| {
777            matches!(
778                self.displayed_items.get(&node.item_ref),
779                Some(DisplayedItem::Placeholder(_))
780            )
781        });
782        for removed_ref in removed_refs {
783            self.displayed_items.remove(&removed_ref);
784        }
785    }
786
787    #[inline]
788    pub fn any_displayed(&self) -> bool {
789        !self.displayed_items.is_empty()
790    }
791
792    /// Find the top-most of the currently visible items.
793    pub fn get_top_item(&self) -> usize {
794        let default = if self.drawing_infos.is_empty() {
795            0
796        } else {
797            self.drawing_infos.len() - 1
798        };
799        self.drawing_infos
800            .iter()
801            .enumerate()
802            .find(|(_, di)| di.top() >= self.top_item_draw_offset - 1.) // Subtract a bit of margin to avoid floating-point errors
803            .map_or(default, |(idx, _)| idx)
804    }
805
806    /// Find the item at a given y-location.
807    pub fn get_item_at_y(&self, y: f32) -> Option<VisibleItemIndex> {
808        if self.drawing_infos.is_empty() {
809            return None;
810        }
811        let first_element_top = self.drawing_infos.first().unwrap().top();
812        let first_element_bottom = self.drawing_infos.last().unwrap().bottom();
813        let threshold = y + first_element_top + self.scroll_offset;
814        if first_element_bottom <= threshold {
815            return None;
816        }
817        self.drawing_infos
818            .iter()
819            .enumerate()
820            .rev()
821            .find(|(_, di)| di.top() <= threshold)
822            .map(|(vidx, _)| VisibleItemIndex(vidx))
823    }
824
825    pub fn scroll_to_item(&mut self, idx: usize) {
826        if self.drawing_infos.is_empty() {
827            return;
828        }
829        // Set scroll_offset to different between requested element and first element
830        let first_element_y = self.drawing_infos.first().unwrap().top();
831        let item_y = self
832            .drawing_infos
833            .get(idx)
834            .unwrap_or_else(|| self.drawing_infos.last().unwrap())
835            .top();
836        // only scroll if new location is outside of visible area
837        if self.scroll_offset > self.total_height {
838            self.scroll_offset = item_y - first_element_y;
839        }
840    }
841
842    /// Set cursor at next (or previous, if `next` is false) transition of `variable`. If `skip_zero` is true,
843    /// use the next transition to a non-zero value.
844    pub fn set_cursor_at_transition(
845        &mut self,
846        next: bool,
847        variable: Option<VisibleItemIndex>,
848        skip_zero: bool,
849    ) {
850        if let Some(vidx) = variable.or(self.focused_item) {
851            if let Some(cursor) = &self.cursor {
852                if let Some(DisplayedItem::Variable(variable)) = &self
853                    .items_tree
854                    .get_visible(vidx)
855                    .and_then(|node| self.displayed_items.get(&node.item_ref))
856                {
857                    if let Ok(Some(res)) = self.inner.as_waves().unwrap().query_variable(
858                        &variable.variable_ref,
859                        &cursor.to_biguint().unwrap_or_default(),
860                    ) {
861                        if next {
862                            if let Some(ref time) = res.next {
863                                let stime = time.to_bigint();
864                                if stime.is_some() {
865                                    self.cursor.clone_from(&stime);
866                                }
867                            } else {
868                                // No next transition, go to end
869                                self.cursor = Some(self.num_timestamps().expect(
870                                    "No timestamp count even though waveforms should be loaded",
871                                ));
872                            }
873                        } else if let Some(stime) = res.current.unwrap().0.to_bigint() {
874                            let bigone = BigInt::from(1);
875                            // Check if we are on a transition
876                            if stime == *cursor && *cursor >= bigone {
877                                // If so, subtract cursor position by one
878                                if let Ok(Some(newres)) =
879                                    self.inner.as_waves().unwrap().query_variable(
880                                        &variable.variable_ref,
881                                        &(cursor - bigone).to_biguint().unwrap_or_default(),
882                                    )
883                                {
884                                    if let Some(current) = newres.current {
885                                        let newstime = current.0.to_bigint();
886                                        if newstime.is_some() {
887                                            self.cursor.clone_from(&newstime);
888                                        }
889                                    }
890                                }
891                            } else {
892                                self.cursor = Some(stime);
893                            }
894                        }
895
896                        // if zero edges should be skipped
897                        if skip_zero {
898                            // check if the next transition is 0, if so and requested, go to
899                            // next positive transition
900                            if let Some(time) = &self.cursor {
901                                let next_value = self.inner.as_waves().unwrap().query_variable(
902                                    &variable.variable_ref,
903                                    &time.to_biguint().unwrap_or_default(),
904                                );
905                                if next_value.is_ok_and(|r| {
906                                    r.is_some_and(|r| {
907                                        r.current.is_some_and(|v| match v.1 {
908                                            VariableValue::BigUint(v) => v == BigUint::from(0u8),
909                                            _ => false,
910                                        })
911                                    })
912                                }) {
913                                    self.set_cursor_at_transition(next, Some(vidx), false);
914                                }
915                            }
916                        }
917                    }
918                }
919            }
920        }
921    }
922
923    pub fn next_displayed_item_ref(&mut self) -> DisplayedItemRef {
924        self.display_item_ref_counter += 1;
925        self.display_item_ref_counter.into()
926    }
927
928    /// Returns the number of timestamps in the current waves. For now, this adjusts the
929    /// number of timestamps as returned by wave sources if they specify 0 timestamps. This is
930    /// done to avoid having to consider what happens with the viewport.
931    pub fn num_timestamps(&self) -> Option<BigInt> {
932        self.inner
933            .max_timestamp()
934            .and_then(|r| if r == BigUint::zero() { None } else { Some(r) })
935            .and_then(|r| r.to_bigint())
936    }
937
938    pub fn get_displayed_item_index(
939        &self,
940        item_ref: &DisplayedItemRef,
941    ) -> Option<VisibleItemIndex> {
942        // TODO check where this is called since it could now fail...
943        self.items_tree
944            .iter_visible()
945            .enumerate()
946            .find_map(|(vidx, node)| {
947                if node.item_ref == *item_ref {
948                    Some(VisibleItemIndex(vidx))
949                } else {
950                    None
951                }
952            })
953    }
954}
955
956fn calculate_rows_of_stream(
957    transactions: &Vec<Transaction>,
958    last_times_on_row: &mut Vec<(BigUint, BigUint)>,
959) {
960    for transaction in transactions {
961        let mut curr_row = 0;
962        let start_time = transaction.get_start_time();
963        let end_time = transaction.get_end_time();
964
965        while start_time > last_times_on_row[curr_row].0
966            && start_time < last_times_on_row[curr_row].1
967        {
968            curr_row += 1;
969            if last_times_on_row.len() <= curr_row {
970                last_times_on_row.push((BigUint::ZERO, BigUint::ZERO));
971            }
972        }
973        last_times_on_row[curr_row] = (start_time, end_time);
974    }
975}