libsurfer/
lib.rs

1#![deny(unused_crate_dependencies)]
2
3pub mod async_util;
4pub mod batch_commands;
5#[cfg(feature = "performance_plot")]
6pub mod benchmark;
7mod channels;
8pub mod clock_highlighting;
9pub mod command_parser;
10pub mod command_prompt;
11pub mod config;
12pub mod cxxrtl;
13pub mod cxxrtl_container;
14pub mod data_container;
15pub mod dialog;
16pub mod displayed_item;
17pub mod displayed_item_tree;
18pub mod drawing_canvas;
19pub mod file_dialog;
20pub mod file_watcher;
21pub mod fzcmd;
22pub mod graphics;
23pub mod help;
24pub mod hierarchy;
25pub mod keys;
26pub mod logs;
27pub mod marker;
28pub mod menus;
29pub mod message;
30pub mod mousegestures;
31pub mod overview;
32pub mod remote;
33pub mod state;
34pub mod state_file_io;
35pub mod state_util;
36pub mod statusbar;
37pub mod system_state;
38#[cfg(test)]
39pub mod tests;
40pub mod time;
41pub mod toolbar;
42pub mod tooltips;
43pub mod transaction_container;
44pub mod transactions;
45pub mod translation;
46pub mod util;
47pub mod variable_direction;
48pub mod variable_filter;
49mod variable_index;
50pub mod variable_name_type;
51pub mod variable_type;
52pub mod view;
53pub mod viewport;
54#[cfg(target_arch = "wasm32")]
55pub mod wasm_api;
56#[cfg(target_arch = "wasm32")]
57pub mod wasm_panic;
58pub mod wave_container;
59pub mod wave_data;
60pub mod wave_source;
61pub mod wcp;
62pub mod wellen;
63
64use crate::config::AutoLoad;
65use crate::displayed_item_tree::ItemIndex;
66use crate::displayed_item_tree::TargetPosition;
67use std::collections::HashMap;
68use std::sync::atomic::{AtomicU32, Ordering};
69use std::sync::mpsc::{self, Receiver, Sender};
70use std::sync::{Arc, RwLock};
71
72use batch_commands::read_command_bytes;
73use batch_commands::read_command_file;
74#[cfg(target_arch = "wasm32")]
75use channels::{GlobalChannelTx, IngressHandler, IngressReceiver};
76use derive_more::Display;
77use displayed_item::DisplayedVariable;
78use displayed_item_tree::DisplayedItemTree;
79use eframe::{App, CreationContext};
80use egui::{FontData, FontDefinitions, FontFamily};
81use eyre::Context;
82use eyre::Result;
83use ftr_parser::types::Transaction;
84use futures::executor::block_on;
85use itertools::Itertools;
86use lazy_static::lazy_static;
87use log::{error, info, warn};
88use message::MessageTarget;
89use num::BigInt;
90use serde::Deserialize;
91use surfer_translation_types::Translator;
92pub use system_state::SystemState;
93#[cfg(target_arch = "wasm32")]
94use tokio_stream as _;
95#[cfg(all(not(target_arch = "wasm32"), feature = "wasm_plugins"))]
96use translation::wasm_translator::PluginTranslator;
97use wave_container::ScopeRef;
98use wcp::{proto::WcpCSMessage, proto::WcpEvent, proto::WcpSCMessage};
99
100#[cfg(all(not(target_arch = "wasm32"), feature = "wasm_plugins"))]
101use crate::async_util::perform_work;
102use crate::config::{SurferConfig, SurferTheme};
103use crate::dialog::{OpenSiblingStateFileDialog, ReloadWaveformDialog};
104use crate::displayed_item::{DisplayedFieldRef, DisplayedItem, DisplayedItemRef, FieldFormat};
105use crate::displayed_item_tree::VisibleItemIndex;
106use crate::drawing_canvas::TxDrawingCommands;
107use crate::message::Message;
108use crate::transaction_container::{StreamScopeRef, TransactionRef, TransactionStreamRef};
109use crate::translation::{all_translators, AnyTranslator};
110use crate::variable_filter::{VariableIOFilterType, VariableNameFilterType};
111use crate::viewport::Viewport;
112use crate::wave_container::VariableRefExt;
113use crate::wave_container::{ScopeRefExt, WaveContainer};
114use crate::wave_data::{ScopeType, WaveData};
115use crate::wave_source::{LoadOptions, WaveFormat, WaveSource};
116use crate::wellen::{convert_format, HeaderResult};
117
118lazy_static! {
119    pub static ref EGUI_CONTEXT: RwLock<Option<Arc<egui::Context>>> = RwLock::new(None);
120    /// A number that is non-zero if there are asynchronously triggered operations that
121    /// have been triggered but not successfully completed yet. In practice, if this is
122    /// non-zero, we will re-run the egui update function in order to ensure that we deal
123    /// with the outstanding transactions eventually.
124    /// When incrementing this, it is important to make sure that it gets decremented
125    /// whenever the asynchronous transaction is completed, otherwise we will re-render
126    /// things until program exit
127    pub(crate) static ref OUTSTANDING_TRANSACTIONS: AtomicU32 = AtomicU32::new(0);
128}
129
130#[cfg(target_arch = "wasm32")]
131lazy_static! {
132    pub(crate) static ref WCP_CS_HANDLER: IngressHandler<WcpCSMessage> = IngressHandler::new();
133    pub(crate) static ref WCP_SC_HANDLER: GlobalChannelTx<WcpSCMessage> = GlobalChannelTx::new();
134}
135
136#[derive(Default)]
137pub struct StartupParams {
138    pub waves: Option<WaveSource>,
139    pub wcp_initiate: Option<u16>,
140    pub startup_commands: Vec<String>,
141}
142
143fn setup_custom_font(ctx: &egui::Context) {
144    let mut fonts = FontDefinitions::default();
145
146    fonts.font_data.insert(
147        "remix_icons".to_owned(),
148        FontData::from_static(egui_remixicon::FONT).into(),
149    );
150
151    fonts
152        .families
153        .get_mut(&FontFamily::Proportional)
154        .unwrap()
155        .push("remix_icons".to_owned());
156
157    fonts
158        .families
159        .get_mut(&FontFamily::Monospace)
160        .unwrap()
161        .push("remix_icons".to_owned());
162
163    ctx.set_fonts(fonts);
164}
165
166pub fn run_egui(cc: &CreationContext, mut state: SystemState) -> Result<Box<dyn App>> {
167    let ctx_arc = Arc::new(cc.egui_ctx.clone());
168    *EGUI_CONTEXT.write().unwrap() = Some(ctx_arc.clone());
169    state.context = Some(ctx_arc.clone());
170    cc.egui_ctx
171        .set_visuals_of(egui::Theme::Dark, state.get_visuals());
172    cc.egui_ctx
173        .set_visuals_of(egui::Theme::Light, state.get_visuals());
174    #[cfg(not(target_arch = "wasm32"))]
175    if state.user.config.wcp.autostart {
176        state.start_wcp_server(Some(state.user.config.wcp.address.clone()), false);
177    }
178    setup_custom_font(&cc.egui_ctx);
179    Ok(Box::new(state))
180}
181
182#[derive(Debug, Clone, Copy, Deserialize, Display, PartialEq, Eq)]
183pub enum MoveDir {
184    #[display("up")]
185    Up,
186
187    #[display("down")]
188    Down,
189}
190
191pub enum ColorSpecifier {
192    Index(usize),
193    Name(String),
194}
195
196enum CachedDrawData {
197    WaveDrawData(CachedWaveDrawData),
198    TransactionDrawData(CachedTransactionDrawData),
199}
200
201struct CachedWaveDrawData {
202    pub draw_commands: HashMap<DisplayedFieldRef, drawing_canvas::DrawingCommands>,
203    pub clock_edges: Vec<f32>,
204    pub ticks: Vec<(String, f32)>,
205}
206
207struct CachedTransactionDrawData {
208    pub draw_commands: HashMap<TransactionRef, TxDrawingCommands>,
209    pub stream_to_displayed_txs: HashMap<TransactionStreamRef, Vec<TransactionRef>>,
210    pub inc_relation_tx_ids: Vec<TransactionRef>,
211    pub out_relation_tx_ids: Vec<TransactionRef>,
212}
213
214pub struct Channels {
215    pub msg_sender: Sender<Message>,
216    pub msg_receiver: Receiver<Message>,
217    #[cfg(target_arch = "wasm32")]
218    wcp_c2s_receiver: Option<IngressReceiver<WcpCSMessage>>,
219    #[cfg(not(target_arch = "wasm32"))]
220    wcp_c2s_receiver: Option<tokio::sync::mpsc::Receiver<WcpCSMessage>>,
221    wcp_s2c_sender: Option<tokio::sync::mpsc::Sender<WcpSCMessage>>,
222}
223impl Channels {
224    fn new() -> Self {
225        let (msg_sender, msg_receiver) = mpsc::channel();
226        Self {
227            msg_sender,
228            msg_receiver,
229            wcp_c2s_receiver: None,
230            wcp_s2c_sender: None,
231        }
232    }
233}
234
235pub struct WcpClientCapabilities {
236    pub waveforms_loaded: bool,
237    pub goto_declaration: bool,
238    pub add_drivers: bool,
239    pub add_loads: bool,
240}
241impl WcpClientCapabilities {
242    fn new() -> Self {
243        Self {
244            waveforms_loaded: false,
245            goto_declaration: false,
246            add_drivers: false,
247            add_loads: false,
248        }
249    }
250}
251
252/// Stores the current canvas state to enable undo/redo operations
253struct CanvasState {
254    message: String,
255    focused_item: Option<VisibleItemIndex>,
256    focused_transaction: (Option<TransactionRef>, Option<Transaction>),
257    items_tree: DisplayedItemTree,
258    displayed_items: HashMap<DisplayedItemRef, DisplayedItem>,
259    markers: HashMap<u8, BigInt>,
260}
261
262impl SystemState {
263    pub fn update(&mut self, message: Message) -> Option<()> {
264        if log::log_enabled!(log::Level::Trace)
265            && !matches!(message, Message::CommandPromptUpdate { .. })
266        {
267            let mut s = format!("{message:?}");
268            s.shrink_to(100);
269            log::info!("{s}");
270        }
271        match message {
272            Message::SetActiveScope(scope) => {
273                let waves = self.user.waves.as_mut()?;
274                let scope = if let ScopeType::StreamScope(StreamScopeRef::Empty(name)) = scope {
275                    ScopeType::StreamScope(StreamScopeRef::new_stream_from_name(
276                        waves.inner.as_transactions().unwrap(),
277                        name,
278                    ))
279                } else {
280                    scope
281                };
282
283                if waves.inner.scope_exists(&scope) {
284                    waves.active_scope = Some(scope);
285                } else {
286                    warn!("Setting active scope to {scope} which does not exist");
287                }
288            }
289            Message::ExpandScope(scope_ref) => {
290                *self.scope_ref_to_expand.borrow_mut() = Some(scope_ref);
291            }
292            Message::AddVariables(vars) => {
293                if !vars.is_empty() {
294                    let undo_msg = if vars.len() == 1 {
295                        format!("Add variable {}", vars[0].name)
296                    } else {
297                        format!("Add {} variables", vars.len())
298                    };
299                    self.save_current_canvas(undo_msg);
300                    if let Some(waves) = self.user.waves.as_mut() {
301                        if let (Some(cmd), _) =
302                            waves.add_variables(&self.translators, vars, None, true, false)
303                        {
304                            self.load_variables(cmd);
305                        }
306                        self.invalidate_draw_commands();
307                    } else {
308                        error!("Could not load signals, no waveform loaded");
309                    }
310                }
311            }
312            Message::AddDivider(name, vidx) => {
313                self.save_current_canvas("Add divider".into());
314                let waves = self.user.waves.as_mut()?;
315                waves.add_divider(name, vidx);
316            }
317            Message::AddTimeLine(vidx) => {
318                self.save_current_canvas("Add timeline".into());
319                let waves = self.user.waves.as_mut()?;
320                waves.add_timeline(vidx);
321            }
322            Message::AddScope(scope, recursive) => {
323                self.save_current_canvas(format!("Add scope {}", scope.name()));
324
325                let vars = self.get_scope(scope, recursive);
326                let waves = self.user.waves.as_mut()?;
327
328                // TODO add parameter to add_variables, insert to (self.drag_target_idx, self.drag_source_idx)
329                if let (Some(cmd), _) =
330                    waves.add_variables(&self.translators, vars, None, true, false)
331                {
332                    self.load_variables(cmd);
333                }
334
335                self.invalidate_draw_commands();
336            }
337            Message::AddScopeAsGroup(scope, recursive) => {
338                self.save_current_canvas(format!("Add scope {} as group", scope.name()));
339
340                let waves = self.user.waves.as_mut()?;
341                let passed_or_focused = waves.focused_insert_position();
342                let target = passed_or_focused.unwrap_or_else(|| waves.end_insert_position());
343
344                self.add_scope_as_group(scope, target, recursive);
345                self.invalidate_draw_commands();
346
347                self.user.waves.as_mut()?.compute_variable_display_names();
348            }
349            Message::AddCount(digit) => {
350                if let Some(count) = &mut self.user.count {
351                    count.push(digit);
352                } else {
353                    self.user.count = Some(digit.to_string());
354                }
355            }
356            Message::AddStreamOrGenerator(s) => {
357                let undo_msg = if let Some(gen_id) = s.gen_id {
358                    format!("Add generator(id: {gen_id})")
359                } else {
360                    format!("Add stream(id: {})", s.stream_id)
361                };
362                self.save_current_canvas(undo_msg);
363
364                let waves = self.user.waves.as_mut()?;
365                if s.gen_id.is_some() {
366                    waves.add_generator(s);
367                } else {
368                    waves.add_stream(s);
369                }
370                self.invalidate_draw_commands();
371            }
372            Message::AddStreamOrGeneratorFromName(scope, name) => {
373                self.save_current_canvas(format!("Add Stream/Generator from name: {}", &name));
374                let waves = self.user.waves.as_mut()?;
375                let inner = waves.inner.as_transactions()?;
376                match scope {
377                    Some(StreamScopeRef::Root) => {
378                        let (stream_id, name) = inner
379                            .get_stream_from_name(name)
380                            .map(|s| (s.id, s.name.clone()))
381                            .unwrap();
382
383                        waves.add_stream(TransactionStreamRef::new_stream(stream_id, name));
384                    }
385                    Some(StreamScopeRef::Stream(stream)) => {
386                        let (stream_id, id, name) = inner
387                            .get_generator_from_name(Some(stream.stream_id), name)
388                            .map(|gen| (gen.stream_id, gen.id, gen.name.clone()))
389                            .unwrap();
390
391                        waves.add_generator(TransactionStreamRef::new_gen(stream_id, id, name));
392                    }
393                    Some(StreamScopeRef::Empty(_)) => {}
394                    None => {
395                        let (stream_id, id, name) = inner
396                            .get_generator_from_name(None, name)
397                            .map(|gen| (gen.stream_id, gen.id, gen.name.clone()))
398                            .unwrap();
399
400                        waves.add_generator(TransactionStreamRef::new_gen(stream_id, id, name));
401                    }
402                }
403                self.invalidate_draw_commands();
404            }
405            Message::AddAllFromStreamScope(scope_name) => {
406                self.save_current_canvas(format!("Add all from scope {}", scope_name.clone()));
407                let waves = self.user.waves.as_mut()?;
408                if scope_name == "tr" {
409                    waves.add_all_streams();
410                } else {
411                    let inner = waves.inner.as_transactions()?;
412                    let stream = inner.get_stream_from_name(scope_name)?;
413                    let gens = stream
414                        .generators
415                        .iter()
416                        .map(|gen_id| inner.get_generator(*gen_id).unwrap())
417                        .map(|gen| (gen.stream_id, gen.id, gen.name.clone()))
418                        .collect_vec();
419
420                    for (stream_id, id, name) in gens {
421                        waves.add_generator(TransactionStreamRef::new_gen(
422                            stream_id,
423                            id,
424                            name.clone(),
425                        ))
426                    }
427                }
428                self.invalidate_draw_commands();
429            }
430            Message::InvalidateCount => self.user.count = None,
431            Message::SetNameAlignRight(align_right) => {
432                self.user.align_names_right = Some(align_right);
433            }
434            Message::FocusItem(idx) => {
435                let waves = self.user.waves.as_mut()?;
436
437                let visible_items_len = waves.displayed_items.len();
438                if idx.0 < visible_items_len {
439                    waves.focused_item = Some(idx);
440                } else {
441                    error!(
442                        "Can not focus variable {} because only {visible_items_len} variables are visible.", idx.0
443                    );
444                }
445            }
446            Message::ItemSelectRange(select_to) => {
447                let waves = self.user.waves.as_mut()?;
448                let select_from = waves.focused_item?;
449                waves
450                    .items_tree
451                    .xselect_visible_range(select_from, select_to, true);
452            }
453            Message::ItemSelectAll => {
454                let waves = self.user.waves.as_mut()?;
455                waves.items_tree.xselect_all_visible(true);
456            }
457            Message::SetItemSelected(vidx, selected) => {
458                let waves = self.user.waves.as_mut()?;
459                waves.items_tree.xselect(vidx, selected);
460            }
461            Message::ToggleItemSelected(vidx) => {
462                let waves = self.user.waves.as_mut()?;
463                let node = vidx
464                    .or(waves.focused_item)
465                    .and_then(|vidx| waves.items_tree.to_displayed(vidx))
466                    .and_then(|item| waves.items_tree.get_mut(item))?;
467                node.selected = !node.selected;
468            }
469            Message::ToggleDefaultTimeline => {
470                self.user.show_default_timeline = Some(!self.show_default_timeline());
471            }
472            Message::UnfocusItem => {
473                let waves = self.user.waves.as_mut()?;
474                waves.focused_item = None;
475            }
476            Message::RenameItem(vidx) => {
477                self.save_current_canvas(format!(
478                    "Rename item to {}",
479                    self.item_renaming_string.borrow()
480                ));
481                let waves = self.user.waves.as_mut()?;
482                let vidx = vidx.or(waves.focused_item)?;
483                self.user.rename_target = Some(vidx);
484                *self.item_renaming_string.borrow_mut() = waves
485                    .items_tree
486                    .get_visible(vidx)
487                    .and_then(|node| waves.displayed_items.get(&node.item_ref))
488                    .map(displayed_item::DisplayedItem::name)
489                    .unwrap_or_default();
490            }
491            Message::MoveFocus(direction, count, select) => {
492                let waves = self.user.waves.as_mut()?;
493                let visible_item_cnt = waves.items_tree.iter_visible().count();
494                if visible_item_cnt == 0 {
495                    return None;
496                }
497
498                let new_focus_vidx = VisibleItemIndex(match direction {
499                    MoveDir::Up => waves
500                        .focused_item
501                        .map(|vidx| vidx.0)
502                        .unwrap_or(visible_item_cnt)
503                        .saturating_sub(count),
504                    MoveDir::Down => waves
505                        .focused_item
506                        .map(|vidx| vidx.0)
507                        .unwrap_or(usize::MAX)
508                        .wrapping_add(count)
509                        .clamp(0, visible_item_cnt - 1),
510                });
511
512                if select {
513                    if let Some(vidx) = waves.focused_item {
514                        waves.items_tree.xselect(vidx, true)
515                    };
516                    waves.items_tree.xselect(new_focus_vidx, true)
517                }
518                waves.focused_item = Some(new_focus_vidx);
519            }
520            Message::FocusTransaction(tx_ref, tx) => {
521                if tx_ref.is_some() && tx.is_none() {
522                    self.save_current_canvas(format!(
523                        "Focus Transaction id: {}",
524                        tx_ref.as_ref().unwrap().id
525                    ));
526                }
527                let waves = self.user.waves.as_mut()?;
528                let invalidate = tx.is_none();
529                waves.focused_transaction =
530                    (tx_ref, tx.or_else(|| waves.focused_transaction.1.clone()));
531                if invalidate {
532                    self.invalidate_draw_commands();
533                }
534            }
535            Message::ScrollToItem(position) => {
536                let waves = self.user.waves.as_mut()?;
537                waves.scroll_to_item(position);
538            }
539            Message::SetScrollOffset(offset) => {
540                let waves = self.user.waves.as_mut()?;
541                waves.scroll_offset = offset;
542            }
543            Message::SetLogsVisible(visibility) => self.user.show_logs = visibility,
544            Message::SetCursorWindowVisible(visibility) => {
545                self.user.show_cursor_window = visibility
546            }
547            Message::VerticalScroll(direction, count) => {
548                let waves = self.user.waves.as_mut()?;
549                let current_item = waves.get_top_item();
550                match direction {
551                    MoveDir::Down => {
552                        waves.scroll_to_item(current_item + count);
553                    }
554                    MoveDir::Up => {
555                        if current_item > count {
556                            waves.scroll_to_item(current_item - count);
557                        } else {
558                            waves.scroll_to_item(0);
559                        }
560                    }
561                }
562            }
563            Message::RemoveItemByIndex(vidx) => {
564                let waves = self.user.waves.as_ref();
565                let item_ref = waves
566                    .and_then(|waves| waves.items_tree.get_visible(vidx))
567                    .map(|node| node.item_ref);
568                let undo_msg = item_ref
569                    .and_then(|item_ref| {
570                        waves.and_then(|waves| waves.displayed_items.get(&item_ref))
571                    })
572                    .map(displayed_item::DisplayedItem::name)
573                    .map(|name| format!("Remove item {name}"))
574                    .unwrap_or("Remove one item".to_string());
575                self.save_current_canvas(undo_msg);
576                if let Some(waves) = self.user.waves.as_mut() {
577                    if let Some(item_ref) = item_ref {
578                        waves.remove_displayed_item(item_ref)
579                    }
580                };
581            }
582            Message::RemoveItems(items) => {
583                let undo_msg = self
584                    .user
585                    .waves
586                    .as_ref()
587                    .and_then(|waves| {
588                        if items.len() == 1 {
589                            items.first().and_then(|item_ref| {
590                                waves
591                                    .displayed_items
592                                    .get(item_ref)
593                                    .map(|item| format!("Remove item {}", item.name()))
594                            })
595                        } else {
596                            Some(format!("Remove {} items", items.len()))
597                        }
598                    })
599                    .unwrap_or("".to_string());
600                self.save_current_canvas(undo_msg);
601
602                let waves = self.user.waves.as_mut()?;
603                for id in items.iter().sorted_unstable_by(|a, b| Ord::cmp(b, a)) {
604                    waves.remove_displayed_item(*id);
605                }
606            }
607            Message::MoveFocusedItem(direction, count) => {
608                self.save_current_canvas(format!("Move item {direction}, {count}"));
609                self.invalidate_draw_commands();
610                let waves = self.user.waves.as_mut()?;
611                let mut vidx = waves.focused_item?;
612                for _ in 0..count {
613                    vidx = waves
614                        .items_tree
615                        .move_item(vidx, direction, |node| {
616                            matches!(
617                                waves.displayed_items.get(&node.item_ref),
618                                Some(DisplayedItem::Group(..))
619                            )
620                        })
621                        .expect("move failed for unknown reason");
622                }
623                waves.focused_item = waves.focused_item.and(Some(vidx));
624            }
625            Message::CanvasScroll {
626                delta,
627                viewport_idx,
628            } => {
629                let waves = self.user.waves.as_mut()?;
630                waves.viewports[viewport_idx].handle_canvas_scroll(delta.y as f64 + delta.x as f64);
631                self.invalidate_draw_commands();
632            }
633            Message::CanvasZoom {
634                delta,
635                mouse_ptr,
636                viewport_idx,
637            } => {
638                let waves = self.user.waves.as_mut()?;
639                if let Some(num_timestamps) = waves.num_timestamps() {
640                    waves.viewports[viewport_idx].handle_canvas_zoom(
641                        mouse_ptr,
642                        delta as f64,
643                        &num_timestamps,
644                    );
645                    self.invalidate_draw_commands();
646                }
647            }
648            Message::ZoomToFit { viewport_idx } => {
649                let waves = self.user.waves.as_mut()?;
650                waves.viewports[viewport_idx].zoom_to_fit();
651                self.invalidate_draw_commands();
652            }
653            Message::GoToEnd { viewport_idx } => {
654                let waves = self.user.waves.as_mut()?;
655                waves.viewports[viewport_idx].go_to_end();
656                self.invalidate_draw_commands();
657            }
658            Message::GoToStart { viewport_idx } => {
659                let waves = self.user.waves.as_mut()?;
660                waves.viewports[viewport_idx].go_to_start();
661                self.invalidate_draw_commands();
662            }
663            Message::GoToTime(time, viewport_idx) => {
664                let waves = self.user.waves.as_mut()?;
665                let time = time?;
666                let num_timestamps = waves
667                    .num_timestamps()
668                    .expect("No timestamps count, even though waveforms should be loaded");
669                waves.viewports[viewport_idx].go_to_time(&time.clone(), &num_timestamps);
670                self.invalidate_draw_commands();
671            }
672            Message::SetTimeUnit(timeunit) => {
673                self.user.wanted_timeunit = timeunit;
674                self.invalidate_draw_commands();
675            }
676            Message::SetTimeStringFormatting(format) => {
677                self.user.time_string_format = format;
678                self.invalidate_draw_commands();
679            }
680            Message::ZoomToRange {
681                start,
682                end,
683                viewport_idx,
684            } => {
685                let waves = self.user.waves.as_mut()?;
686                let num_timestamps = waves
687                    .num_timestamps()
688                    .expect("No timestamps count, even though waveforms should be loaded");
689                waves.viewports[viewport_idx].zoom_to_range(&start, &end, &num_timestamps);
690                self.invalidate_draw_commands();
691            }
692            Message::VariableFormatChange(displayed_field_ref, format) => {
693                let waves = self.user.waves.as_mut()?;
694                if !self
695                    .translators
696                    .all_translator_names()
697                    .contains(&format.as_str())
698                {
699                    warn!("No translator {format}");
700                    return None;
701                }
702
703                let update_format =
704                    |variable: &mut DisplayedVariable, field_ref: DisplayedFieldRef| {
705                        if field_ref.field.is_empty() {
706                            let Ok(meta) = waves
707                                .inner
708                                .as_waves()
709                                .unwrap()
710                                .variable_meta(&variable.variable_ref)
711                                .map_err(|e| {
712                                    warn!("Error trying to get variable metadata: {e:#?}")
713                                })
714                            else {
715                                return;
716                            };
717                            let translator = self.translators.get_translator(&format);
718                            let new_info = translator.variable_info(&meta).unwrap();
719
720                            variable.format = Some(format.clone());
721                            variable.info = new_info;
722                        } else {
723                            variable
724                                .field_formats
725                                .retain(|ff| ff.field != field_ref.field);
726                            variable.field_formats.push(FieldFormat {
727                                field: field_ref.field,
728                                format: format.clone(),
729                            });
730                        }
731                    };
732
733                // convert focused item index to item ref
734                let focused = waves
735                    .focused_item
736                    .and_then(|vidx| waves.items_tree.get_visible(vidx))
737                    .map(|node| node.item_ref);
738
739                let mut redraw = false;
740
741                match displayed_field_ref {
742                    MessageTarget::Explicit(field_ref) => {
743                        if let Some(DisplayedItem::Variable(displayed_variable)) =
744                            waves.displayed_items.get_mut(&field_ref.item)
745                        {
746                            update_format(displayed_variable, field_ref);
747                            redraw = true;
748                        }
749                    }
750                    MessageTarget::CurrentSelection => {
751                        //If an item is focused, update its format too
752                        if let Some(focused) = focused {
753                            if let Some(DisplayedItem::Variable(displayed_variable)) =
754                                waves.displayed_items.get_mut(&focused)
755                            {
756                                update_format(displayed_variable, DisplayedFieldRef::from(focused));
757                                redraw = true;
758                            }
759                        }
760                        for item in waves
761                            .items_tree
762                            .iter_visible_selected()
763                            .map(|node| node.item_ref)
764                        {
765                            //Update format for all selected
766                            let field_ref = DisplayedFieldRef::from(item);
767                            if let Some(DisplayedItem::Variable(variable)) =
768                                waves.displayed_items.get_mut(&item)
769                            {
770                                update_format(variable, field_ref);
771                            }
772                            redraw = true;
773                        }
774                    }
775                }
776
777                if redraw {
778                    self.invalidate_draw_commands();
779                }
780            }
781            Message::ItemSelectionClear => {
782                let waves = self.user.waves.as_mut()?;
783                waves.items_tree.xselect_all_visible(false);
784            }
785            Message::ItemColorChange(vidx, color_name) => {
786                self.save_current_canvas(format!(
787                    "Change item color to {}",
788                    color_name.clone().unwrap_or("default".into())
789                ));
790                self.invalidate_draw_commands();
791                let waves = self.user.waves.as_mut()?;
792
793                match vidx {
794                    MessageTarget::Explicit(vidx) => {
795                        let node = waves.items_tree.get_visible(vidx)?;
796                        waves
797                            .displayed_items
798                            .entry(node.item_ref)
799                            .and_modify(|item| item.set_color(color_name.clone()));
800                    }
801                    MessageTarget::CurrentSelection => {
802                        if let Some(focused) = waves.focused_item {
803                            let node = waves.items_tree.get_visible(focused)?;
804                            waves
805                                .displayed_items
806                                .entry(node.item_ref)
807                                .and_modify(|item| item.set_color(color_name.clone()));
808                        }
809
810                        for node in waves.items_tree.iter_visible_selected() {
811                            waves
812                                .displayed_items
813                                .entry(node.item_ref)
814                                .and_modify(|item| item.set_color(color_name.clone()));
815                        }
816                    }
817                }
818            }
819            Message::ItemNameChange(vidx, name) => {
820                self.save_current_canvas(format!(
821                    "Change item name to {}",
822                    name.clone().unwrap_or("default".into())
823                ));
824                let waves = self.user.waves.as_mut()?;
825                let vidx = vidx.or(waves.focused_item)?;
826                let node = waves.items_tree.get_visible(vidx)?;
827                waves
828                    .displayed_items
829                    .entry(node.item_ref)
830                    .and_modify(|item| item.set_name(name));
831            }
832            Message::ItemBackgroundColorChange(vidx, color_name) => {
833                self.save_current_canvas(format!(
834                    "Change item background color to {}",
835                    color_name.clone().unwrap_or("default".into())
836                ));
837                let waves = self.user.waves.as_mut()?;
838
839                match vidx {
840                    MessageTarget::Explicit(vidx) => {
841                        let node = waves.items_tree.get_visible(vidx)?;
842                        waves
843                            .displayed_items
844                            .entry(node.item_ref)
845                            .and_modify(|item| item.set_background_color(color_name.clone()));
846                    }
847                    MessageTarget::CurrentSelection => {
848                        if let Some(focused) = waves.focused_item {
849                            let node = waves.items_tree.get_visible(focused)?;
850                            waves
851                                .displayed_items
852                                .entry(node.item_ref)
853                                .and_modify(|item| item.set_background_color(color_name.clone()));
854                        }
855
856                        for node in waves.items_tree.iter_visible_selected() {
857                            waves
858                                .displayed_items
859                                .entry(node.item_ref)
860                                .and_modify(|item| item.set_background_color(color_name.clone()));
861                        }
862                    }
863                }
864            }
865            Message::ItemHeightScalingFactorChange(vidx, scale) => {
866                self.save_current_canvas(format!("Change item height scaling factor to {scale}"));
867                let waves = self.user.waves.as_mut()?;
868                let vidx = match vidx {
869                    MessageTarget::Explicit(vidx) => vidx,
870                    MessageTarget::CurrentSelection => waves.focused_item?,
871                };
872                let node = waves.items_tree.get_visible(vidx)?;
873                waves
874                    .displayed_items
875                    .entry(node.item_ref)
876                    .and_modify(|item| item.set_height_scaling_factor(scale));
877            }
878            Message::MoveCursorToTransition {
879                next,
880                variable,
881                skip_zero,
882            } => {
883                let waves = self.user.waves.as_mut()?;
884                // if no cursor is set, move it to
885                // start of visible area transition for next transition
886                // end of visible area for previous transition
887                if waves.cursor.is_none() && waves.focused_item.is_some() {
888                    if let Some(vp) = waves.viewports.first() {
889                        let num_timestamps = waves
890                            .num_timestamps()
891                            .expect("No timestamps count, even though waveforms should be loaded");
892                        waves.cursor = if next {
893                            Some(vp.left_edge_time(&num_timestamps))
894                        } else {
895                            Some(vp.right_edge_time(&num_timestamps))
896                        };
897                    }
898                }
899                waves.set_cursor_at_transition(next, variable, skip_zero);
900                let moved = waves.go_to_cursor_if_not_in_view();
901                if moved {
902                    self.invalidate_draw_commands();
903                }
904            }
905            Message::MoveTransaction { next } => {
906                let undo_msg = if next {
907                    "Move to next transaction"
908                } else {
909                    "Move to previous transaction"
910                };
911                self.save_current_canvas(undo_msg.to_string());
912                let waves = self.user.waves.as_mut()?;
913                let inner = waves.inner.as_transactions()?;
914                let mut transactions = waves
915                    .items_tree
916                    .iter_visible()
917                    .flat_map(|node| {
918                        let item = &waves.displayed_items[&node.item_ref];
919                        match item {
920                            DisplayedItem::Stream(s) => {
921                                let stream_ref = &s.transaction_stream_ref;
922                                let stream_id = stream_ref.stream_id;
923                                if let Some(gen_id) = stream_ref.gen_id {
924                                    inner.get_transactions_from_generator(gen_id)
925                                } else {
926                                    inner.get_transactions_from_stream(stream_id)
927                                }
928                            }
929                            _ => vec![],
930                        }
931                    })
932                    .collect_vec();
933
934                transactions.sort();
935                let tx = if let Some(focused_tx) = &waves.focused_transaction.0 {
936                    let next_id = transactions
937                        .iter()
938                        .enumerate()
939                        .find(|(_, tx)| **tx == focused_tx.id)
940                        .map(|(vec_idx, _)| {
941                            if next {
942                                if vec_idx + 1 < transactions.len() {
943                                    vec_idx + 1
944                                } else {
945                                    transactions.len() - 1
946                                }
947                            } else if vec_idx as i32 - 1 > 0 {
948                                vec_idx - 1
949                            } else {
950                                0
951                            }
952                        })
953                        .unwrap_or(if next { transactions.len() - 1 } else { 0 });
954                    Some(TransactionRef {
955                        id: *transactions.get(next_id).unwrap(),
956                    })
957                } else if !transactions.is_empty() {
958                    Some(TransactionRef {
959                        id: *transactions.first().unwrap(),
960                    })
961                } else {
962                    None
963                };
964                waves.focused_transaction = (tx, waves.focused_transaction.1.clone());
965
966                self.invalidate_draw_commands();
967            }
968            Message::ResetVariableFormat(displayed_field_ref) => {
969                let waves = self.user.waves.as_mut()?;
970                if let Some(DisplayedItem::Variable(displayed_variable)) =
971                    waves.displayed_items.get_mut(&displayed_field_ref.item)
972                {
973                    if displayed_field_ref.field.is_empty() {
974                        displayed_variable.format = None;
975                    } else {
976                        displayed_variable
977                            .field_formats
978                            .retain(|ff| ff.field != displayed_field_ref.field);
979                    }
980                    self.invalidate_draw_commands();
981                }
982            }
983            Message::CursorSet(time) => {
984                let waves = self.user.waves.as_mut()?;
985                waves.cursor = Some(time);
986            }
987            Message::ExpandParameterSection => {
988                self.expand_parameter_section = true;
989            }
990            Message::LoadFile(filename, load_options) => {
991                #[cfg(not(target_arch = "wasm32"))]
992                self.load_from_file(filename, load_options).ok();
993                #[cfg(target_arch = "wasm32")]
994                error!("Cannot load file from path in WASM");
995            }
996            Message::LoadWaveformFileFromUrl(url, load_options) => {
997                self.load_wave_from_url(url, load_options);
998            }
999            Message::LoadFromData(data, load_options) => {
1000                self.load_from_data(data, load_options).ok();
1001            }
1002            #[cfg(feature = "python")]
1003            Message::LoadPythonTranslator(filename) => {
1004                try_log_error!(
1005                    self.translators.load_python_translator(filename),
1006                    "Error loading Python translator",
1007                )
1008            }
1009            #[cfg(all(not(target_arch = "wasm32"), feature = "wasm_plugins"))]
1010            Message::LoadWasmTranslator(path) => {
1011                let sender = self.channels.msg_sender.clone();
1012                perform_work(
1013                    move || match PluginTranslator::new(path.into_std_path_buf()) {
1014                        Ok(t) => sender.send(Message::TranslatorLoaded(Box::new(t))).unwrap(),
1015                        Err(e) => {
1016                            error!("Failed to load wasm translator {e:#}")
1017                        }
1018                    },
1019                )
1020            }
1021            Message::LoadCommandFile(path) => {
1022                self.add_batch_commands(read_command_file(&path));
1023            }
1024            Message::LoadCommandFileFromUrl(url) => {
1025                self.load_commands_from_url(url);
1026            }
1027            Message::LoadCommandFromData(bytes) => {
1028                self.add_batch_commands(read_command_bytes(bytes));
1029            }
1030            Message::SetupCxxrtl(kind) => self.connect_to_cxxrtl(kind, false),
1031            Message::SurferServerStatus(_start, server, status) => {
1032                self.server_status_to_progress(server, status);
1033            }
1034            Message::FileDropped(dropped_file) => {
1035                self.load_from_dropped(dropped_file)
1036                    .map_err(|e| error!("{e:#?}"))
1037                    .ok();
1038            }
1039            Message::WaveHeaderLoaded(start, source, load_options, header) => {
1040                // for files using the `wellen` backend, we load the header before parsing the body
1041                info!(
1042                    "Loaded the hierarchy and meta-data of {source} in {:?}",
1043                    start.elapsed()
1044                );
1045                match header {
1046                    HeaderResult::LocalFile(header) => {
1047                        // register waveform as loaded (but with no variable info yet!)
1048                        let shared_hierarchy = Arc::new(header.hierarchy);
1049                        let new_waves =
1050                            Box::new(WaveContainer::new_waveform(shared_hierarchy.clone()));
1051                        self.on_waves_loaded(
1052                            source.clone(),
1053                            convert_format(header.file_format),
1054                            new_waves,
1055                            load_options,
1056                        );
1057                        // start parsing of the body
1058                        self.load_wave_body(source, header.body, header.body_len, shared_hierarchy);
1059                    }
1060                    HeaderResult::LocalBytes(header) => {
1061                        // register waveform as loaded (but with no variable info yet!)
1062                        let shared_hierarchy = Arc::new(header.hierarchy);
1063                        let new_waves =
1064                            Box::new(WaveContainer::new_waveform(shared_hierarchy.clone()));
1065                        self.on_waves_loaded(
1066                            source.clone(),
1067                            convert_format(header.file_format),
1068                            new_waves,
1069                            load_options,
1070                        );
1071                        // start parsing of the body
1072                        self.load_wave_body(source, header.body, header.body_len, shared_hierarchy);
1073                    }
1074                    HeaderResult::Remote(hierarchy, file_format, server) => {
1075                        // register waveform as loaded (but with no variable info yet!)
1076                        let new_waves = Box::new(WaveContainer::new_remote_waveform(
1077                            server.clone(),
1078                            hierarchy.clone(),
1079                        ));
1080                        self.on_waves_loaded(
1081                            source.clone(),
1082                            convert_format(file_format),
1083                            new_waves,
1084                            load_options,
1085                        );
1086                        // body is already being parsed on the server, we need to request the time table though
1087                        Self::get_time_table_from_server(self.channels.msg_sender.clone(), server);
1088                    }
1089                }
1090            }
1091            Message::WaveBodyLoaded(start, source, body) => {
1092                // for files using the `wellen` backend, parse the body in a second step
1093                info!("Loaded the body of {source} in {:?}", start.elapsed());
1094                self.progress_tracker = None;
1095                let waves = self
1096                    .user
1097                    .waves
1098                    .as_mut()
1099                    .expect("Waves should be loaded at this point!");
1100                // add source and time table
1101                let maybe_cmd = waves // TODO
1102                    .inner
1103                    .as_waves_mut()
1104                    .unwrap()
1105                    .wellen_add_body(body)
1106                    .map_err(|err| {
1107                        error!("While getting commands to lazy-load signals: {err:?}");
1108                    })
1109                    .ok()
1110                    .flatten();
1111                // Pre-load parameters
1112                let param_cmd = waves
1113                    .inner
1114                    .as_waves_mut()
1115                    .unwrap()
1116                    .load_parameters()
1117                    .map_err(|err| {
1118                        error!("While getting commands to lazy-load parameters: {err:?}");
1119                    })
1120                    .ok()
1121                    .flatten();
1122
1123                if self.wcp_greeted_signal.load(Ordering::Relaxed)
1124                    && self.wcp_client_capabilities.waveforms_loaded
1125                {
1126                    let source = match source {
1127                        WaveSource::File(path) => path.to_string(),
1128                        WaveSource::Url(url) => url,
1129                        _ => "".to_string(),
1130                    };
1131                    self.channels.wcp_s2c_sender.as_ref().map(|ch| {
1132                        block_on(
1133                            ch.send(WcpSCMessage::event(WcpEvent::waveforms_loaded { source })),
1134                        )
1135                    });
1136                }
1137
1138                // update viewports, now that we have the time table
1139                waves.update_viewports();
1140                // make sure we redraw
1141                self.invalidate_draw_commands();
1142                // start loading parameters
1143                if let Some(cmd) = param_cmd {
1144                    self.load_variables(cmd);
1145                }
1146                // start loading variables
1147                if let Some(cmd) = maybe_cmd {
1148                    self.load_variables(cmd);
1149                }
1150            }
1151            Message::SignalsLoaded(start, res) => {
1152                info!("Loaded {} variables in {:?}", res.len(), start.elapsed());
1153                self.progress_tracker = None;
1154                let waves = self
1155                    .user
1156                    .waves
1157                    .as_mut()
1158                    .expect("Waves should be loaded at this point!");
1159                match waves.inner.as_waves_mut().unwrap().on_signals_loaded(res) {
1160                    Err(err) => error!("{err:?}"),
1161                    Ok(Some(cmd)) => self.load_variables(cmd),
1162                    _ => {}
1163                }
1164                // make sure we redraw since now more variable data is available
1165                self.invalidate_draw_commands();
1166            }
1167            Message::WavesLoaded(filename, format, new_waves, load_options) => {
1168                self.on_waves_loaded(filename, format, new_waves, load_options);
1169                // here, the body and thus the number of timestamps is already loaded!
1170                self.user
1171                    .waves
1172                    .as_mut()
1173                    .expect("Waves should be loaded at this point!")
1174                    .update_viewports();
1175                self.progress_tracker = None;
1176            }
1177            Message::TransactionStreamsLoaded(filename, format, new_ftr, loaded_options) => {
1178                self.on_transaction_streams_loaded(filename, format, new_ftr, loaded_options);
1179                self.user
1180                    .waves
1181                    .as_mut()
1182                    .expect("Waves should be loaded at this point!")
1183                    .update_viewports();
1184            }
1185            Message::BlacklistTranslator(idx, translator) => {
1186                self.user.blacklisted_translators.insert((idx, translator));
1187            }
1188            Message::Error(e) => {
1189                error!("{e:?}");
1190                self.user.show_logs = true;
1191            }
1192            Message::TranslatorLoaded(t) => {
1193                info!("Translator {} loaded", t.name());
1194                t.set_wave_source(
1195                    self.user
1196                        .waves
1197                        .as_ref()
1198                        .map(|waves| waves.source.into_translation_type()),
1199                );
1200
1201                self.translators.add_or_replace(AnyTranslator::Full(t));
1202            }
1203            Message::ToggleSidePanel => self.user.show_hierarchy = Some(!self.show_hierarchy()),
1204            Message::ToggleMenu => self.user.show_menu = Some(!self.show_menu()),
1205            Message::ToggleToolbar => self.user.show_toolbar = Some(!self.show_toolbar()),
1206            Message::ToggleEmptyScopes => {
1207                self.user.show_empty_scopes = Some(!self.show_empty_scopes())
1208            }
1209            Message::ToggleParametersInScopes => {
1210                self.user.show_parameters_in_scopes = Some(!self.show_parameters_in_scopes())
1211            }
1212            Message::ToggleStatusbar => self.user.show_statusbar = Some(!self.show_statusbar()),
1213            Message::ToggleTickLines => self.user.show_ticks = Some(!self.show_ticks()),
1214            Message::ToggleVariableTooltip => self.user.show_tooltip = Some(self.show_tooltip()),
1215            Message::ToggleScopeTooltip => {
1216                self.user.show_scope_tooltip = Some(!self.show_scope_tooltip())
1217            }
1218            Message::ToggleOverview => self.user.show_overview = Some(!self.show_overview()),
1219            Message::ToggleDirection => {
1220                self.user.show_variable_direction = Some(!self.show_variable_direction())
1221            }
1222            Message::ToggleIndices => {
1223                let new = !self.show_variable_indices();
1224                self.user.show_variable_indices = Some(new);
1225                let waves = self.user.waves.as_mut()?;
1226                waves.display_variable_indices = new;
1227                waves.compute_variable_display_names();
1228            }
1229            Message::SetHighlightFocused(highlight) => {
1230                self.user.highlight_focused = Some(highlight);
1231            }
1232            Message::ShowCommandPrompt(text) => {
1233                if let Some(init_text) = text {
1234                    self.command_prompt.new_cursor_pos = Some(init_text.len());
1235                    *self.command_prompt_text.borrow_mut() = init_text;
1236                    self.command_prompt.visible = true;
1237                } else {
1238                    *self.command_prompt_text.borrow_mut() = "".to_string();
1239                    self.command_prompt.suggestions = vec![];
1240                    self.command_prompt.selected = self.command_prompt.previous_commands.len();
1241                    self.command_prompt.visible = false;
1242                }
1243            }
1244            Message::FileDownloaded(url, bytes, load_options) => {
1245                self.load_from_bytes(WaveSource::Url(url), bytes.to_vec(), load_options)
1246            }
1247            Message::CommandFileDownloaded(_url, bytes) => {
1248                self.add_batch_commands(read_command_bytes(bytes.to_vec()));
1249                self.progress_tracker = None;
1250            }
1251            Message::SetConfigFromString(s) => {
1252                // FIXME think about a structured way to collect errors
1253                let config = SurferConfig::new_from_toml(&s)
1254                    .with_context(|| "Failed to load config file")
1255                    .ok()?;
1256
1257                self.user.config = config;
1258
1259                let ctx = &self.context.as_ref()?;
1260                ctx.set_visuals(self.get_visuals())
1261            }
1262            Message::ReloadConfig => {
1263                // FIXME think about a structured way to collect errors
1264                let config = SurferConfig::new(false)
1265                    .with_context(|| "Failed to load config file")
1266                    .ok()?;
1267                self.translators = all_translators();
1268                self.user.config = config;
1269
1270                let ctx = &self.context.as_ref()?;
1271                ctx.set_visuals(self.get_visuals());
1272            }
1273            Message::ReloadWaveform(keep_unavailable) => {
1274                let waves = self.user.waves.as_ref()?;
1275                let options = LoadOptions {
1276                    keep_variables: true,
1277                    keep_unavailable,
1278                };
1279                match &waves.source {
1280                    WaveSource::File(filename) => {
1281                        self.load_from_file(filename.clone(), options).ok();
1282                    }
1283                    WaveSource::Data => {}       // can't reload
1284                    WaveSource::Cxxrtl(..) => {} // can't reload
1285                    WaveSource::DragAndDrop(filename) => {
1286                        filename
1287                            .clone()
1288                            .and_then(|filename| self.load_from_file(filename, options).ok());
1289                    }
1290                    WaveSource::Url(url) => {
1291                        self.load_wave_from_url(url.clone(), options);
1292                    }
1293                };
1294
1295                for translator in self.translators.all_translators() {
1296                    translator.reload(self.channels.msg_sender.clone());
1297                }
1298                self.variable_name_info_cache.borrow_mut().clear();
1299            }
1300            Message::SuggestReloadWaveform => match self.autoreload_files() {
1301                AutoLoad::Always => self.update(Message::ReloadWaveform(true))?,
1302                AutoLoad::Never => (),
1303                AutoLoad::Ask => {
1304                    self.user.show_reload_suggestion = Some(ReloadWaveformDialog::default())
1305                }
1306            },
1307            Message::CloseReloadWaveformDialog {
1308                reload_file,
1309                do_not_show_again,
1310            } => {
1311                if do_not_show_again {
1312                    // FIXME: This is currently saved in state, but could be persisted in
1313                    // some setting.
1314                    self.user.autoreload_files = Some(AutoLoad::from_bool(reload_file));
1315                }
1316                self.user.show_reload_suggestion = None;
1317                if reload_file {
1318                    self.update(Message::ReloadWaveform(true));
1319                }
1320            }
1321            Message::UpdateReloadWaveformDialog(dialog) => {
1322                self.user.show_reload_suggestion = Some(dialog);
1323            }
1324            Message::OpenSiblingStateFile(open) => {
1325                if !open {
1326                    return None;
1327                }
1328                let waves = self.user.waves.as_ref()?;
1329                let state_file_path = waves.source.sibling_state_file()?;
1330                self.load_state_file(Some(state_file_path.clone().into_std_path_buf()));
1331            }
1332            Message::SuggestOpenSiblingStateFile => match self.autoload_sibling_state_files() {
1333                AutoLoad::Always => {
1334                    self.update(Message::OpenSiblingStateFile(true));
1335                }
1336                AutoLoad::Never => {}
1337                AutoLoad::Ask => {
1338                    self.user.show_open_sibling_state_file_suggestion =
1339                        Some(OpenSiblingStateFileDialog::default())
1340                }
1341            },
1342            Message::CloseOpenSiblingStateFileDialog {
1343                load_state,
1344                do_not_show_again,
1345            } => {
1346                if do_not_show_again {
1347                    self.user.autoload_sibling_state_files = Some(AutoLoad::from_bool(load_state));
1348                }
1349                self.user.show_open_sibling_state_file_suggestion = None;
1350                if load_state {
1351                    self.update(Message::OpenSiblingStateFile(true));
1352                }
1353            }
1354            Message::UpdateOpenSiblingStateFileDialog(dialog) => {
1355                self.user.show_open_sibling_state_file_suggestion = Some(dialog);
1356            }
1357            Message::RemovePlaceholders => {
1358                let waves = self.user.waves.as_mut()?;
1359                waves.remove_placeholders();
1360            }
1361            Message::SetClockHighlightType(new_type) => {
1362                self.user.clock_highlight_type = Some(new_type);
1363            }
1364            Message::SetFillHighValues(fill) => self.user.fill_high_values = Some(fill),
1365            Message::AddMarker {
1366                time,
1367                name,
1368                move_focus,
1369            } => {
1370                if let Some(name) = &name {
1371                    self.save_current_canvas(format!("Add marker {name} at {time}"));
1372                } else {
1373                    self.save_current_canvas(format!("Add marker at {time}"));
1374                }
1375                let waves = self.user.waves.as_mut()?;
1376                waves.add_marker(&time, name, move_focus);
1377            }
1378            Message::SetMarker { id, time } => {
1379                self.save_current_canvas(format!("Set marker {id} to {time}"));
1380                let waves = self.user.waves.as_mut()?;
1381                waves.set_marker_position(id, &time);
1382            }
1383            Message::RemoveMarker(id) => {
1384                let waves = self.user.waves.as_mut()?;
1385                waves.remove_marker(id);
1386            }
1387            Message::MoveMarkerToCursor(idx) => {
1388                self.save_current_canvas("Move marker".into());
1389                let waves = self.user.waves.as_mut()?;
1390                waves.move_marker_to_cursor(idx);
1391            }
1392            Message::GoToCursorIfNotInView => {
1393                let waves = self.user.waves.as_mut()?;
1394                if waves.go_to_cursor_if_not_in_view() {
1395                    self.invalidate_draw_commands();
1396                }
1397            }
1398            Message::GoToMarkerPosition(idx, viewport_idx) => {
1399                let waves = self.user.waves.as_mut()?;
1400                let cursor = waves.markers.get(&idx)?;
1401                let num_timestamps = waves
1402                    .num_timestamps()
1403                    .expect("No timestamps count, even though waveforms should be loaded");
1404                waves.viewports[viewport_idx].go_to_time(cursor, &num_timestamps);
1405                self.invalidate_draw_commands();
1406            }
1407            Message::ChangeVariableNameType(vidx, name_type) => {
1408                let waves = self.user.waves.as_mut()?;
1409                // checks if vidx is Some then use that, else try focused variable
1410                let vidx = match vidx {
1411                    MessageTarget::Explicit(vidx) => vidx,
1412                    MessageTarget::CurrentSelection => waves.focused_item?,
1413                };
1414                let item_ref = waves
1415                    .items_tree
1416                    .get_visible(vidx)
1417                    .map(|node| node.item_ref)?;
1418
1419                let mut recompute_names = false;
1420                waves.displayed_items.entry(item_ref).and_modify(|item| {
1421                    if let DisplayedItem::Variable(variable) = item {
1422                        variable.display_name_type = name_type;
1423                        recompute_names = true;
1424                    }
1425                });
1426                if recompute_names {
1427                    waves.compute_variable_display_names();
1428                }
1429            }
1430            Message::ForceVariableNameTypes(name_type) => {
1431                let waves = self.user.waves.as_mut()?;
1432                waves.force_variable_name_type(name_type);
1433            }
1434            Message::CommandPromptClear => {
1435                *self.command_prompt_text.borrow_mut() = String::new();
1436                self.command_prompt.suggestions = vec![];
1437                // self.command_prompt.selected = self.command_prompt.previous_commands.len();
1438                self.command_prompt.selected = if self.command_prompt_text.borrow().is_empty() {
1439                    self.command_prompt.previous_commands.len().clamp(0, 3)
1440                } else {
1441                    0
1442                };
1443            }
1444            Message::CommandPromptUpdate { suggestions } => {
1445                self.command_prompt.suggestions = suggestions;
1446                self.command_prompt.selected = if self.command_prompt_text.borrow().is_empty() {
1447                    self.command_prompt.previous_commands.len().clamp(0, 3)
1448                } else {
1449                    0
1450                };
1451                self.command_prompt.new_selection =
1452                    Some(if self.command_prompt_text.borrow().is_empty() {
1453                        self.command_prompt.previous_commands.len().clamp(0, 3)
1454                    } else {
1455                        0
1456                    });
1457            }
1458            Message::CommandPromptPushPrevious(cmd) => {
1459                let len = cmd.len();
1460                self.command_prompt
1461                    .previous_commands
1462                    .insert(0, (cmd, vec![false; len]));
1463            }
1464            Message::OpenFileDialog(mode) => {
1465                self.open_file_dialog(mode);
1466            }
1467            Message::OpenCommandFileDialog => {
1468                self.open_command_file_dialog();
1469            }
1470            #[cfg(feature = "python")]
1471            Message::OpenPythonPluginDialog => {
1472                self.open_python_file_dialog();
1473            }
1474            #[cfg(feature = "python")]
1475            Message::ReloadPythonPlugin => {
1476                try_log_error!(
1477                    self.translators.reload_python_translator(),
1478                    "Error reloading Python translator"
1479                );
1480                self.invalidate_draw_commands();
1481            }
1482            Message::SaveStateFile(path) => self.save_state_file(path),
1483            Message::LoadStateFile(path) => self.load_state_file(path),
1484            Message::LoadState(state, path) => self.load_state(state, path),
1485            Message::SetStateFile(path) => {
1486                // since in wasm we can't support "save", only "save as" - never set the `state_file`
1487                #[cfg(not(target_arch = "wasm32"))]
1488                {
1489                    self.user.state_file = Some(path);
1490                }
1491                #[cfg(target_arch = "wasm32")]
1492                {
1493                    error!("Failed to load {path:?}. Loading state files is unsupported on wasm")
1494                }
1495            }
1496            Message::SetAboutVisible(s) => self.user.show_about = s,
1497            Message::SetKeyHelpVisible(s) => self.user.show_keys = s,
1498            Message::SetGestureHelpVisible(s) => self.user.show_gestures = s,
1499            Message::SetUrlEntryVisible(s, f) => {
1500                self.user.show_url_entry = s;
1501                self.url_callback = f;
1502            }
1503            Message::SetLicenseVisible(s) => self.user.show_license = s,
1504            Message::SetQuickStartVisible(s) => self.user.show_quick_start = s,
1505            Message::SetRenameItemVisible(_) => self.user.rename_target = None,
1506            Message::SetPerformanceVisible(s) => {
1507                if !s {
1508                    self.continuous_redraw = false;
1509                }
1510                self.user.show_performance = s;
1511            }
1512            Message::SetContinuousRedraw(s) => self.continuous_redraw = s,
1513            Message::SetMouseGestureDragStart(pos) => self.gesture_start_location = pos,
1514            Message::SetMeasureDragStart(pos) => self.measure_start_location = pos,
1515            Message::SetFilterFocused(s) => self.user.variable_name_filter_focused = s,
1516            Message::SetVariableNameFilterType(variable_name_filter_type) => {
1517                self.user.variable_filter.name_filter_type = variable_name_filter_type;
1518            }
1519            Message::SetVariableNameFilterCaseInsensitive(s) => {
1520                self.user.variable_filter.name_filter_case_insensitive = s;
1521            }
1522            Message::SetVariableIOFilter(t, b) => {
1523                match t {
1524                    VariableIOFilterType::Output => self.user.variable_filter.include_outputs = b,
1525                    VariableIOFilterType::Input => self.user.variable_filter.include_inputs = b,
1526                    VariableIOFilterType::InOut => self.user.variable_filter.include_inouts = b,
1527                    VariableIOFilterType::Other => self.user.variable_filter.include_others = b,
1528                };
1529            }
1530            Message::SetVariableGroupByDirection(b) => {
1531                self.user.variable_filter.group_by_direction = b;
1532            }
1533            Message::SetUIZoomFactor(scale) => {
1534                if let Some(ctx) = &mut self.context.as_ref() {
1535                    ctx.set_zoom_factor(scale);
1536                }
1537                self.user.ui_zoom_factor = Some(scale);
1538            }
1539            Message::SelectPrevCommand => {
1540                self.command_prompt.new_selection = Some(
1541                    self.command_prompt
1542                        .new_selection
1543                        .unwrap_or(self.command_prompt.selected)
1544                        .saturating_sub(1)
1545                        .max(0),
1546                );
1547            }
1548            Message::SelectNextCommand => {
1549                self.command_prompt.new_selection = Some(
1550                    self.command_prompt
1551                        .new_selection
1552                        .unwrap_or(self.command_prompt.selected)
1553                        .saturating_add(1)
1554                        .min(self.command_prompt.suggestions.len().saturating_sub(1)),
1555                );
1556            }
1557            Message::SetHierarchyStyle(style) => self.user.hierarchy_style = Some(style),
1558            Message::SetArrowKeyBindings(bindings) => {
1559                self.user.arrow_key_bindings = Some(bindings);
1560            }
1561            Message::SetPrimaryMouseDragBehavior(behavior) => {
1562                self.user.primary_button_drag_behavior = Some(behavior);
1563            }
1564            Message::InvalidateDrawCommands => self.invalidate_draw_commands(),
1565            Message::UnpauseSimulation => {
1566                let waves = self.user.waves.as_ref()?;
1567                waves.inner.as_waves().unwrap().unpause_simulation();
1568            }
1569            Message::PauseSimulation => {
1570                let waves = self.user.waves.as_ref()?;
1571                waves.inner.as_waves().unwrap().pause_simulation();
1572            }
1573            Message::Batch(messages) => {
1574                for message in messages {
1575                    self.update(message);
1576                }
1577            }
1578            Message::AddDraggedVariables(variables) => {
1579                let waves = self.user.waves.as_mut()?;
1580
1581                waves.focused_item = None;
1582                self.user.drag_source_idx = None;
1583                let target = self.user.drag_target_idx.take();
1584
1585                if let (Some(cmd), _) =
1586                    waves.add_variables(&self.translators, variables, target, true, false)
1587                {
1588                    self.load_variables(cmd);
1589                }
1590                self.invalidate_draw_commands();
1591            }
1592            Message::VariableDragStarted(vidx) => {
1593                self.user.drag_started = true;
1594                self.user.drag_source_idx = Some(vidx);
1595                self.user.drag_target_idx = None;
1596            }
1597            Message::VariableDragTargetChanged(position) => {
1598                self.user.drag_target_idx = Some(position);
1599            }
1600            Message::VariableDragFinished => {
1601                self.user.drag_started = false;
1602
1603                let source_vidx = self.user.drag_source_idx.take()?;
1604                let target_position = self.user.drag_target_idx.take()?;
1605
1606                // reordering
1607                self.save_current_canvas("Drag item".to_string());
1608                self.invalidate_draw_commands();
1609                let waves = self.user.waves.as_mut()?;
1610
1611                let focused_index = waves
1612                    .focused_item
1613                    .and_then(|vidx| waves.items_tree.to_displayed(vidx));
1614                let focused_item_ref = focused_index
1615                    .and_then(|idx| waves.items_tree.get(idx))
1616                    .map(|node| node.item_ref);
1617
1618                let mut to_move = waves
1619                    .items_tree
1620                    .iter_visible_extra()
1621                    .filter_map(|info| info.node.selected.then_some(info.idx))
1622                    .collect::<Vec<_>>();
1623                if let Some(idx) = focused_index {
1624                    to_move.push(idx)
1625                };
1626                if let Some(vidx) = waves.items_tree.to_displayed(source_vidx) {
1627                    to_move.push(vidx)
1628                };
1629
1630                let _ = waves.items_tree.move_items(to_move, target_position);
1631
1632                waves.focused_item = focused_item_ref
1633                    .and_then(|item_ref| {
1634                        waves
1635                            .items_tree
1636                            .iter_visible()
1637                            .position(|node| node.item_ref == item_ref)
1638                    })
1639                    .map(VisibleItemIndex);
1640            }
1641            Message::VariableValueToClipbord(vidx) => {
1642                self.handle_variable_clipboard_operation(
1643                    vidx,
1644                    |waves, item_ref: DisplayedItemRef| {
1645                        if let Some(DisplayedItem::Variable(_)) =
1646                            waves.displayed_items.get(&item_ref)
1647                        {
1648                            let field_ref = item_ref.into();
1649                            self.get_variable_value(
1650                                waves,
1651                                &field_ref,
1652                                &waves.cursor.as_ref().and_then(num::BigInt::to_biguint),
1653                            )
1654                        } else {
1655                            None
1656                        }
1657                    },
1658                );
1659            }
1660            Message::VariableNameToClipboard(vidx) => {
1661                self.handle_variable_clipboard_operation(
1662                    vidx,
1663                    |waves, item_ref: DisplayedItemRef| {
1664                        if let Some(DisplayedItem::Variable(variable)) =
1665                            waves.displayed_items.get(&item_ref)
1666                        {
1667                            Some(variable.variable_ref.name.clone())
1668                        } else {
1669                            None
1670                        }
1671                    },
1672                );
1673            }
1674            Message::VariableFullNameToClipboard(vidx) => {
1675                self.handle_variable_clipboard_operation(
1676                    vidx,
1677                    |waves, item_ref: DisplayedItemRef| {
1678                        if let Some(DisplayedItem::Variable(variable)) =
1679                            waves.displayed_items.get(&item_ref)
1680                        {
1681                            Some(variable.variable_ref.full_path_string())
1682                        } else {
1683                            None
1684                        }
1685                    },
1686                );
1687            }
1688            Message::SetViewportStrategy(s) => {
1689                if let Some(waves) = &mut self.user.waves {
1690                    for vp in &mut waves.viewports {
1691                        vp.move_strategy = s
1692                    }
1693                }
1694            }
1695            Message::Undo(count) => {
1696                let waves = self.user.waves.as_mut()?;
1697                for _ in 0..count {
1698                    if let Some(prev_state) = self.undo_stack.pop() {
1699                        self.redo_stack
1700                            .push(SystemState::current_canvas_state(waves, prev_state.message));
1701                        waves.focused_item = prev_state.focused_item;
1702                        waves.focused_transaction = prev_state.focused_transaction;
1703                        waves.items_tree = prev_state.items_tree;
1704                        waves.displayed_items = prev_state.displayed_items;
1705                        waves.markers = prev_state.markers;
1706                    } else {
1707                        break;
1708                    }
1709                }
1710                self.invalidate_draw_commands();
1711            }
1712            Message::Redo(count) => {
1713                let waves = self.user.waves.as_mut()?;
1714                for _ in 0..count {
1715                    if let Some(prev_state) = self.redo_stack.pop() {
1716                        self.undo_stack
1717                            .push(SystemState::current_canvas_state(waves, prev_state.message));
1718                        waves.focused_item = prev_state.focused_item;
1719                        waves.focused_transaction = prev_state.focused_transaction;
1720                        waves.items_tree = prev_state.items_tree;
1721                        waves.displayed_items = prev_state.displayed_items;
1722                        waves.markers = prev_state.markers;
1723                    } else {
1724                        break;
1725                    }
1726                }
1727                self.invalidate_draw_commands();
1728            }
1729            Message::DumpTree => {
1730                let waves = self.user.waves.as_ref()?;
1731                dump_tree(waves);
1732            }
1733            Message::GroupNew {
1734                name,
1735                before,
1736                items,
1737            } => {
1738                self.save_current_canvas(format!(
1739                    "Create group {}",
1740                    name.clone().unwrap_or("".to_owned())
1741                ));
1742                self.invalidate_draw_commands();
1743                let waves = self.user.waves.as_mut()?;
1744
1745                let passed_or_focused = before
1746                    .and_then(|before| {
1747                        waves
1748                            .items_tree
1749                            .get(before)
1750                            .map(|node| node.level)
1751                            .map(|level| TargetPosition { before, level })
1752                    })
1753                    .or_else(|| waves.focused_insert_position());
1754                let final_target = passed_or_focused.unwrap_or_else(|| waves.end_insert_position());
1755
1756                let mut item_refs = items.unwrap_or_else(|| {
1757                    waves
1758                        .items_tree
1759                        .iter_visible_selected()
1760                        .map(|node| node.item_ref)
1761                        .collect::<Vec<_>>()
1762                });
1763
1764                // if we are using the focus as the insert anchor, then move that as well
1765                let item_refs = if before.is_none() & passed_or_focused.is_some() {
1766                    let focus_index = waves
1767                        .items_tree
1768                        .to_displayed(waves.focused_item.expect("Inconsistent state"))
1769                        .expect("Inconsistent state");
1770                    item_refs.push(
1771                        waves
1772                            .items_tree
1773                            .get(focus_index)
1774                            .expect("Inconsistent state")
1775                            .item_ref,
1776                    );
1777                    item_refs
1778                } else {
1779                    item_refs
1780                };
1781
1782                if item_refs.is_empty() {
1783                    return None;
1784                }
1785
1786                let group_ref =
1787                    waves.add_group(name.unwrap_or("Group".to_owned()), Some(final_target));
1788
1789                let item_idxs = waves
1790                    .items_tree
1791                    .iter()
1792                    .enumerate()
1793                    .filter_map(|(idx, node)| {
1794                        item_refs
1795                            .contains(&node.item_ref)
1796                            .then_some(crate::displayed_item_tree::ItemIndex(idx))
1797                    })
1798                    .collect::<Vec<_>>();
1799
1800                if let Err(e) = waves.items_tree.move_items(
1801                    item_idxs,
1802                    crate::displayed_item_tree::TargetPosition {
1803                        before: ItemIndex(final_target.before.0 + 1),
1804                        level: final_target.level.saturating_add(1),
1805                    },
1806                ) {
1807                    dump_tree(waves);
1808                    waves.remove_displayed_item(group_ref);
1809                    error!("failed to move items into group: {e:?}")
1810                }
1811                waves.items_tree.xselect_all_visible(false);
1812            }
1813            Message::GroupDissolve(item_ref) => {
1814                self.save_current_canvas("Dissolve group".to_owned());
1815                self.invalidate_draw_commands();
1816                let waves = self.user.waves.as_mut()?;
1817                let item_index = waves.index_for_ref_or_focus(item_ref)?;
1818
1819                waves.items_tree.remove_dissolve(item_index);
1820            }
1821            Message::GroupFold(item_ref)
1822            | Message::GroupUnfold(item_ref)
1823            | Message::GroupFoldRecursive(item_ref)
1824            | Message::GroupUnfoldRecursive(item_ref) => {
1825                let unfold = matches!(
1826                    message,
1827                    Message::GroupUnfold(..) | Message::GroupUnfoldRecursive(..)
1828                );
1829                let recursive = matches!(
1830                    message,
1831                    Message::GroupFoldRecursive(..) | Message::GroupUnfoldRecursive(..)
1832                );
1833
1834                let undo_msg = if unfold {
1835                    "Unfold group".to_owned()
1836                } else {
1837                    "Fold group".to_owned()
1838                } + &(if recursive {
1839                    " recursive".to_owned()
1840                } else {
1841                    "".to_owned()
1842                });
1843                // TODO add group name? would have to break the pattern that we insert an
1844                // undo message even if no waves are available
1845                self.save_current_canvas(undo_msg);
1846                self.invalidate_draw_commands();
1847
1848                let waves = self.user.waves.as_mut()?;
1849                let item = waves.index_for_ref_or_focus(item_ref)?;
1850
1851                if let Some(focused_item) = waves.focused_item {
1852                    let info = waves
1853                        .items_tree
1854                        .get_visible_extra(focused_item)
1855                        .expect("Inconsistent state");
1856                    if waves.items_tree.subtree_contains(item, info.idx) {
1857                        waves.focused_item = None;
1858                    }
1859                }
1860                if recursive {
1861                    waves.items_tree.xfold_recursive(item, unfold);
1862                } else {
1863                    waves.items_tree.xfold(item, unfold);
1864                }
1865            }
1866            Message::GroupFoldAll | Message::GroupUnfoldAll => {
1867                let unfold = matches!(message, Message::GroupUnfoldAll);
1868                let undo_msg = if unfold {
1869                    "Fold all groups".to_owned()
1870                } else {
1871                    "Unfold all groups".to_owned()
1872                };
1873                self.save_current_canvas(undo_msg);
1874                self.invalidate_draw_commands();
1875
1876                let waves = self.user.waves.as_mut()?;
1877
1878                // remove focus if focused item is folded away -> prevent future waveform
1879                // adds being invisibly inserted
1880                if let Some(focused_item) = waves.focused_item {
1881                    let focused_level = waves
1882                        .items_tree
1883                        .get_visible(focused_item)
1884                        .expect("Inconsistent state")
1885                        .level;
1886                    if !unfold & (focused_level > 0) {
1887                        waves.focused_item = None;
1888                    }
1889                }
1890                waves.items_tree.xfold_all(unfold);
1891            }
1892            #[cfg(target_arch = "wasm32")]
1893            Message::StartWcpServer { .. } => {
1894                error!("Wcp is not supported on wasm")
1895            }
1896            #[cfg(target_arch = "wasm32")]
1897            Message::StopWcpServer => {
1898                error!("Wcp is not supported on wasm")
1899            }
1900            #[cfg(not(target_arch = "wasm32"))]
1901            Message::StartWcpServer { address, initiate } => {
1902                self.start_wcp_server(address, initiate);
1903            }
1904            #[cfg(not(target_arch = "wasm32"))]
1905            Message::StopWcpServer => {
1906                self.stop_wcp_server();
1907            }
1908            Message::SetupChannelWCP => {
1909                #[cfg(target_arch = "wasm32")]
1910                {
1911                    use futures::executor::block_on;
1912                    self.channels.wcp_c2s_receiver = block_on(WCP_CS_HANDLER.rx.write()).take();
1913                    if self.channels.wcp_c2s_receiver.is_none() {
1914                        error!("Failed to claim wasm tx, was SetupWasmWCP executed twice?");
1915                    }
1916                    self.channels.wcp_s2c_sender = Some(WCP_SC_HANDLER.tx.clone());
1917                }
1918            }
1919            Message::Exit | Message::ToggleFullscreen => {} // Handled in eframe::update
1920            Message::AddViewport => {
1921                let waves = self.user.waves.as_mut()?;
1922                let viewport = Viewport::new();
1923                waves.viewports.push(viewport);
1924                self.draw_data.borrow_mut().push(None);
1925            }
1926            Message::RemoveViewport => {
1927                let waves = self.user.waves.as_mut()?;
1928                if waves.viewports.len() > 1 {
1929                    waves.viewports.pop();
1930                    self.draw_data.borrow_mut().pop();
1931                }
1932            }
1933            Message::SelectTheme(theme_name) => {
1934                let theme = SurferTheme::new(theme_name)
1935                    .with_context(|| "Failed to set theme")
1936                    .ok()?;
1937                self.user.config.theme = theme;
1938                let ctx = self.context.as_ref()?;
1939                ctx.set_visuals(self.get_visuals());
1940            }
1941            Message::AsyncDone(_) => (),
1942            Message::AddGraphic(id, g) => {
1943                let waves = self.user.waves.as_mut()?;
1944                waves.graphics.insert(id, g);
1945            }
1946            Message::RemoveGraphic(id) => {
1947                let waves = self.user.waves.as_mut()?;
1948                waves.graphics.retain(|k, _| k != &id)
1949            }
1950            Message::ExpandDrawnItem { item, levels } => {
1951                self.items_to_expand.borrow_mut().push((item, levels))
1952            }
1953            Message::AddCharToPrompt(c) => *self.char_to_add_to_prompt.borrow_mut() = Some(c),
1954        }
1955        Some(())
1956    }
1957
1958    pub fn add_scope_as_group(
1959        &mut self,
1960        scope: ScopeRef,
1961        pos: TargetPosition,
1962        recursive: bool,
1963    ) -> TargetPosition {
1964        let Some(waves) = self.user.waves.as_mut() else {
1965            return pos;
1966        };
1967        let Some(container) = waves.inner.as_waves() else {
1968            return pos;
1969        };
1970
1971        let variables = container
1972            .variables_in_scope(&scope)
1973            .iter()
1974            .sorted_by(|a, b| numeric_sort::cmp(&a.name, &b.name))
1975            .cloned()
1976            .collect_vec();
1977        let child_scopes = container.child_scopes(&scope);
1978
1979        waves.add_group(scope.name(), Some(pos));
1980        let into_group_pos = TargetPosition {
1981            before: ItemIndex(pos.before.0 + 1),
1982            level: pos.level + 1,
1983        };
1984
1985        let (cmd, variable_refs) = waves.add_variables(
1986            &self.translators,
1987            variables,
1988            Some(into_group_pos),
1989            false,
1990            false,
1991        );
1992        let mut into_group_pos = TargetPosition {
1993            before: ItemIndex(into_group_pos.before.0 + variable_refs.len()),
1994            level: pos.level + 1,
1995        };
1996
1997        if let Some(cmd) = cmd {
1998            self.load_variables(cmd);
1999        }
2000
2001        if recursive {
2002            for child in child_scopes.unwrap_or(vec![]) {
2003                into_group_pos = self.add_scope_as_group(child, into_group_pos, recursive);
2004                into_group_pos.level = pos.level + 1;
2005            }
2006        }
2007        into_group_pos
2008    }
2009
2010    fn handle_variable_clipboard_operation<F>(
2011        &self,
2012        vidx: MessageTarget<VisibleItemIndex>,
2013        get_text: F,
2014    ) where
2015        F: FnOnce(&WaveData, DisplayedItemRef) -> Option<String>,
2016    {
2017        let Some(waves) = &self.user.waves else {
2018            return;
2019        };
2020        let vidx = if let MessageTarget::Explicit(vidx) = vidx {
2021            vidx
2022        } else if let Some(focused) = waves.focused_item {
2023            focused
2024        } else {
2025            return;
2026        };
2027        let Some(item_ref) = waves.items_tree.get_visible(vidx).map(|node| node.item_ref) else {
2028            return;
2029        };
2030
2031        if let Some(text) = get_text(waves, item_ref) {
2032            if let Some(ctx) = &self.context {
2033                ctx.copy_text(text);
2034            }
2035        }
2036    }
2037}
2038
2039pub fn dump_tree(waves: &WaveData) {
2040    let mut result = String::new();
2041    for (idx, node) in waves.items_tree.iter().enumerate() {
2042        for _ in 0..node.level.saturating_sub(1) {
2043            result.push(' ');
2044        }
2045
2046        if node.level > 0 {
2047            match waves.items_tree.get(ItemIndex(idx + 1)) {
2048                Some(next) if next.level < node.level => result.push_str("╰╴"),
2049                _ => result.push_str("├╴"),
2050            }
2051        }
2052
2053        result.push_str(
2054            &waves
2055                .displayed_items
2056                .get(&node.item_ref)
2057                .map(|item| item.name())
2058                .unwrap_or("?".to_owned()),
2059        );
2060        result.push_str(&format!("   ({:?})", node.item_ref));
2061        if node.selected {
2062            result.push_str(" !SEL! ")
2063        }
2064        result.push('\n');
2065    }
2066    info!("tree: \n{}", &result);
2067}
2068
2069pub struct StateWrapper(Arc<RwLock<SystemState>>);
2070impl App for StateWrapper {
2071    fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
2072        App::update(&mut *self.0.write().unwrap(), ctx, frame)
2073    }
2074}