1#![deny(unused_crate_dependencies)]
2
3pub mod analog_renderer;
4pub mod analog_signal_cache;
5pub mod annotation;
6pub mod annotation_list;
7pub mod arrow;
8pub mod async_util;
9pub mod batch_commands;
10#[cfg(feature = "performance_plot")]
11pub mod benchmark;
12mod channels;
13pub mod clock_highlighting;
14pub mod command_parser;
15pub mod command_prompt;
16pub mod comment;
17pub mod config;
18pub mod cxxrtl;
19pub mod cxxrtl_container;
20pub mod data_container;
21pub mod dialog;
22pub mod displayed_item;
23pub mod displayed_item_tree;
24pub mod drawing_canvas;
25pub mod file_dialog;
26pub mod file_history;
27pub mod file_watcher;
28pub mod frame_buffer;
29pub mod fzcmd;
30pub mod graphics;
31pub mod help;
32pub mod hierarchy;
33pub mod item_drawing_info;
34pub mod keyboard_shortcuts;
35pub mod keys;
36pub mod logs;
37pub mod marker;
38pub mod memory_viewer;
39pub mod menus;
40pub mod message;
41pub mod mousegestures;
42pub mod overview;
43pub mod rectangle;
44pub mod remote;
45pub mod server_file_window;
46pub mod state;
47pub mod state_file_io;
48pub mod state_util;
49pub mod statusbar;
50pub mod system_state;
51#[cfg(test)]
52pub mod tests;
53pub mod time;
54pub mod toolbar;
55pub mod tooltips;
56pub mod trace_style;
57pub mod transaction_container;
58pub mod transactions;
59pub mod translation;
60pub mod util;
61pub mod variable_direction;
62pub mod variable_filter;
63mod variable_index;
64pub mod variable_meta;
65pub mod variable_name_type;
66pub mod view;
67pub mod viewport;
68#[cfg(target_arch = "wasm32")]
69pub mod wasm_api;
70#[cfg(target_arch = "wasm32")]
71pub mod wasm_panic;
72pub mod wave_container;
73pub mod wave_data;
74pub mod wave_source;
75pub mod wcp;
76pub mod wellen;
77
78use crate::annotation::Annotatable;
79use crate::annotation::Annotation;
80use crate::annotation_list::AnnotationGroup;
81use crate::annotation_list::DEFAULT_GROUP_NAME;
82use crate::arrow::ArrowAnnotation;
83use crate::comment::CommentMessage;
84use crate::config::AutoLoad;
85use crate::displayed_item_tree::ItemIndex;
86use crate::displayed_item_tree::TargetPosition;
87use crate::rectangle::RectAnnotation;
88use crate::remote::get_time_table_from_server;
89use crate::variable_name_type::VariableNameType;
90
91use std::collections::HashMap;
92use std::sync::atomic::{AtomicUsize, Ordering};
93use std::sync::mpsc::{self, Receiver, Sender};
94use std::sync::{Arc, LazyLock, RwLock};
95
96use crate::channels::checked_send;
97use batch_commands::read_command_bytes;
98use batch_commands::read_command_file;
99#[cfg(target_arch = "wasm32")]
100use channels::{GlobalChannelTx, IngressHandler, IngressReceiver};
101use derive_more::Display;
102use displayed_item::DisplayedVariable;
103use displayed_item_tree::DisplayedItemTree;
104use eframe::{App, CreationContext};
105use egui::Id;
106use egui::{FontData, FontDefinitions, FontFamily};
107use eyre::{Result, WrapErr as _};
108use ftr_parser::types::Transaction;
109use futures::executor::block_on;
110use itertools::Itertools;
111use message::MessageTarget;
112use num::BigInt;
113use serde::Deserialize;
114use surfer_translation_types::Translator;
115use surfer_wcp::{WcpCSMessage, WcpEvent, WcpSCMessage};
116pub use system_state::SystemState;
117#[cfg(target_arch = "wasm32")]
118use tokio_stream as _;
119use tracing::{error, info, warn};
120#[cfg(all(not(target_arch = "wasm32"), feature = "wasm_plugins"))]
121use translation::wasm_translator::PluginTranslator;
122use wave_container::ScopeRef;
123
124#[cfg(all(not(target_arch = "wasm32"), feature = "wasm_plugins"))]
125use crate::async_util::perform_work;
126use crate::config::{SurferConfig, SurferTheme};
127use crate::dialog::{OpenSiblingStateFileDialog, ReloadWaveformDialog};
128use crate::displayed_item::{
129 AnalogVarState, DisplayedFieldRef, DisplayedItem, DisplayedItemRef, FieldFormat,
130};
131use crate::displayed_item_tree::VisibleItemIndex;
132use crate::drawing_canvas::TxDrawingCommands;
133use crate::frame_buffer::{FrameBufferColorMode, FrameBufferContent, build_frame_buffer_content};
134use crate::message::Message;
135use crate::transaction_container::{TransactionRef, TransactionStreamRef};
136use crate::translation::{AnyTranslator, all_translators};
137use crate::variable_filter::{VariableIOFilterType, VariableNameFilterType};
138use crate::viewport::Viewport;
139use crate::wave_container::{ScopeRefExt, VariableRefExt, WaveContainer};
140use crate::wave_data::WaveData;
141use crate::wave_source::{LoadOptions, WaveFormat, WaveSource};
142use crate::wellen::{HeaderResult, convert_format};
143
144pub(crate) static OUTSTANDING_TRANSACTIONS: AtomicUsize = AtomicUsize::new(0);
152
153pub static EGUI_CONTEXT: LazyLock<RwLock<Option<Arc<egui::Context>>>> =
154 LazyLock::new(|| RwLock::new(None));
155
156#[cfg(target_arch = "wasm32")]
157pub(crate) static WCP_CS_HANDLER: LazyLock<IngressHandler<WcpCSMessage>> =
158 LazyLock::new(IngressHandler::new);
159
160#[cfg(target_arch = "wasm32")]
161pub(crate) static WCP_SC_HANDLER: LazyLock<GlobalChannelTx<WcpSCMessage>> =
162 LazyLock::new(GlobalChannelTx::new);
163
164#[derive(Default)]
165pub struct StartupParams {
166 pub waves: Option<WaveSource>,
167 pub wcp_initiate: Option<u16>,
168 pub startup_commands: Vec<String>,
169}
170
171fn setup_custom_font(ctx: &egui::Context) {
172 let mut fonts = FontDefinitions::default();
173
174 fonts.font_data.insert(
175 "remix_icons".to_owned(),
176 FontData::from_static(egui_remixicon::FONT).into(),
177 );
178
179 fonts
180 .families
181 .get_mut(&FontFamily::Proportional)
182 .unwrap()
183 .push("remix_icons".to_owned());
184
185 fonts
186 .families
187 .get_mut(&FontFamily::Monospace)
188 .unwrap()
189 .push("remix_icons".to_owned());
190
191 ctx.set_fonts(fonts);
192}
193
194pub fn run_egui(cc: &CreationContext, mut state: SystemState) -> Result<Box<dyn App>> {
195 let ctx_arc = Arc::new(cc.egui_ctx.clone());
196 *EGUI_CONTEXT.write().unwrap() = Some(ctx_arc.clone());
197 state.context = Some(ctx_arc.clone());
198 cc.egui_ctx
199 .set_visuals_of(egui::Theme::Dark, state.get_visuals());
200 cc.egui_ctx
201 .set_visuals_of(egui::Theme::Light, state.get_visuals());
202 cc.egui_ctx.all_styles_mut(|style| {
203 if state.user.config.animation_time == 0.0 {
204 info!("With animation_time set to 0.0, animations cannot be enabled.");
205 }
206 style.animation_time = if state.user.config.animation_enabled() {
207 state.user.config.animation_time
208 } else {
209 0.0
210 };
211 });
212 #[cfg(not(target_arch = "wasm32"))]
213 if state.user.config.wcp.autostart {
214 state.start_wcp_server(Some(state.user.config.wcp.address.clone()), false);
215 }
216 setup_custom_font(&cc.egui_ctx);
217 Ok(Box::new(state))
218}
219
220#[derive(Debug, Clone, Copy, Deserialize, Display, PartialEq, Eq)]
221pub enum MoveDir {
222 #[display("up")]
223 Up,
224
225 #[display("down")]
226 Down,
227}
228
229pub enum ColorSpecifier {
230 Index(usize),
231 Name(String),
232}
233
234enum CachedDrawData {
235 WaveDrawData(CachedWaveDrawData),
236 TransactionDrawData(CachedTransactionDrawData),
237}
238
239struct CachedWaveDrawData {
240 pub draw_commands: HashMap<DisplayedFieldRef, drawing_canvas::DrawingCommands>,
241 pub clock_edges: crate::clock_highlighting::ClockHighlightData,
242 pub ticks: Vec<(String, f32, i64)>,
243}
244
245struct CachedTransactionDrawData {
246 pub draw_commands: HashMap<TransactionRef, TxDrawingCommands>,
247 pub stream_to_displayed_txs: HashMap<TransactionStreamRef, Vec<TransactionRef>>,
248 pub inc_relation_tx_ids: Vec<TransactionRef>,
249 pub out_relation_tx_ids: Vec<TransactionRef>,
250}
251
252pub struct Channels {
253 pub msg_sender: Sender<Message>,
254 pub msg_receiver: Receiver<Message>,
255 #[cfg(target_arch = "wasm32")]
256 wcp_c2s_receiver: Option<IngressReceiver<WcpCSMessage>>,
257 #[cfg(not(target_arch = "wasm32"))]
258 wcp_c2s_receiver: Option<tokio::sync::mpsc::Receiver<WcpCSMessage>>,
259 wcp_s2c_sender: Option<tokio::sync::mpsc::Sender<WcpSCMessage>>,
260}
261impl Channels {
262 fn new() -> Self {
263 let (msg_sender, msg_receiver) = mpsc::channel();
264 Self {
265 msg_sender,
266 msg_receiver,
267 wcp_c2s_receiver: None,
268 wcp_s2c_sender: None,
269 }
270 }
271}
272
273pub struct WcpClientCapabilities {
274 pub waveforms_loaded: bool,
275 pub goto_declaration: bool,
276 pub add_drivers: bool,
277 pub add_loads: bool,
278}
279impl WcpClientCapabilities {
280 fn new() -> Self {
281 Self {
282 waveforms_loaded: false,
283 goto_declaration: false,
284 add_drivers: false,
285 add_loads: false,
286 }
287 }
288}
289
290struct CanvasState {
292 message: String,
293 focused_item: Option<VisibleItemIndex>,
294 focused_transaction: (Option<TransactionRef>, Option<Transaction>),
295 items_tree: DisplayedItemTree,
296 displayed_items: HashMap<DisplayedItemRef, DisplayedItem>,
297 markers: HashMap<u8, BigInt>,
298 annotations: Vec<Annotation>,
299 annotation_group: Vec<AnnotationGroup>,
300 annotation_list: bool,
301 selected_annotation: Option<Id>,
302 annotation_counter: i32,
303}
304
305impl SystemState {
306 pub fn update(&mut self, message: Message) -> Option<()> {
307 if tracing::enabled!(tracing::Level::TRACE)
308 && !matches!(message, Message::CommandPromptUpdate { .. })
309 {
310 tracing::trace!("{message:?}");
311 }
312 match message {
313 Message::SetActiveScope(scope) => {
314 let waves = self.user.waves.as_mut()?;
315 waves.set_active_scope(scope)?;
316 }
317
318 Message::ExpandScope(scope_ref) => {
319 *self.scope_ref_to_expand.borrow_mut() = Some(scope_ref);
320 }
321 Message::AddVariables(vars) => {
322 if !vars.is_empty() {
323 let undo_msg = if vars.len() == 1 {
324 format!("Add variable {}", vars[0].name)
325 } else {
326 format!("Add {} variables", vars.len())
327 };
328 self.save_current_canvas(undo_msg);
329 if let Some(waves) = self.user.waves.as_mut() {
330 if let (Some(cmd), _) =
331 waves.add_variables(&self.translators, vars, None, true, false, None)
332 {
333 self.load_variables(cmd);
334 }
335 self.invalidate_draw_commands();
336 } else {
337 error!("Could not load signals, no waveform loaded");
338 }
339 }
340 }
341 Message::DownloadDefaultConfig => {
342 #[cfg(not(target_arch = "wasm32"))]
343 {
344 if let Err(e) = crate::config::write_default_config() {
345 tracing::error!("Failed to write default config: {}", e);
346 }
347 }
348
349 #[cfg(target_arch = "wasm32")]
350 {
351 tracing::warn!("Download default config is not supported on WASM");
352 }
353 }
354
355 Message::AddDivider(name, vidx) => {
356 self.save_current_canvas("Add divider".into());
357 let waves = self.user.waves.as_mut()?;
358 waves.add_divider(name, vidx);
359 }
360 Message::AddTimeLine(vidx) => {
361 self.save_current_canvas("Add timeline".into());
362 let waves = self.user.waves.as_mut()?;
363 waves.add_timeline(vidx);
364 }
365 Message::AddScope(scope, recursive) => {
366 self.save_current_canvas(format!("Add scope {}", scope.name()));
367
368 let vars = self.get_scope(&scope, recursive);
369 let waves = self.user.waves.as_mut()?;
370
371 if let (Some(cmd), _) =
373 waves.add_variables(&self.translators, vars, None, true, false, None)
374 {
375 self.load_variables(cmd);
376 }
377
378 self.invalidate_draw_commands();
379 }
380 Message::AddScopeAsGroup(scope, recursive) => {
381 self.save_current_canvas(format!("Add scope {} as group", scope.name()));
382 let waves = self.user.waves.as_mut()?;
383 let passed_or_focused = waves.insert_position(waves.focused_item);
384 let target = passed_or_focused.unwrap_or_else(|| waves.end_insert_position());
385
386 self.add_scope_as_group(&scope, target, recursive, None);
387 self.invalidate_draw_commands();
388
389 self.user.waves.as_mut()?.compute_variable_display_names();
390 }
391 Message::AddCount(digit) => {
392 if let Some(count) = &mut self.user.count {
393 count.push(digit);
394 } else {
395 self.user.count = Some(digit.to_string());
396 }
397 }
398 Message::AddStreamOrGenerator(s) => {
399 let undo_msg = if let Some(gen_id) = s.gen_id {
400 format!("Add generator(id: {gen_id})")
401 } else {
402 format!("Add stream(id: {})", s.stream_id)
403 };
404 self.save_current_canvas(undo_msg);
405
406 let waves = self.user.waves.as_mut()?;
407 if s.gen_id.is_some() {
408 waves.add_generator(s);
409 } else {
410 waves.add_stream(s);
411 }
412 self.invalidate_draw_commands();
413 }
414 Message::AddStreamOrGeneratorFromName(scope, name) => {
415 self.save_current_canvas(format!("Add Stream/Generator from name: {name}"));
416 let waves = self.user.waves.as_mut()?;
417 waves.add_stream_or_generator_from_name(scope, name)?;
418 self.invalidate_draw_commands();
419 }
420 Message::AddAllFromStreamScope(scope_name) => {
421 self.save_current_canvas(format!("Add all from scope {}", scope_name.clone()));
422 let waves = self.user.waves.as_mut()?;
423 waves.add_all_from_stream_scope(scope_name)?;
424 self.invalidate_draw_commands();
425 }
426 Message::InvalidateCount => self.user.count = None,
427 Message::SetNameAlignRight(align_right) => {
428 self.user.align_names_right = Some(align_right);
429 }
430 Message::FocusItem(idx) => {
431 let waves = self.user.waves.as_mut()?;
432
433 let visible_items_len = waves.displayed_items.len();
434 if idx.0 < visible_items_len {
435 waves.focused_item = Some(idx);
436 } else {
437 error!(
438 "Can not focus variable {} because only {visible_items_len} variables are visible.",
439 idx.0
440 );
441 }
442 }
443 Message::ItemSelectRange(select_to) => {
444 let waves = self.user.waves.as_mut()?;
445 let select_from = waves.focused_item?;
446 waves
447 .items_tree
448 .xselect_visible_range(select_from, select_to, true);
449 }
450 Message::ItemSelectAll => {
451 let waves = self.user.waves.as_mut()?;
452 waves.items_tree.xselect_all_visible(true);
453 }
454 Message::SetItemSelected(vidx, selected) => {
455 let waves = self.user.waves.as_mut()?;
456 waves.items_tree.xselect(vidx, selected);
457 }
458 Message::ToggleItemSelected(vidx) => {
459 let waves = self.user.waves.as_mut()?;
460 let node = vidx
461 .or(waves.focused_item)
462 .and_then(|vidx| waves.items_tree.to_displayed(vidx))
463 .and_then(|item| waves.items_tree.get_mut(item))?;
464 node.selected = !node.selected;
465 }
466 Message::SetDefaultTimeline(v) => {
467 self.user.show_default_timeline = Some(v);
468 }
469 Message::UnfocusItem => {
470 let waves = self.user.waves.as_mut()?;
471 waves.focused_item = None;
472 }
473 Message::MoveFocus(direction, count, select) => {
474 let waves = self.user.waves.as_mut()?;
475 let visible_item_cnt = waves.items_tree.iter_visible().count();
476 if visible_item_cnt == 0 {
477 return None;
478 }
479
480 let new_focus_vidx = VisibleItemIndex(match direction {
481 MoveDir::Up => waves
482 .focused_item
483 .map_or(visible_item_cnt, |vidx| vidx.0)
484 .saturating_sub(count),
485 MoveDir::Down => waves
486 .focused_item
487 .map_or(usize::MAX, |vidx| vidx.0)
488 .wrapping_add(count)
489 .clamp(0, visible_item_cnt - 1),
490 });
491
492 if select {
493 if let Some(vidx) = waves.focused_item {
494 waves.items_tree.xselect(vidx, true);
495 }
496 waves.items_tree.xselect(new_focus_vidx, true);
497 }
498 waves.focused_item = Some(new_focus_vidx);
499 }
500 Message::FocusTransaction(tx_ref, tx) => {
501 if let Some(tx_ref) = tx_ref.as_ref()
502 && tx.is_none()
503 {
504 self.save_current_canvas(format!("Focus Transaction id: {}", tx_ref.id));
505 }
506 let waves = self.user.waves.as_mut()?;
507 let invalidate = tx.is_none();
508 waves.focused_transaction =
509 (tx_ref, tx.or_else(|| waves.focused_transaction.1.clone()));
510 if invalidate {
511 self.invalidate_draw_commands();
512 }
513 }
514 Message::ScrollToItem(position) => {
515 let waves = self.user.waves.as_mut()?;
516 waves.scroll_to_item(position);
517 }
518 Message::SetScrollOffset(offset) => {
519 let waves = self.user.waves.as_mut()?;
520 waves.scroll_offset = offset;
521 }
522 Message::SetLogsVisible(visibility) => self.user.show_logs = visibility,
523 Message::SetFrameBufferVariable(variable_ref) => {
524 let waves = self.user.waves.as_mut()?;
525 if let Some(cmd) = waves
526 .inner
527 .as_waves_mut()?
528 .load_variables(std::iter::once(&variable_ref))
529 .map_err(|e| error!("{e:#?}"))
530 .ok()
531 .flatten()
532 {
533 self.load_variables(cmd);
534 }
535 self.frame_buffer_content = Some(FrameBufferContent::Variable(variable_ref));
536 }
537 Message::SetFrameBufferVisibleVariable(None) => {
538 self.frame_buffer_content = None;
539 }
540 Message::SetFrameBufferVisibleVariable(Some(vidx)) => {
541 let waves = self.user.waves.as_ref()?;
542 self.frame_buffer_content = waves
543 .items_tree
544 .get_visible(vidx)
545 .and_then(|node| waves.displayed_items.get(&node.item_ref))
546 .and_then(|item| match item {
547 DisplayedItem::Variable(variable) => {
548 Some(FrameBufferContent::Variable(variable.variable_ref.clone()))
549 }
550 _ => None,
551 });
552 }
553 Message::SetFrameBufferArray(scope_ref) => {
554 let waves = self.user.waves.as_mut()?;
555 let (levels, all_leaf_vars) = {
556 let wave_container = waves.inner.as_waves()?;
557 build_frame_buffer_content(wave_container, &scope_ref)?
558 };
559 if let Some(cmd) = waves
560 .inner
561 .as_waves_mut()?
562 .load_variables(all_leaf_vars.iter())
563 .map_err(|e| error!("{e:#?}"))
564 .ok()
565 .flatten()
566 {
567 self.load_variables(cmd);
568 }
569 self.frame_buffer_content = Some(FrameBufferContent::Array { scope_ref, levels });
570 }
571
572 Message::SetFrameBufferMode(mode, bits1, bits2, bits3) => {
573 let settings = &mut self.user.frame_buffer.color_settings;
574 settings.color_mode = mode;
575 match mode {
576 FrameBufferColorMode::Grayscale => {
577 settings.grayscale_bits = bits1.clamp(1, 8);
578 }
579 FrameBufferColorMode::Rgb => {
580 settings.r_bits = bits1.min(8);
581 settings.g_bits = bits2.min(8);
582 settings.b_bits = bits3.min(8);
583 }
584 FrameBufferColorMode::YCbCr => {
585 settings.y_bits = bits1.min(8);
586 settings.cb_bits = bits2.min(8);
587 settings.cr_bits = bits3.min(8);
588 }
589 }
590 self.frame_buffer_pixel_cache = None;
591 }
592 Message::SetFrameBufferWidth(width) => {
593 self.user.frame_buffer.pixels_per_row = width.max(1);
594 }
595 Message::SetFrameBufferRange(ranges) => {
596 let Some(FrameBufferContent::Array { levels, .. }) =
597 self.frame_buffer_content.as_mut()
598 else {
599 return None;
600 };
601
602 for (level, (first, last)) in levels.iter_mut().zip(ranges) {
603 let mut clamped_first = first.clamp(level.min_index, level.max_index);
604 let mut clamped_last = last.clamp(level.min_index, level.max_index);
605 if clamped_first > clamped_last {
606 std::mem::swap(&mut clamped_first, &mut clamped_last);
607 }
608 level.first_index = clamped_first;
609 level.last_index = clamped_last;
610 }
611 }
612 Message::OpenMemoryViewer { scope, name } => {
613 let waves = self.user.waves.as_mut()?;
614 let wave_container = waves.inner.as_waves()?;
615
616 let sibling_variables = wave_container.variables_in_scope(&scope);
617
618 if let Some(cmd) = waves
619 .inner
620 .as_waves_mut()?
621 .load_variables(sibling_variables.iter())
622 .map_err(|e| error!("{e:#?}"))
623 .ok()
624 .flatten()
625 {
626 self.load_variables(cmd);
627 }
628
629 self.memory_viewer.open = true;
630 self.memory_viewer.scope = Some(scope);
631 self.memory_viewer.name = name;
632 }
633 Message::SetCursorWindowVisible(visibility) => {
634 self.user.show_cursor_window = visibility;
635 }
636 Message::VerticalScroll(direction, count) => {
637 let waves = self.user.waves.as_mut()?;
638 let current_item = waves.get_top_item();
639 match direction {
640 MoveDir::Down => {
641 waves.scroll_to_item(current_item + count);
642 }
643 MoveDir::Up => {
644 if current_item > count {
645 waves.scroll_to_item(current_item - count);
646 } else {
647 waves.scroll_to_item(0);
648 }
649 }
650 }
651 }
652 Message::SetSurverFileWindowVisible(visibility) => {
653 self.user.show_server_file_window = visibility;
654 }
655 Message::LoadSurverFileByIndex(file_index, load_options) => {
656 self.user.show_server_file_window = false;
658 let force_switch = self.user.selected_server_file_index != file_index;
659 if let Some(url) = self.user.surver_url.as_ref() {
660 self.load_wave_from_url(url.clone(), load_options, force_switch, file_index);
661 }
662 }
663 Message::LoadSurverFileByName(file_name, load_options) => {
664 self.user.show_server_file_window = false;
666 let file_index = self
667 .user
668 .surver_file_infos
669 .as_ref()?
670 .iter()
671 .position(|fi| fi.filename == file_name);
672 let force_switch = self.user.selected_server_file_index != file_index;
673
674 if let Some(url) = self.user.surver_url.as_ref() {
675 self.load_wave_from_url(url.clone(), load_options, force_switch, file_index);
676 }
677 }
678 Message::RemoveVisibleItems(target) => match target {
679 MessageTarget::Explicit(vidx) => {
680 let waves = self.user.waves.as_ref();
681 let item_ref = waves
682 .and_then(|waves| waves.items_tree.get_visible(vidx))
683 .map(|node| node.item_ref);
684 let undo_msg = item_ref
685 .and_then(|item_ref| {
686 waves.and_then(|waves| waves.displayed_items.get(&item_ref))
687 })
688 .map(displayed_item::DisplayedItem::name)
689 .map_or("Remove one item".to_string(), |name| {
690 format!("Remove item {name}")
691 });
692 self.save_current_canvas(undo_msg);
693
694 if let Some(waves) = self.user.waves.as_mut()
695 && let Some(item_ref) = item_ref
696 {
697 waves.remove_displayed_item(item_ref);
698 waves.compute_variable_display_names();
699 }
700 }
701 MessageTarget::CurrentSelection => {
702 self.save_current_canvas("Remove selected items".to_owned());
703 let waves = self.user.waves.as_mut()?;
704
705 let mut remove_ids: Vec<_> = waves
706 .items_tree
707 .iter_visible_selected()
708 .map(|node| node.item_ref)
709 .collect();
710 if let Some(node) = waves
711 .focused_item
712 .and_then(|focus| waves.items_tree.get_visible(focus))
713 {
714 remove_ids.push(node.item_ref);
715 }
716 for &item_ref in &remove_ids {
717 waves.remove_displayed_item(item_ref);
718 }
719 waves.compute_variable_display_names();
720 }
721 },
722 Message::RemoveItems(items) => {
723 let undo_msg = self
724 .user
725 .waves
726 .as_ref()
727 .and_then(|waves| {
728 if items.len() == 1 {
729 items.first().and_then(|item_ref| {
730 waves
731 .displayed_items
732 .get(item_ref)
733 .map(|item| format!("Remove item {}", item.name()))
734 })
735 } else {
736 Some(format!("Remove {} items", items.len()))
737 }
738 })
739 .unwrap_or_default();
740 self.save_current_canvas(undo_msg);
741
742 let waves = self.user.waves.as_mut()?;
743 for id in items.iter().sorted_unstable_by(|a, b| Ord::cmp(b, a)) {
744 waves.remove_displayed_item(*id);
745 }
746 }
747 Message::MoveFocusedItem(direction, count) => {
748 self.save_current_canvas(format!("Move item {direction}, {count}"));
749 self.invalidate_draw_commands();
750 let waves = self.user.waves.as_mut()?;
751 let mut vidx = waves.focused_item?;
752 for _ in 0..count {
753 vidx = waves
754 .items_tree
755 .move_item(vidx, direction, |node| {
756 matches!(
757 waves.displayed_items.get(&node.item_ref),
758 Some(DisplayedItem::Group(..))
759 )
760 })
761 .expect("move failed for unknown reason");
762 }
763 waves.focused_item = waves.focused_item.and(Some(vidx));
764 }
765 Message::CanvasScroll {
766 delta,
767 viewport_idx,
768 } => {
769 let waves = self.user.waves.as_mut()?;
770 waves.viewports[viewport_idx]
771 .handle_canvas_scroll(f64::from(delta.y) + f64::from(delta.x));
772 self.invalidate_draw_commands();
773 }
774 Message::CanvasZoom {
775 delta,
776 mouse_ptr,
777 viewport_idx,
778 } => {
779 let waves = self.user.waves.as_mut()?;
780 if let Some(max_timestamp) = waves.max_timestamp() {
781 waves.viewports[viewport_idx].handle_canvas_zoom(
782 mouse_ptr,
783 f64::from(delta),
784 &max_timestamp,
785 &waves.cached_time_offset,
786 );
787 self.invalidate_draw_commands();
788 } else {
789 warn!(
790 "Canvas zoom: No timestamps count, even though waveforms should be loaded"
791 );
792 }
793 }
794 Message::ZoomToCursor {
795 delta,
796 viewport_idx,
797 } => {
798 let waves = self.user.waves.as_mut()?;
799 if let Some(max_timestamp) = waves.max_timestamp() {
800 let cursor = waves.cursor.as_ref()?;
801 let time_offset = &waves.cached_time_offset;
802 waves.viewports[viewport_idx].zoom_to_time(
803 cursor,
804 f64::from(delta),
805 &max_timestamp,
806 time_offset,
807 );
808 self.invalidate_draw_commands();
809 } else {
810 warn!(
811 "Zoom to cursor: No timestamps count, even though waveforms should be loaded"
812 );
813 }
814 }
815 Message::ZoomToFit { viewport_idx } => {
816 let waves = self.user.waves.as_mut()?;
817 waves.viewports[viewport_idx].zoom_to_fit();
818 self.invalidate_draw_commands();
819 }
820 Message::GoToEnd { viewport_idx } => {
821 let waves = self.user.waves.as_mut()?;
822 waves.viewports[viewport_idx].go_to_end();
823 self.invalidate_draw_commands();
824 }
825 Message::GoToStart { viewport_idx } => {
826 let waves = self.user.waves.as_mut()?;
827 waves.viewports[viewport_idx].go_to_start();
828 self.invalidate_draw_commands();
829 }
830 Message::GoToTime(time, viewport_idx) => {
831 let waves = self.user.waves.as_mut()?;
832 if let Some(max_timestamp) = waves.max_timestamp() {
834 let time = time?;
835 waves.viewports[viewport_idx].go_to_time(
836 &time.clone(),
837 &max_timestamp,
838 &waves.cached_time_offset,
839 );
840 self.invalidate_draw_commands();
841 } else {
842 warn!(
843 "Go to time: No timestamps count, even though waveforms should be loaded"
844 );
845 }
846 }
847 Message::SetTimeUnit(timeunit) => {
848 self.user.wanted_timeunit = timeunit;
849 self.invalidate_draw_commands();
850 }
851 Message::SetTimeStringFormatting(format) => {
852 self.user.time_string_format = format;
853 self.invalidate_draw_commands();
854 }
855 Message::ZoomToRange {
856 start,
857 end,
858 viewport_idx,
859 } => {
860 let waves = self.user.waves.as_mut()?;
861 if let Some(max_timestamp) = waves.max_timestamp() {
863 waves.viewports[viewport_idx].zoom_to_range(
864 &start,
865 &end,
866 &max_timestamp,
867 &waves.cached_time_offset,
868 );
869 self.invalidate_draw_commands();
870 } else {
871 warn!(
872 "Zoom to range: No timestamps count, even though waveforms should be loaded"
873 );
874 }
875 }
876 Message::VariableFormatChange(displayed_field_ref, format) => {
877 let waves = self.user.waves.as_mut()?;
878 if !self
879 .translators
880 .all_translator_names()
881 .contains(&format.as_str())
882 {
883 warn!("No translator {format}");
884 return None;
885 }
886
887 let update_format =
888 |variable: &mut DisplayedVariable, field_ref: DisplayedFieldRef| {
889 if field_ref.field.is_empty() {
890 let Ok(meta) = waves
891 .inner
892 .as_waves()
893 .unwrap()
894 .variable_meta(&variable.variable_ref)
895 .map_err(|e| {
896 warn!("Error trying to get variable metadata: {e:#?}");
897 })
898 else {
899 return;
900 };
901 let translator = self.translators.get_translator(&format);
902 let new_info = translator.variable_info(&meta).unwrap();
903
904 variable.format = Some(format.clone());
905 variable.info = new_info;
906
907 variable.downgrade_type_limits_if_unsupported(translator, &meta);
908 } else {
909 variable
910 .field_formats
911 .retain(|ff| ff.field != field_ref.field);
912 variable.field_formats.push(FieldFormat {
913 field: field_ref.field,
914 format: format.clone(),
915 });
916 }
917 };
918
919 let focused = waves
921 .focused_item
922 .and_then(|vidx| waves.items_tree.get_visible(vidx))
923 .map(|node| node.item_ref);
924
925 let mut redraw = false;
926
927 match displayed_field_ref {
928 MessageTarget::Explicit(field_ref) => {
929 if let Some(DisplayedItem::Variable(displayed_variable)) =
930 waves.displayed_items.get_mut(&field_ref.item)
931 {
932 update_format(displayed_variable, field_ref);
933 redraw = true;
934 }
935 }
936 MessageTarget::CurrentSelection => {
937 if let Some(focused) = focused
939 && let Some(DisplayedItem::Variable(displayed_variable)) =
940 waves.displayed_items.get_mut(&focused)
941 {
942 update_format(displayed_variable, DisplayedFieldRef::from(focused));
943 redraw = true;
944 }
945 for item in waves
946 .items_tree
947 .iter_visible_selected()
948 .map(|node| node.item_ref)
949 {
950 let field_ref = DisplayedFieldRef::from(item);
952 if let Some(DisplayedItem::Variable(variable)) =
953 waves.displayed_items.get_mut(&item)
954 {
955 update_format(variable, field_ref);
956 }
957 redraw = true;
958 }
959 }
960 }
961
962 if redraw {
963 self.invalidate_draw_commands();
964 }
965 }
966 Message::ItemSelectionClear => {
967 let waves = self.user.waves.as_mut()?;
968 waves.items_tree.xselect_all_visible(false);
969 }
970 Message::ItemColorChange(vidx, color_name) => {
971 self.save_current_canvas(format!(
972 "Change item color to {}",
973 color_name.clone().unwrap_or("default".into())
974 ));
975 self.invalidate_draw_commands();
976 let waves = self.user.waves.as_mut()?;
977
978 match vidx {
979 MessageTarget::Explicit(vidx) => {
980 let node = waves.items_tree.get_visible(vidx)?;
981 waves
982 .displayed_items
983 .entry(node.item_ref)
984 .and_modify(|item| item.set_color(&color_name));
985 }
986 MessageTarget::CurrentSelection => {
987 if let Some(focused) = waves.focused_item {
988 let node = waves.items_tree.get_visible(focused)?;
989 waves
990 .displayed_items
991 .entry(node.item_ref)
992 .and_modify(|item| item.set_color(&color_name));
993 }
994
995 for node in waves.items_tree.iter_visible_selected() {
996 waves
997 .displayed_items
998 .entry(node.item_ref)
999 .and_modify(|item| item.set_color(&color_name));
1000 }
1001 }
1002 }
1003 }
1004 Message::ItemNameChange(vidx, name) => {
1005 self.save_current_canvas(format!(
1006 "Change item name to {}",
1007 name.clone().unwrap_or("default".into())
1008 ));
1009 let waves = self.user.waves.as_mut()?;
1010 let vidx = vidx.or(waves.focused_item)?;
1011 let node = waves.items_tree.get_visible(vidx)?;
1012 waves
1013 .displayed_items
1014 .entry(node.item_ref)
1015 .and_modify(|item| item.set_name(name));
1016 }
1017 Message::ItemNameReset(target) => {
1018 self.save_current_canvas("Resetting item name(s)".to_owned());
1019 let waves = self.user.waves.as_mut()?;
1020 match target {
1021 MessageTarget::Explicit(vidx) => {
1022 let node = waves.items_tree.get_visible(vidx)?;
1023 waves
1024 .displayed_items
1025 .entry(node.item_ref)
1026 .and_modify(|item| item.set_name(None));
1027 }
1028 MessageTarget::CurrentSelection => {
1029 for node in waves.items_tree.iter_visible_selected() {
1030 waves
1031 .displayed_items
1032 .entry(node.item_ref)
1033 .and_modify(|item| item.set_name(None));
1034 }
1035 }
1036 }
1037 }
1038 Message::ItemBackgroundColorChange(vidx, color_name) => {
1039 self.save_current_canvas(format!(
1040 "Change item background color to {}",
1041 color_name.clone().unwrap_or("default".into())
1042 ));
1043 let waves = self.user.waves.as_mut()?;
1044
1045 match vidx {
1046 MessageTarget::Explicit(vidx) => {
1047 let node = waves.items_tree.get_visible(vidx)?;
1048 waves
1049 .displayed_items
1050 .entry(node.item_ref)
1051 .and_modify(|item| item.set_background_color(&color_name));
1052 }
1053 MessageTarget::CurrentSelection => {
1054 if let Some(focused) = waves.focused_item {
1055 let node = waves.items_tree.get_visible(focused)?;
1056 waves
1057 .displayed_items
1058 .entry(node.item_ref)
1059 .and_modify(|item| item.set_background_color(&color_name));
1060 }
1061
1062 for node in waves.items_tree.iter_visible_selected() {
1063 waves
1064 .displayed_items
1065 .entry(node.item_ref)
1066 .and_modify(|item| item.set_background_color(&color_name));
1067 }
1068 }
1069 }
1070 }
1071 Message::ItemHeightScalingFactorChange(vidx, scale) => {
1072 self.save_current_canvas(format!("Change item height scaling factor to {scale}"));
1073 let waves = self.user.waves.as_mut()?;
1074
1075 match vidx {
1076 MessageTarget::Explicit(vidx) => {
1077 let node = waves.items_tree.get_visible(vidx)?;
1078 waves
1079 .displayed_items
1080 .entry(node.item_ref)
1081 .and_modify(|item| item.set_height_scaling_factor(scale));
1082 }
1083 MessageTarget::CurrentSelection => {
1084 if let Some(focused) = waves.focused_item {
1085 let node = waves.items_tree.get_visible(focused)?;
1086 waves
1087 .displayed_items
1088 .entry(node.item_ref)
1089 .and_modify(|item| item.set_height_scaling_factor(scale));
1090 }
1091
1092 for node in waves.items_tree.iter_visible_selected() {
1093 waves
1094 .displayed_items
1095 .entry(node.item_ref)
1096 .and_modify(|item| item.set_height_scaling_factor(scale));
1097 }
1098 }
1099 }
1100 }
1101 Message::SetAnalogSettings(vidx, new_settings) => {
1102 self.save_current_canvas("Set analog state".into());
1103 self.invalidate_draw_commands();
1104 let analog_waveform_multiplier = self.user.config.layout.analog_waveform_multiplier;
1105 let waves = self.user.waves.as_mut()?;
1106
1107 let update = |item: &mut DisplayedItem| {
1109 if let DisplayedItem::Variable(var) = item {
1110 match (&mut var.analog, new_settings) {
1111 (Some(s), Some(new)) => s.settings = new,
1112 (None, Some(new)) => {
1113 var.analog = Some(AnalogVarState::new(new));
1114 var.height_scaling_factor = Some(analog_waveform_multiplier);
1115 }
1116 (_, None) => var.analog = None,
1117 }
1118 }
1119 };
1120
1121 match vidx {
1122 MessageTarget::Explicit(vidx) => {
1123 let node = waves.items_tree.get_visible(vidx)?;
1124 waves
1125 .displayed_items
1126 .entry(node.item_ref)
1127 .and_modify(update);
1128 }
1129 MessageTarget::CurrentSelection => {
1130 if let Some(focused) = waves.focused_item {
1131 let node = waves.items_tree.get_visible(focused)?;
1132 waves
1133 .displayed_items
1134 .entry(node.item_ref)
1135 .and_modify(update);
1136 }
1137 for node in waves.items_tree.iter_visible_selected() {
1138 waves
1139 .displayed_items
1140 .entry(node.item_ref)
1141 .and_modify(update);
1142 }
1143 }
1144 }
1145 }
1146 Message::MoveCursorToTransition {
1147 next,
1148 variable,
1149 skip_zero,
1150 } => {
1151 let waves = self.user.waves.as_mut()?;
1152 if let Some(max_timestamp) = waves.max_timestamp() {
1154 if waves.cursor.is_none()
1158 && waves.focused_item.is_some()
1159 && let Some(vp) = waves.viewports.first()
1160 {
1161 let time_offset = waves.time_offset();
1162 waves.cursor = if next {
1163 Some(vp.left_edge_time(&max_timestamp, time_offset))
1164 } else {
1165 Some(vp.right_edge_time(&max_timestamp, time_offset))
1166 };
1167 }
1168 waves.set_cursor_at_transition(next, variable, skip_zero);
1169 let moved = waves.go_to_cursor_if_not_in_view();
1170 if moved {
1171 self.invalidate_draw_commands();
1172 }
1173 } else {
1174 warn!(
1175 "Move cursor to transition: No timestamps count, even though waveforms should be loaded"
1176 );
1177 }
1178 }
1179 Message::MoveTransaction { next } => {
1180 let undo_msg = if next {
1181 "Move to next transaction"
1182 } else {
1183 "Move to previous transaction"
1184 };
1185 self.save_current_canvas(undo_msg.to_string());
1186 let waves = self.user.waves.as_mut()?;
1187 waves.move_to_transaction(next)?;
1188 self.invalidate_draw_commands();
1189 }
1190 Message::ResetVariableFormat(displayed_field_ref) => {
1191 let waves = self.user.waves.as_mut()?;
1192 if let Some(DisplayedItem::Variable(displayed_variable)) =
1193 waves.displayed_items.get_mut(&displayed_field_ref.item)
1194 {
1195 if displayed_field_ref.field.is_empty() {
1196 displayed_variable.format = None;
1197 } else {
1198 displayed_variable
1199 .field_formats
1200 .retain(|ff| ff.field != displayed_field_ref.field);
1201 }
1202 self.invalidate_draw_commands();
1203 }
1204 }
1205 Message::CursorSet(time) => {
1206 let waves = self.user.waves.as_mut()?;
1207 waves.cursor = Some(time);
1208 }
1209 Message::ExpandParameterSection => {
1210 self.expand_parameter_section = true;
1211 }
1212 Message::LoadFile(filename, load_options) => {
1213 self.user.selected_server_file_index = None;
1214 *self.surver_selected_file.borrow_mut() = None;
1215 #[cfg(not(target_arch = "wasm32"))]
1216 self.load_from_file(filename, load_options).ok();
1217 #[cfg(target_arch = "wasm32")]
1218 error!("Cannot load file from path in WASM");
1219 }
1220 Message::LoadWaveformFileFromUrl(url, load_options) => {
1221 self.user.selected_server_file_index = None;
1222 *self.surver_selected_file.borrow_mut() = None;
1223 self.load_wave_from_url(url, load_options, true, None);
1225 }
1226 Message::LoadFromData(data, load_options) => {
1227 self.user.selected_server_file_index = None;
1228 *self.surver_selected_file.borrow_mut() = None;
1229 self.load_from_data(data, load_options).ok();
1230 }
1231 #[cfg(feature = "python")]
1232 Message::LoadPythonTranslator(filename) => {
1233 try_log_error!(
1234 self.translators.load_python_translator(filename),
1235 "Error loading Python translator",
1236 )
1237 }
1238 #[cfg(all(not(target_arch = "wasm32"), feature = "wasm_plugins"))]
1239 Message::LoadWasmTranslator(path) => {
1240 let sender = self.channels.msg_sender.clone();
1241 let max_memory_mib = self.user.config.plugin.max_memory_mib;
1242 perform_work(move || {
1243 match PluginTranslator::new(path.into_std_path_buf(), max_memory_mib) {
1244 Ok(t) => {
1245 checked_send(&sender, Message::TranslatorLoaded(Arc::new(t)));
1246 }
1247 Err(e) => {
1248 error!("Failed to load wasm translator {e:#}");
1249 }
1250 }
1251 });
1252 }
1253 Message::LoadCommandFile(path) => {
1254 self.add_batch_commands(read_command_file(&path));
1255 }
1256 Message::LoadCommandFileFromUrl(url) => {
1257 self.load_commands_from_url(url);
1258 }
1259 Message::LoadCommandFromData(bytes) => {
1260 self.add_batch_commands(read_command_bytes(bytes));
1261 }
1262 Message::SetupCxxrtl(kind) => self.connect_to_cxxrtl(kind, false),
1263 Message::SetSurverStatus(_start, server, status) => {
1264 self.user.surver_file_infos = Some(status.file_infos.clone());
1265 info!(
1266 "Received surfer server status from {server}. {} files available.",
1267 status.file_infos.len()
1268 );
1269 self.user.surver_url = Some(server.clone());
1270 if status.file_infos.is_empty() {
1271 warn!("Received surfer server status with no file infos");
1272 return None;
1273 }
1274 if self.user.selected_server_file_index.is_none() {
1275 if status.file_infos.len() == 1 {
1276 info!(
1278 "Only one file available on server {}, loading it automatically",
1279 server
1280 );
1281 self.load_wave_from_url(server.clone(), LoadOptions::Clear, false, Some(0));
1282 } else {
1283 self.user.show_server_file_window = true;
1285 self.progress_tracker = None;
1286 }
1287 }
1288
1289 if let Some(file_index) = self.user.selected_server_file_index {
1290 if file_index >= status.file_infos.len() {
1291 warn!(
1292 "Selected server file index {file_index} is out of bounds ({} files available)",
1293 status.file_infos.len()
1294 );
1295 return None;
1296 }
1297 self.server_status_to_progress(&server, &status.file_infos[file_index]);
1298 }
1299 }
1300 Message::FileDropped(dropped_file) => {
1301 self.load_from_dropped(dropped_file)
1302 .map_err(|e| error!("{e:#?}"))
1303 .ok();
1304 }
1305 Message::StopProgressTracker => {
1306 self.progress_tracker = None;
1307 }
1308 Message::WaveHeaderLoaded(start, source, load_options, header) => {
1309 info!(
1311 "Loaded the hierarchy and meta-data of {source} in {:?}",
1312 start.elapsed()
1313 );
1314 match header {
1315 HeaderResult::LocalFile(header) => {
1316 let shared_hierarchy = Arc::new(header.hierarchy);
1318 let new_waves =
1319 Box::new(WaveContainer::new_waveform(shared_hierarchy.clone()));
1320 self.on_waves_loaded(
1321 source.clone(),
1322 convert_format(header.file_format),
1323 new_waves,
1324 load_options,
1325 );
1326 self.load_wave_body(source, header.body, header.body_len, shared_hierarchy);
1328 }
1329 HeaderResult::LocalBytes(header) => {
1330 let shared_hierarchy = Arc::new(header.hierarchy);
1332 let new_waves =
1333 Box::new(WaveContainer::new_waveform(shared_hierarchy.clone()));
1334 self.on_waves_loaded(
1335 source.clone(),
1336 convert_format(header.file_format),
1337 new_waves,
1338 load_options,
1339 );
1340 self.load_wave_body(source, header.body, header.body_len, shared_hierarchy);
1342 }
1343 HeaderResult::Remote(hierarchy, file_format, server, file_index) => {
1344 let new_waves = Box::new(WaveContainer::new_remote_waveform(
1346 &server,
1347 hierarchy.clone(),
1348 file_index,
1349 ));
1350 self.on_waves_loaded(
1351 source.clone(),
1352 convert_format(file_format),
1353 new_waves,
1354 load_options,
1355 );
1356 get_time_table_from_server(
1358 self.channels.msg_sender.clone(),
1359 server,
1360 file_index,
1361 );
1362 }
1363 }
1364 }
1365 Message::WaveBodyLoaded(start, source, body) => {
1366 info!("Loaded the body of {source} in {:?}", start.elapsed());
1368 self.progress_tracker = None;
1369 let enable_time_offset = self.enable_time_offset();
1370 let waves = self
1371 .user
1372 .waves
1373 .as_mut()
1374 .expect("Waves should be loaded at this point!");
1375 let maybe_cmd = waves .inner
1378 .as_waves_mut()?
1379 .wellen_add_body(body)
1380 .map_err(|err| {
1381 error!("While getting commands to lazy-load signals: {err:?}");
1382 })
1383 .ok()
1384 .flatten();
1385 let param_cmd = waves
1387 .inner
1388 .as_waves_mut()?
1389 .load_parameters()
1390 .map_err(|err| {
1391 error!("While getting commands to lazy-load parameters: {err:?}");
1392 })
1393 .ok()
1394 .flatten();
1395
1396 if self.wcp_greeted_signal.load(Ordering::Relaxed)
1397 && self.wcp_client_capabilities.waveforms_loaded
1398 {
1399 let source = match source {
1400 WaveSource::File(path) => path.to_string(),
1401 WaveSource::Url(url) => url,
1402 _ => String::new(),
1403 };
1404 self.channels.wcp_s2c_sender.as_ref().map(|ch| {
1405 block_on(
1406 ch.send(WcpSCMessage::event(WcpEvent::waveforms_loaded { source })),
1407 )
1408 });
1409 }
1410
1411 waves.refresh_time_offset(enable_time_offset);
1413 waves.update_viewports();
1415 self.invalidate_draw_commands();
1417 if let Some(cmd) = param_cmd {
1419 self.load_variables(cmd);
1420 }
1421 if let Some(cmd) = maybe_cmd {
1423 self.load_variables(cmd);
1424 }
1425 }
1426 Message::SignalsLoaded(start, res) => {
1427 info!("Loaded {} variables in {:?}", res.len(), start.elapsed());
1428 self.progress_tracker = None;
1429 let waves = self
1430 .user
1431 .waves
1432 .as_mut()
1433 .expect("Waves should be loaded at this point!");
1434 match waves.inner.as_waves_mut()?.on_signals_loaded(res) {
1435 Err(err) => error!("{err:?}"),
1436 Ok(Some(cmd)) => self.load_variables(cmd),
1437 _ => {}
1438 }
1439 self.invalidate_draw_commands();
1441 }
1442 Message::WavesLoaded(filename, format, new_waves, load_options) => {
1443 self.on_waves_loaded(filename, format, new_waves, load_options);
1444 let enable_time_offset = self.enable_time_offset();
1446 let waves = self
1447 .user
1448 .waves
1449 .as_mut()
1450 .expect("Waves should be loaded at this point!");
1451 waves.refresh_time_offset(enable_time_offset);
1452 waves.update_viewports();
1453 self.progress_tracker = None;
1454 }
1455 Message::TransactionStreamsLoaded(filename, format, new_ftr, loaded_options) => {
1456 self.on_transaction_streams_loaded(filename, format, new_ftr, loaded_options);
1457 let enable_time_offset = self.enable_time_offset();
1458 let waves = self
1459 .user
1460 .waves
1461 .as_mut()
1462 .expect("Waves should be loaded at this point!");
1463 waves.refresh_time_offset(enable_time_offset);
1464 waves.update_viewports();
1465 }
1466 Message::BlacklistTranslator(idx, translator) => {
1467 self.user.blacklisted_translators.insert((idx, translator));
1468 }
1469 Message::Error(e) => {
1470 error!("{e:?}");
1471 self.user.show_logs = true;
1472 }
1473 Message::TranslatorLoaded(t) => {
1474 info!("Translator {} loaded", t.name());
1475 t.set_wave_source(
1476 self.user
1477 .waves
1478 .as_ref()
1479 .map(|waves| waves.source.into_translation_type()),
1480 );
1481
1482 self.translators.add_or_replace(AnyTranslator::Full(t));
1483 }
1484 Message::SetSidePanelVisible(v) => self.user.show_hierarchy = Some(v),
1485 Message::SetMenuVisible(v) => self.user.show_menu = Some(v),
1486 Message::ToggleMenu => {
1487 self.user.show_menu = Some(!self.show_menu());
1488 }
1489 Message::SetToolbarVisible(v) => self.user.show_toolbar = Some(v),
1490 Message::SetToolbarGroupEnabled(group_id, enabled) => {
1491 self.user
1492 .toolbar_group_enabled
1493 .insert(group_id, Some(enabled));
1494 }
1495 Message::SetToolbarGroupRow(group_id, row) => {
1496 self.set_toolbar_group_row(&group_id, row);
1497 }
1498 Message::SetShowEmptyScopes(v) => self.user.show_empty_scopes = Some(v),
1499 Message::SetShowHierarchyIcons(v) => self.user.show_hierarchy_icons = Some(v),
1500 Message::SetParameterDisplayLocation(location) => {
1501 self.user.parameter_display_location = Some(location);
1502 }
1503 Message::SetStatusbarVisible(v) => self.user.show_statusbar = Some(v),
1504 Message::SetTickLines(v) => self.user.show_ticks = Some(v),
1505 Message::SetVariableTooltip(v) => self.user.show_tooltip = Some(v),
1506 Message::SetScopeTooltip(v) => self.user.show_scope_tooltip = Some(v),
1507 Message::SetOverviewVisible(v) => self.user.show_overview = Some(v),
1508 Message::SetShowVariableDirection(v) => self.user.show_variable_direction = Some(v),
1509 Message::SetTransitionValue(v) => self.user.transition_value = Some(v),
1510 Message::SetDrawVectorUnknownsAsLine(v) => {
1511 self.user.draw_vector_unknowns_as_line = Some(v);
1512 }
1513 Message::SetFocusHighlight(focus_highlight) => {
1514 self.user.focus_highlight = Some(focus_highlight);
1515 }
1516 Message::SetShowIndices(v) => {
1517 let new = v;
1518 self.user.show_variable_indices = Some(new);
1519 let waves = self.user.waves.as_mut()?;
1520 waves.display_variable_indices = new;
1521 waves.compute_variable_display_names();
1522 }
1523 Message::HideCommandPrompt => {
1524 *self.command_prompt_text.borrow_mut() = String::new();
1525 self.command_prompt.suggestions = vec![];
1526 self.command_prompt.selected = self.command_prompt.previous_commands.len();
1527 self.command_prompt.visible = false;
1528 }
1529 Message::ShowCommandPrompt(text, selected) => {
1530 self.command_prompt.new_text = Some((text, selected.unwrap_or(String::new())));
1531 self.command_prompt.visible = true;
1532 }
1533 Message::FileDownloaded(url, bytes, load_options) => {
1534 self.load_from_bytes(WaveSource::Url(url), bytes.to_vec(), load_options);
1535 }
1536 Message::CommandFileDownloaded(_url, bytes) => {
1537 self.add_batch_commands(read_command_bytes(bytes.to_vec()));
1538 self.progress_tracker = None;
1539 }
1540 Message::SetConfigFromString(s) => {
1541 let config = SurferConfig::new_from_toml(&s)
1543 .with_context(|| "Failed to load config file")
1544 .ok()?;
1545
1546 self.user.config = config;
1547
1548 let enable_time_offset = self.enable_time_offset();
1550 if let Some(waves) = &mut self.user.waves {
1551 waves.refresh_time_offset(enable_time_offset);
1552 }
1553
1554 let ctx = &self.context.as_ref()?;
1555 ctx.set_visuals(self.get_visuals());
1556 }
1557 Message::ReloadConfig => {
1558 let config = SurferConfig::new(false)
1560 .with_context(|| "Failed to load config file")
1561 .ok()?;
1562 self.translators = all_translators();
1563 self.user.config = config;
1564
1565 let enable_time_offset = self.enable_time_offset();
1567 if let Some(waves) = &mut self.user.waves {
1568 waves.refresh_time_offset(enable_time_offset);
1569 }
1570
1571 let ctx = &self.context.as_ref()?;
1572 ctx.set_visuals(self.get_visuals());
1573 }
1574 Message::ReloadWaveform(keep_unavailable) => {
1575 let waves = self.user.waves.as_ref()?;
1576 let options = if keep_unavailable {
1577 LoadOptions::KeepAll
1578 } else {
1579 LoadOptions::KeepAvailable
1580 };
1581 match &waves.source {
1582 WaveSource::File(filename) => {
1583 self.load_from_file(filename.clone(), options).ok();
1584 }
1585 WaveSource::Data => {} WaveSource::Cxxrtl(..) => {} WaveSource::DragAndDrop(filename) => {
1588 filename
1589 .clone()
1590 .and_then(|filename| self.load_from_file(filename, options).ok());
1591 }
1592 WaveSource::Url(url) => {
1593 self.load_wave_from_url(
1594 url.clone(),
1595 options,
1596 false,
1597 self.user.selected_server_file_index,
1598 );
1599 }
1600 }
1601
1602 for translator in self.translators.all_translators() {
1603 translator.reload(self.channels.msg_sender.clone());
1604 }
1605 self.variable_name_info_cache.borrow_mut().clear();
1606 self.translator_generation += 1;
1607
1608 if let Some(waves) = self.user.waves.as_mut() {
1609 waves.compute_variable_display_names();
1610 }
1611 }
1612 Message::SuggestReloadWaveform => match self.autoreload_files() {
1613 AutoLoad::Always => self.update(Message::ReloadWaveform(true))?,
1614 AutoLoad::Never => (),
1615 AutoLoad::Ask => {
1616 self.user.show_reload_suggestion = Some(ReloadWaveformDialog::default());
1617 }
1618 },
1619 Message::CloseReloadWaveformDialog {
1620 reload_file,
1621 do_not_show_again,
1622 } => {
1623 if do_not_show_again {
1624 self.user.autoreload_files = Some(AutoLoad::from_bool(reload_file));
1627 }
1628 self.user.show_reload_suggestion = None;
1629 if reload_file {
1630 self.update(Message::ReloadWaveform(true));
1631 }
1632 }
1633 Message::UpdateReloadWaveformDialog(dialog) => {
1634 self.user.show_reload_suggestion = Some(dialog);
1635 }
1636 Message::OpenSiblingStateFile(open) => {
1637 if !open {
1638 return None;
1639 }
1640 let waves = self.user.waves.as_ref()?;
1641 let state_file_path = waves.source.sibling_state_file()?;
1642 self.load_state_file(Some(state_file_path.clone().into_std_path_buf()));
1643 }
1644 Message::SuggestOpenSiblingStateFile => match self.autoload_sibling_state_files() {
1645 AutoLoad::Always => {
1646 self.update(Message::OpenSiblingStateFile(true));
1647 }
1648 AutoLoad::Never => {}
1649 AutoLoad::Ask => {
1650 self.user.show_open_sibling_state_file_suggestion =
1651 Some(OpenSiblingStateFileDialog::default());
1652 }
1653 },
1654 Message::CloseOpenSiblingStateFileDialog {
1655 load_state,
1656 do_not_show_again,
1657 } => {
1658 if do_not_show_again {
1659 self.user.autoload_sibling_state_files = Some(AutoLoad::from_bool(load_state));
1660 }
1661 self.user.show_open_sibling_state_file_suggestion = None;
1662 if load_state {
1663 self.update(Message::OpenSiblingStateFile(true));
1664 }
1665 }
1666 Message::UpdateOpenSiblingStateFileDialog(dialog) => {
1667 self.user.show_open_sibling_state_file_suggestion = Some(dialog);
1668 }
1669 Message::RemovePlaceholders => {
1670 let waves = self.user.waves.as_mut()?;
1671 waves.remove_placeholders();
1672 }
1673 Message::SetClockHighlightType(new_type) => {
1674 self.user.clock_highlight_type = Some(new_type);
1675 self.invalidate_draw_commands();
1676 }
1677 Message::SetFillHighValues(fill) => self.user.fill_high_values = Some(fill),
1678 Message::SetTraceStyle(trace_style) => {
1679 self.user.trace_style = Some(trace_style);
1680 self.invalidate_draw_commands();
1681 }
1682 Message::ResolveMarkerSet { name, time } => {
1683 let marker_id = self.user.waves.as_ref()?.resolve_marker_name(&name);
1684 let msg = match marker_id {
1685 Some(id) => Message::SetMarker { id, time },
1686 None => Message::AddMarker {
1687 time,
1688 name: Some(name),
1689 move_focus: true,
1690 },
1691 };
1692 self.update(msg);
1693 }
1694 Message::AddMarker {
1695 time,
1696 name,
1697 move_focus,
1698 } => {
1699 if let Some(name) = &name {
1700 self.save_current_canvas(format!("Add marker {name} at {time}"));
1701 } else {
1702 self.save_current_canvas(format!("Add marker at {time}"));
1703 }
1704 let waves = self.user.waves.as_mut()?;
1705 waves.add_marker(&time, name, move_focus);
1706 }
1707 Message::SetMarker { id, time } => {
1708 self.save_current_canvas(format!("Set marker {id} to {time}"));
1709 let waves = self.user.waves.as_mut()?;
1710 waves.set_marker_position(id, &time);
1711 }
1712 Message::ResolveMarkerRemove(name) => {
1713 if let Some(id) = self.user.waves.as_ref()?.resolve_marker_name(&name) {
1714 self.update(Message::RemoveMarker(id));
1715 }
1716 }
1717 Message::RemoveMarker(id) => {
1718 let waves = self.user.waves.as_mut()?;
1719 waves.remove_marker(id);
1720 }
1721 Message::MoveMarkerToCursor(idx) => {
1722 self.save_current_canvas("Move marker".into());
1723 let waves = self.user.waves.as_mut()?;
1724 waves.move_marker_to_cursor(idx);
1725 }
1726 Message::GoToCursorIfNotInView => {
1727 let waves = self.user.waves.as_mut()?;
1728 if waves.go_to_cursor_if_not_in_view() {
1729 self.invalidate_draw_commands();
1730 }
1731 }
1732 Message::GoToMarkerPosition(idx, viewport_idx) => {
1733 let waves = self.user.waves.as_mut()?;
1734 if let Some(max_timestamp) = waves.max_timestamp() {
1736 let cursor = waves.markers.get(&idx)?;
1737 waves.viewports[viewport_idx].go_to_time(
1738 cursor,
1739 &max_timestamp,
1740 &waves.cached_time_offset,
1741 );
1742 self.invalidate_draw_commands();
1743 } else {
1744 warn!(
1745 "Go to marker position: No timestamps count, even though waveforms should be loaded"
1746 );
1747 }
1748 }
1749 Message::ChangeVariableNameType(target, name_type) => {
1750 let waves = self.user.waves.as_mut()?;
1751 let recompute_names = waves.change_variable_name_type(target, name_type);
1752
1753 if recompute_names {
1754 waves.compute_variable_display_names();
1755 }
1756 }
1757 Message::ForceVariableNameTypes(name_type) => {
1758 let waves = self.user.waves.as_mut()?;
1759 waves.force_variable_name_type(name_type);
1760 }
1761 Message::CommandPromptClear => {
1762 *self.command_prompt_text.borrow_mut() = String::new();
1763 self.command_prompt.suggestions = vec![];
1764 self.command_prompt.selected = if self.command_prompt_text.borrow().is_empty() {
1766 self.command_prompt.previous_commands.len().clamp(0, 3)
1767 } else {
1768 0
1769 };
1770 }
1771 Message::CommandPromptUpdate { suggestions } => {
1772 self.command_prompt.suggestions = suggestions;
1773 self.command_prompt.selected = if self.command_prompt_text.borrow().is_empty() {
1774 self.command_prompt.previous_commands.len().clamp(0, 3)
1775 } else {
1776 0
1777 };
1778 self.command_prompt.new_selection =
1779 Some(if self.command_prompt_text.borrow().is_empty() {
1780 self.command_prompt.previous_commands.len().clamp(0, 3)
1781 } else {
1782 0
1783 });
1784 }
1785 Message::CommandPromptPushPrevious(cmd) => {
1786 let len = cmd.len();
1787 self.command_prompt
1788 .previous_commands
1789 .insert(0, (cmd, vec![false; len]));
1790 }
1791 Message::OpenFileDialog(mode) => {
1792 self.open_file_dialog(mode);
1793 }
1794 Message::OpenCommandFileDialog => {
1795 self.open_command_file_dialog();
1796 }
1797 #[cfg(feature = "python")]
1798 Message::OpenPythonPluginDialog => {
1799 self.open_python_file_dialog();
1800 }
1801 #[cfg(feature = "python")]
1802 Message::ReloadPythonPlugin => {
1803 try_log_error!(
1804 self.translators.reload_python_translator(),
1805 "Error reloading Python translator"
1806 );
1807 self.translator_generation += 1;
1808 self.invalidate_draw_commands();
1809 }
1810 Message::SaveStateFile(path) => self.save_state_file(path),
1811 Message::LoadStateFromData(bytes) => self.load_state_from_bytes(&bytes),
1812 Message::LoadStateFile(path) => self.load_state_file(path),
1813 Message::LoadState(state, path) => self.load_state(state, path),
1814 Message::SetStateFile(path) => {
1815 #[cfg(not(target_arch = "wasm32"))]
1817 {
1818 self.user.state_file = Some(path);
1819 }
1820 #[cfg(target_arch = "wasm32")]
1821 {
1822 error!("Failed to load {path:?}. Loading state files is unsupported on wasm")
1823 }
1824 }
1825 Message::SetAboutVisible(s) => self.user.show_about = s,
1826 Message::SetKeyHelpVisible(s) => self.user.show_keys = s,
1827 Message::SetGestureHelpVisible(s) => self.user.show_gestures = s,
1828 Message::SetUrlEntryVisible(s, f) => {
1829 self.user.show_url_entry = s;
1830 self.url_callback = f;
1831 }
1832 Message::SetLicenseVisible(s) => self.user.show_license = s,
1833 Message::SetQuickStartVisible(s) => self.user.show_quick_start = s,
1834 Message::SetPerformanceVisible(s) => {
1835 if !s {
1836 self.continuous_redraw = false;
1837 }
1838 self.user.show_performance = s;
1839 }
1840 Message::SetContinuousRedraw(s) => self.continuous_redraw = s,
1841 Message::SetMouseGestureDragStart(pos, time) => {
1842 self.gesture_start_location = pos;
1843 self.gesture_start_time = time;
1844 }
1845 Message::SetMeasureDragStart(pos) => self.measure_start_location = pos,
1846 Message::SetTextEditFocused(id, s) => {
1847 self.text_edit_focused.insert(id, s);
1848 }
1849 Message::SetRequestTextEditFocus(id, s) => {
1850 self.text_edit_request_focus.insert(id, s);
1851 }
1852 Message::ClearAllTextEditFocuses => {
1853 self.text_edit_focused.clear();
1854 self.text_edit_request_focus.clear();
1855 }
1856 Message::SetVariableNameFilterType(variable_name_filter_type) => {
1857 self.user.variable_filter.name_filter_type = variable_name_filter_type;
1858 }
1859 Message::SetVariableNameFilterCaseInsensitive(s) => {
1860 self.user.variable_filter.name_filter_case_insensitive = s;
1861 }
1862 Message::SetVariableIOFilter(t, b) => match t {
1863 VariableIOFilterType::Output => self.user.variable_filter.include_outputs = b,
1864 VariableIOFilterType::Input => self.user.variable_filter.include_inputs = b,
1865 VariableIOFilterType::InOut => self.user.variable_filter.include_inouts = b,
1866 VariableIOFilterType::Other => self.user.variable_filter.include_others = b,
1867 },
1868 Message::SetVariableGroupByDirection(b) => {
1869 self.user.variable_filter.group_by_direction = b;
1870 }
1871 Message::SetUIZoomFactor(scale) => {
1872 if let Some(ctx) = &mut self.context.as_ref() {
1873 ctx.set_zoom_factor(scale);
1874 }
1875 self.user.ui_zoom_factor = Some(scale);
1876 }
1877 Message::SelectPrevCommand => {
1878 self.command_prompt.new_selection = Some(
1879 self.command_prompt
1880 .new_selection
1881 .unwrap_or(self.command_prompt.selected)
1882 .saturating_sub(1),
1883 );
1884 }
1885 Message::SelectNextCommand => {
1886 self.command_prompt.new_selection = Some(
1887 self.command_prompt
1888 .new_selection
1889 .unwrap_or(self.command_prompt.selected)
1890 .saturating_add(1)
1891 .min(self.command_prompt.suggestions.len().saturating_sub(1)),
1892 );
1893 }
1894 Message::SetHierarchyStyle(style) => self.user.hierarchy_style = Some(style),
1895 Message::SetArrowKeyBindings(bindings) => {
1896 self.user.arrow_key_bindings = Some(bindings);
1897 }
1898 Message::SetPrimaryMouseDragBehavior(behavior) => {
1899 self.user.primary_button_drag_behavior = Some(behavior);
1900 }
1901 Message::SetTimeOffsetEnabled(enabled) => {
1902 self.user.enable_time_offset = Some(enabled);
1903 let enable_time_offset = self.enable_time_offset();
1904 if let Some(waves) = &mut self.user.waves {
1905 waves.refresh_time_offset(enable_time_offset);
1906 waves.update_viewports();
1907 }
1908 self.invalidate_draw_commands();
1909 }
1910 Message::InvalidateDrawCommands => self.invalidate_draw_commands(),
1911 Message::UnpauseSimulation => {
1912 let waves = self.user.waves.as_ref()?;
1913 waves.inner.as_waves()?.unpause_simulation();
1914 }
1915 Message::PauseSimulation => {
1916 let waves = self.user.waves.as_ref()?;
1917 waves.inner.as_waves()?.pause_simulation();
1918 }
1919 Message::Batch(messages) => {
1920 for message in messages {
1921 self.update(message);
1922 }
1923 }
1924 Message::AddDraggedVariables(variables) => {
1925 let waves = self.user.waves.as_mut()?;
1926
1927 waves.focused_item = None;
1928 self.user.drag_source_idx = None;
1929 let target = self.user.drag_target_idx.take();
1930
1931 if let (Some(cmd), _) =
1932 waves.add_variables(&self.translators, variables, target, true, false, None)
1933 {
1934 self.load_variables(cmd);
1935 }
1936 self.invalidate_draw_commands();
1937 }
1938 Message::VariableDragStarted(vidx) => {
1939 self.user.drag_started = true;
1940 self.user.drag_source_idx = Some(vidx);
1941 self.user.drag_target_idx = None;
1942 }
1943 Message::VariableDragTargetChanged(position) => {
1944 self.user.drag_target_idx = Some(position);
1945 }
1946 Message::VariableDragFinished => {
1947 self.user.drag_started = false;
1948
1949 let source_vidx = self.user.drag_source_idx.take()?;
1950 let target_position = self.user.drag_target_idx.take()?;
1951
1952 self.save_current_canvas("Drag item".to_string());
1954 self.invalidate_draw_commands();
1955 let waves = self.user.waves.as_mut()?;
1956
1957 let focused_index = waves
1958 .focused_item
1959 .and_then(|vidx| waves.items_tree.to_displayed(vidx));
1960 let focused_item_ref = focused_index
1961 .and_then(|idx| waves.items_tree.get(idx))
1962 .map(|node| node.item_ref);
1963
1964 let mut to_move = waves
1965 .items_tree
1966 .iter_visible_extra()
1967 .filter_map(|info| info.node.selected.then_some(info.idx))
1968 .collect::<Vec<_>>();
1969 if let Some(idx) = focused_index {
1970 to_move.push(idx);
1971 }
1972 if let Some(vidx) = waves.items_tree.to_displayed(source_vidx) {
1973 to_move.push(vidx);
1974 }
1975
1976 let _ = waves.items_tree.move_items(to_move, target_position);
1977
1978 waves.focused_item = focused_item_ref
1979 .and_then(|item_ref| {
1980 waves
1981 .items_tree
1982 .iter_visible()
1983 .position(|node| node.item_ref == item_ref)
1984 })
1985 .map(VisibleItemIndex);
1986 }
1987 Message::VariableValueToClipbord(vidx) => {
1988 self.handle_variable_clipboard_operation(
1989 vidx,
1990 |waves, item_ref: DisplayedItemRef| {
1991 if let Some(DisplayedItem::Variable(_)) =
1992 waves.displayed_items.get(&item_ref)
1993 {
1994 let field_ref = item_ref.into();
1995 self.get_variable_value(
1996 waves,
1997 &field_ref,
1998 waves
1999 .cursor
2000 .as_ref()
2001 .and_then(num::BigInt::to_biguint)
2002 .as_ref(),
2003 )
2004 } else {
2005 None
2006 }
2007 },
2008 );
2009 }
2010 Message::VariableNameToClipboard(vidx) => {
2011 self.handle_variable_clipboard_operation(
2012 vidx,
2013 |waves, item_ref: DisplayedItemRef| {
2014 if let Some(DisplayedItem::Variable(variable)) =
2015 waves.displayed_items.get(&item_ref)
2016 {
2017 Some(variable.variable_ref.name.clone())
2018 } else {
2019 None
2020 }
2021 },
2022 );
2023 }
2024 Message::VariableFullNameToClipboard(vidx) => {
2025 self.handle_variable_clipboard_operation(
2026 vidx,
2027 |waves, item_ref: DisplayedItemRef| {
2028 if let Some(DisplayedItem::Variable(variable)) =
2029 waves.displayed_items.get(&item_ref)
2030 {
2031 Some(variable.variable_ref.full_path_string())
2032 } else {
2033 None
2034 }
2035 },
2036 );
2037 }
2038 Message::SetViewportStrategy(s) => {
2039 if let Some(waves) = &mut self.user.waves {
2040 for vp in &mut waves.viewports {
2041 vp.move_strategy = s;
2042 }
2043 }
2044 }
2045 Message::Undo(count) => {
2046 let waves = self.user.waves.as_mut()?;
2047 for _ in 0..count {
2048 if let Some(prev_state) = self.undo_stack.pop() {
2049 self.redo_stack
2050 .push(SystemState::current_canvas_state(waves, prev_state.message));
2051 waves.focused_item = prev_state.focused_item;
2052 waves.focused_transaction = prev_state.focused_transaction;
2053 waves.items_tree = prev_state.items_tree;
2054 waves.displayed_items = prev_state.displayed_items;
2055 waves.markers = prev_state.markers;
2056 waves.annotations = prev_state.annotations;
2057 waves.annotation_groups = prev_state.annotation_group;
2058 waves.annotation_list_visible = prev_state.annotation_list;
2059 waves.selected_annotation = prev_state.selected_annotation;
2060 waves.annotation_counter = prev_state.annotation_counter;
2061 } else {
2062 break;
2063 }
2064 }
2065 self.invalidate_draw_commands();
2066 }
2067 Message::Redo(count) => {
2068 let waves = self.user.waves.as_mut()?;
2069 for _ in 0..count {
2070 if let Some(prev_state) = self.redo_stack.pop() {
2071 self.undo_stack
2072 .push(SystemState::current_canvas_state(waves, prev_state.message));
2073 waves.focused_item = prev_state.focused_item;
2074 waves.focused_transaction = prev_state.focused_transaction;
2075 waves.items_tree = prev_state.items_tree;
2076 waves.displayed_items = prev_state.displayed_items;
2077 waves.markers = prev_state.markers;
2078 waves.annotations = prev_state.annotations;
2079 waves.annotation_groups = prev_state.annotation_group;
2080 waves.annotation_list_visible = prev_state.annotation_list;
2081 waves.selected_annotation = prev_state.selected_annotation;
2082 waves.annotation_counter = prev_state.annotation_counter;
2083 } else {
2084 break;
2085 }
2086 }
2087 self.invalidate_draw_commands();
2088 }
2089 Message::DumpTree => {
2090 let waves = self.user.waves.as_ref()?;
2091 dump_tree(waves);
2092 }
2093 Message::GroupNew {
2094 name,
2095 before,
2096 items,
2097 } => {
2098 self.save_current_canvas(format!(
2099 "Create group {}",
2100 name.clone().unwrap_or(String::new())
2101 ));
2102 self.invalidate_draw_commands();
2103 let waves = self.user.waves.as_mut()?;
2104
2105 let passed_or_focused = before
2106 .and_then(|before| {
2107 waves
2108 .items_tree
2109 .get(before)
2110 .map(|node| node.level)
2111 .map(|level| TargetPosition { before, level })
2112 })
2113 .or_else(|| waves.insert_position(waves.focused_item));
2114 let final_target = passed_or_focused.unwrap_or_else(|| waves.end_insert_position());
2115
2116 let mut item_refs = items.unwrap_or_else(|| {
2117 waves
2118 .items_tree
2119 .iter_visible_selected()
2120 .map(|node| node.item_ref)
2121 .collect::<Vec<_>>()
2122 });
2123
2124 let item_refs = if before.is_none() & passed_or_focused.is_some() {
2126 let focus_index = waves
2127 .items_tree
2128 .to_displayed(waves.focused_item.expect("Inconsistent state"))
2129 .expect("Inconsistent state");
2130 item_refs.push(
2131 waves
2132 .items_tree
2133 .get(focus_index)
2134 .expect("Inconsistent state")
2135 .item_ref,
2136 );
2137 item_refs
2138 } else {
2139 item_refs
2140 };
2141
2142 if item_refs.is_empty() {
2143 return None;
2144 }
2145
2146 let group_ref =
2147 waves.add_group(name.unwrap_or("Group".to_owned()), Some(final_target));
2148
2149 let item_idxs = waves
2150 .items_tree
2151 .iter()
2152 .enumerate()
2153 .filter_map(|(idx, node)| {
2154 item_refs
2155 .contains(&node.item_ref)
2156 .then_some(crate::displayed_item_tree::ItemIndex(idx))
2157 })
2158 .collect::<Vec<_>>();
2159
2160 if let Err(e) = waves.items_tree.move_items(
2161 item_idxs,
2162 crate::displayed_item_tree::TargetPosition {
2163 before: ItemIndex(final_target.before.0 + 1),
2164 level: final_target.level.saturating_add(1),
2165 },
2166 ) {
2167 dump_tree(waves);
2168 error!("failed to move items into group: {e:?}");
2169 }
2170 waves.items_tree.xselect_all_visible(false);
2171 waves.focused_item = waves
2172 .items_tree
2173 .iter_visible_extra()
2174 .find_map(|info| (info.node.item_ref == group_ref).then_some(info.vidx));
2175 }
2176 Message::GroupDissolve(item_ref) => {
2177 self.save_current_canvas("Dissolve group".to_owned());
2178 self.invalidate_draw_commands();
2179 let waves = self.user.waves.as_mut()?;
2180 let item_index = waves.index_for_ref_or_focus(item_ref)?;
2181
2182 let removed = waves.items_tree.remove_dissolve(item_index);
2183 waves.displayed_items.remove(&removed);
2184 }
2185 Message::GroupFold(item_ref)
2186 | Message::GroupUnfold(item_ref)
2187 | Message::GroupFoldRecursive(item_ref)
2188 | Message::GroupUnfoldRecursive(item_ref) => {
2189 let unfold = matches!(
2190 message,
2191 Message::GroupUnfold(..) | Message::GroupUnfoldRecursive(..)
2192 );
2193 let recursive = matches!(
2194 message,
2195 Message::GroupFoldRecursive(..) | Message::GroupUnfoldRecursive(..)
2196 );
2197
2198 let undo_msg = if unfold {
2199 "Unfold group".to_owned()
2200 } else {
2201 "Fold group".to_owned()
2202 } + &(if recursive {
2203 " recursive".to_owned()
2204 } else {
2205 String::new()
2206 });
2207 self.save_current_canvas(undo_msg);
2209 self.invalidate_draw_commands();
2210
2211 let waves = self.user.waves.as_mut()?;
2212 let item = waves.index_for_ref_or_focus(item_ref)?;
2213
2214 if let Some(focused_item) = waves.focused_item {
2215 let info = waves
2216 .items_tree
2217 .get_visible_extra(focused_item)
2218 .expect("Inconsistent state");
2219 if waves.items_tree.subtree_contains(item, info.idx) {
2220 waves.focused_item = None;
2221 }
2222 }
2223 if recursive {
2224 waves.items_tree.xfold_recursive(item, unfold);
2225 } else {
2226 waves.items_tree.xfold(item, unfold);
2227 }
2228 }
2229 Message::GroupFoldAll | Message::GroupUnfoldAll => {
2230 let unfold = matches!(message, Message::GroupUnfoldAll);
2231 let undo_msg = if unfold {
2232 "Fold all groups".to_owned()
2233 } else {
2234 "Unfold all groups".to_owned()
2235 };
2236 self.save_current_canvas(undo_msg);
2237 self.invalidate_draw_commands();
2238
2239 let waves = self.user.waves.as_mut()?;
2240
2241 if let Some(focused_item) = waves.focused_item {
2244 let focused_level = waves
2245 .items_tree
2246 .get_visible(focused_item)
2247 .expect("Inconsistent state")
2248 .level;
2249 if !unfold && (focused_level > 0) {
2250 waves.focused_item = None;
2251 }
2252 }
2253 waves.items_tree.xfold_all(unfold);
2254 }
2255 #[cfg(target_arch = "wasm32")]
2256 Message::StartWcpServer { .. } => {
2257 error!("Wcp is not supported on wasm")
2258 }
2259 #[cfg(target_arch = "wasm32")]
2260 Message::StopWcpServer => {
2261 error!("Wcp is not supported on wasm")
2262 }
2263 #[cfg(not(target_arch = "wasm32"))]
2264 Message::StartWcpServer { address, initiate } => {
2265 self.start_wcp_server(address, initiate);
2266 }
2267 #[cfg(not(target_arch = "wasm32"))]
2268 Message::StopWcpServer => {
2269 self.stop_wcp_server();
2270 }
2271 Message::SetupChannelWCP => {
2272 #[cfg(target_arch = "wasm32")]
2273 {
2274 use futures::executor::block_on;
2275 self.channels.wcp_c2s_receiver = block_on(WCP_CS_HANDLER.rx.write()).take();
2276 if self.channels.wcp_c2s_receiver.is_none() {
2277 error!("Failed to claim wasm tx, was SetupWasmWCP executed twice?");
2278 }
2279 self.channels.wcp_s2c_sender = Some(WCP_SC_HANDLER.tx.clone());
2280 }
2281 }
2282 Message::BuildAnalogCache {
2283 display_id,
2284 cache_key,
2285 } => {
2286 let waves = self.user.waves.as_mut()?;
2287 let generation = waves.cache_generation;
2288
2289 let item = waves.displayed_items.get(&display_id)?;
2291 let DisplayedItem::Variable(var) = item else {
2292 return None;
2293 };
2294 if var
2295 .analog
2296 .as_ref()?
2297 .cache
2298 .as_ref()
2299 .is_some_and(|e| e.generation == generation && e.cache_key == cache_key)
2300 {
2301 return None;
2302 }
2303
2304 if let Some(entry) = waves.inflight_caches.get(&cache_key)
2306 && entry.generation == generation
2307 {
2308 if let DisplayedItem::Variable(var) =
2309 waves.displayed_items.get_mut(&display_id)?
2310 {
2311 var.analog.as_mut()?.cache = Some(entry.clone());
2312 }
2313 return None; }
2315
2316 let existing = waves
2318 .displayed_items
2319 .values()
2320 .filter_map(|item| match item {
2321 DisplayedItem::Variable(v) => v.analog.as_ref()?.cache.as_ref(),
2322 _ => None,
2323 })
2324 .find(|e| e.cache_key == cache_key && e.generation == generation)
2325 .cloned();
2326
2327 if let Some(entry) = existing {
2328 if let DisplayedItem::Variable(var) =
2329 waves.displayed_items.get_mut(&display_id)?
2330 {
2331 var.analog.as_mut()?.cache = Some(entry);
2332 }
2333 return None; }
2335
2336 let variable_ref = match waves.displayed_items.get(&display_id)? {
2338 DisplayedItem::Variable(v) => v.variable_ref.clone(),
2339 _ => return None,
2340 };
2341
2342 let entry = std::sync::Arc::new(crate::analog_signal_cache::AnalogCacheEntry::new(
2344 cache_key.clone(),
2345 generation,
2346 ));
2347
2348 if let DisplayedItem::Variable(var) = waves.displayed_items.get_mut(&display_id)? {
2349 var.analog.as_mut()?.cache = Some(entry.clone());
2350 }
2351
2352 let translator = self.translators.clone_translator(&cache_key.1);
2353
2354 waves
2356 .inflight_caches
2357 .insert(cache_key.clone(), entry.clone());
2358
2359 waves.build_analog_cache_async(
2360 entry,
2361 &variable_ref,
2362 translator,
2363 &self.channels.msg_sender,
2364 );
2365 }
2366 Message::AnalogCacheBuilt { entry, result } => {
2367 OUTSTANDING_TRANSACTIONS.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
2368 if let Some(waves) = self.user.waves.as_mut() {
2370 waves.inflight_caches.remove(&entry.cache_key);
2371 }
2372 match result {
2373 Ok(cache) => {
2374 entry.set(cache);
2375 }
2376 Err(err) => {
2377 warn!("Failed to build analog cache: {err}");
2378 }
2379 }
2380 self.invalidate_draw_commands();
2381 }
2382 Message::Exit | Message::ToggleFullscreen => {} Message::AddViewport => {
2384 let waves = self.user.waves.as_mut()?;
2385 let viewport = Viewport::new();
2386 waves.viewports.push(viewport);
2387 self.draw_data.borrow_mut().push(None);
2388 }
2389 Message::RemoveViewport => {
2390 let waves = self.user.waves.as_mut()?;
2391 if waves.viewports.len() > 1 {
2392 waves.viewports.pop();
2393 self.draw_data.borrow_mut().pop();
2394 waves.last_active_viewport_idx = waves
2395 .last_active_viewport_idx
2396 .min(waves.viewports.len() - 1);
2397 }
2398 }
2399 Message::SelectTheme(theme_name) => {
2400 let theme = SurferTheme::new(theme_name)
2401 .with_context(|| "Failed to set theme")
2402 .ok()?;
2403 self.user.config.theme = theme;
2404 let ctx = self.context.as_ref()?;
2405 ctx.set_visuals(self.get_visuals());
2406 }
2407 Message::EnableAnimations(enable) => {
2408 let ctx = self.context.as_ref()?;
2409 self.user.animation_enabled = Some(enable);
2410 ctx.all_styles_mut(|style| {
2411 style.animation_time = if self.animation_enabled() {
2412 self.user.config.animation_time
2413 } else {
2414 0.0
2415 };
2416 });
2417 }
2418 Message::ShowDividerText(show) => {
2419 self.user.config.show_divider_text = show;
2420 }
2421 Message::AsyncDone(_) => (),
2422 Message::AddGraphic(id, g) => {
2423 let waves = self.user.waves.as_mut()?;
2424 waves.graphics.insert(id, g);
2425 }
2426 Message::RemoveGraphic(id) => {
2427 let waves = self.user.waves.as_mut()?;
2428 waves.graphics.retain(|k, _| k != &id);
2429 }
2430 Message::ExpandDrawnItem { item, levels } => {
2431 self.items_to_expand.borrow_mut().push((item, levels));
2432 }
2433 Message::SetMouseGestureAnnotation(annotation_kind) => {
2434 self.annotation_kind = annotation_kind;
2435 }
2436 Message::RectangleAdded {
2437 time_at_start,
2438 time_at_end,
2439 wave_from,
2440 wave_to,
2441 rect,
2442 } => {
2443 let id = self.annotation_id();
2444 self.save_current_canvas(format!("Add rectangle {id:?}"));
2445 let waves = self.user.waves.as_mut()?;
2446 waves.annotation_counter += 1;
2447
2448 let new_rect = Annotation::Rect(RectAnnotation::new(
2449 id,
2450 time_at_start,
2451 time_at_end,
2452 wave_from,
2453 wave_to,
2454 rect,
2455 waves.annotation_counter,
2456 ));
2457 let new_id = new_rect.get_id();
2458 waves.annotations.push(new_rect);
2459
2460 waves.add_annotation_to_group(DEFAULT_GROUP_NAME, new_id);
2461 }
2462 Message::ArrowAdded {
2463 wave_point_from,
2464 wave_point_to,
2465 head_mode,
2466 } => {
2467 let id = self.annotation_id();
2468 self.save_current_canvas(format!("Add arrow {id:?}"));
2469 let waves = self.user.waves.as_mut()?;
2470 waves.annotation_counter += 1;
2471 let new_arrow = Annotation::Arrow(ArrowAnnotation::new(
2472 id,
2473 wave_point_from,
2474 wave_point_to,
2475 head_mode,
2476 waves.annotation_counter,
2477 ));
2478 let new_id = new_arrow.get_id();
2479 waves.annotations.push(new_arrow);
2480
2481 waves.add_annotation_to_group(DEFAULT_GROUP_NAME, new_id);
2482 }
2483
2484 Message::RemoveAnnotation(anno_id) => {
2485 self.save_current_canvas(format!("Removed annotation {anno_id:?}"));
2486 let waves = self.user.waves.as_mut()?;
2487 waves.delete_annotation(anno_id);
2488 waves.remove_annotation_from_group(anno_id);
2489 }
2490
2491 Message::ToggleAnnotationVisiblility(anno_id) => {
2492 self.save_current_canvas(format!("Changed visibility on {anno_id:?}"));
2493 let waves = self.user.waves.as_mut()?;
2494 if let Some(target) = waves.annotations.iter_mut().find(|a| a.get_id() == anno_id) {
2495 target.set_visibility(!target.is_visible());
2496 }
2497 }
2498
2499 Message::ToggleAnnotationListShowComments(anno_id) => {
2500 let waves = self.user.waves.as_mut()?;
2501 if let Some(target) = waves.annotations.iter_mut().find(|a| a.get_id() == anno_id) {
2502 target.set_show_comments(!target.show_comments());
2503 }
2504 }
2505
2506 Message::GoToAnnotationPosition(anno_id, viewport_idx) => {
2507 self.go_to_annotation_position(anno_id, viewport_idx);
2508 }
2509
2510 Message::ToggleAnnotationlistVisibility() => {
2511 let waves = self.user.waves.as_mut()?;
2512 waves.annotation_list_visible = !waves.annotation_list_visible;
2513 self.user.show_annotation_list = waves.annotation_list_visible;
2514 }
2515
2516 Message::CreateAnnotationGroup(name) => {
2517 self.save_current_canvas(format!("Added annotation group {name}"));
2518 let waves = self.user.waves.as_mut()?;
2519
2520 let new_group = AnnotationGroup {
2521 name: name.clone(),
2522 cycle_counter: 0,
2523 annotations: Vec::new(),
2524 };
2525
2526 waves.annotation_groups.push(new_group);
2527 }
2528
2529 Message::DeleteAnnotationGroup(name) => {
2530 self.save_current_canvas(format!("Removed annotation group {name}"));
2531 let waves = self.user.waves.as_mut()?;
2532
2533 waves.delete_group(&name);
2534 }
2535
2536 Message::DeleteAllAnnotationInGroup(name) => {
2537 self.save_current_canvas(format!(
2538 "Removed annotation group {name} and all it's annotations"
2539 ));
2540 let waves = self.user.waves.as_mut()?;
2541
2542 waves.remove_all_annotations_from_group(&name);
2543 }
2544
2545 Message::AddCharToPrompt(c) => *self.char_to_add_to_prompt.borrow_mut() = Some(c),
2546
2547 Message::UpdateAnnotationGroup(anno_id, name) => {
2548 self.save_current_canvas(format!("Added {anno_id:?} to {name:?}"));
2549 let waves = self.user.waves.as_mut()?;
2550
2551 let target = waves.remove_annotation_from_group(anno_id);
2552
2553 match target {
2554 Some(id) => {
2555 waves.add_annotation_to_group(name.as_ref()?, id);
2556 }
2557 None => {
2558 warn!("Error: Just removed non existent id!");
2559 }
2560 }
2561 }
2562
2563 Message::UpdateAnnotationName(anno_id, name) => {
2564 self.save_current_canvas(format!("Changed {anno_id:?} name to {name}"));
2565 let waves = self.user.waves.as_mut()?;
2566 if let Some(target) = waves.annotations.iter_mut().find(|a| a.get_id() == anno_id) {
2567 target.set_name(&name);
2568 }
2569 }
2570
2571 Message::SetGroupVisibility(group, visible) => {
2572 self.save_current_canvas(format!("Changed group {:?} visibility", group.name));
2573 if let Some(waves) = self.user.waves.as_mut() {
2574 for annotation_id in group.annotations {
2575 for annotation in &mut waves.annotations {
2576 if annotation_id == annotation.get_id() {
2577 annotation.set_visibility(visible);
2578 }
2579 }
2580 }
2581 }
2582 }
2583
2584 Message::AnnotationClicked(id, menu_pos, viewport_idx, to_screen, frame_width) => {
2585 if let Some(waves) = self.user.waves.as_mut() {
2586 waves.select_annotation(id);
2587
2588 let max_timestamp = waves.safe_max_timestamp();
2589
2590 let menu_pos_local = to_screen?.inverse().transform_pos(menu_pos?);
2591
2592 let time_offset = waves.time_offset();
2593 let menu_pos_time: BigInt = waves.viewports[viewport_idx?].as_time_bigint(
2594 menu_pos_local.x,
2595 frame_width?,
2596 &max_timestamp,
2597 time_offset,
2598 );
2599
2600 waves.annotation_menu_time = Some(menu_pos_time);
2601
2602 waves.annotation_menu_pos = if id.is_some() { menu_pos } else { None };
2603 }
2604 }
2605
2606 Message::SetActiveViewport(idx) => {
2607 if let Some(waves) = self.user.waves.as_mut() {
2608 let last_idx = waves.viewports.len().checked_sub(1)?;
2609 waves.last_active_viewport_idx = idx.min(last_idx);
2610 }
2611 }
2612
2613 Message::RemoveCommentMessage(annotation_id, message_id) => {
2614 let waves = self.user.waves.as_mut()?;
2616 if let Some(target) = waves
2617 .annotations
2618 .iter_mut()
2619 .find(|a| a.get_id() == annotation_id)
2620 {
2621 target
2622 .get_comment_box_mut()
2623 .message_chain
2624 .retain(|comment_message| comment_message.id != message_id);
2625 }
2626 }
2627
2628 Message::ClickHandled() => {
2629 self.click_handled = true;
2630 }
2631 Message::UpdateCommentBox(changes) => {
2632 let waves = self.user.waves.as_mut()?;
2633 for (annotation_id, comment) in changes {
2634 if let Some(target) = waves
2635 .annotations
2636 .iter_mut()
2637 .find(|a| a.get_id() == annotation_id)
2638 {
2639 target.update_comment_box(comment);
2640 }
2641 }
2642 }
2643 Message::AddCommentMessage(annotation_id, message, user) => {
2644 let waves = self.user.waves.as_mut()?;
2646 if let Some(target) = waves
2647 .annotations
2648 .iter_mut()
2649 .find(|a| a.get_id() == annotation_id)
2650 {
2651 let comment = target.get_comment_box_mut();
2652 comment.message_chain.push(CommentMessage {
2653 id: egui::Id::new(("comment", comment.message_id_source)),
2654 user,
2655 text: message,
2656 });
2657 comment.message_id_source += 1;
2658 }
2659 }
2660 Message::ToggleCommentVisibility(annotation_id) => {
2661 let waves = self.user.waves.as_mut()?;
2662 if let Some(target) = waves
2663 .annotations
2664 .iter_mut()
2665 .find(|a| a.get_id() == annotation_id)
2666 {
2667 target.get_comment_box_mut().visible = !target.get_comment_box().visible;
2668 }
2669 }
2670 }
2671
2672 Some(())
2673 }
2674
2675 pub fn add_scope_as_group(
2676 &mut self,
2677 scope: &ScopeRef,
2678 pos: TargetPosition,
2679 recursive: bool,
2680 variable_name_type: Option<VariableNameType>,
2681 ) -> TargetPosition {
2682 let Some(waves) = self.user.waves.as_mut() else {
2683 return pos;
2684 };
2685 let Some(container) = waves.inner.as_waves() else {
2686 return pos;
2687 };
2688
2689 let variables = container
2690 .variables_in_scope(scope)
2691 .iter()
2692 .sorted_by(|a, b| numeric_sort::cmp(&a.name, &b.name))
2693 .cloned()
2694 .collect_vec();
2695 let child_scopes = container.child_scopes(scope);
2696 let variable_name_type = variable_name_type.or_else(|| {
2697 container
2698 .scope_is_variable(scope)
2699 .then_some(VariableNameType::Local)
2700 });
2701
2702 waves.add_group(scope.name(), Some(pos));
2703 let into_group_pos = TargetPosition {
2704 before: ItemIndex(pos.before.0 + 1),
2705 level: pos.level + 1,
2706 };
2707
2708 let (cmd, variable_refs) = waves.add_variables(
2709 &self.translators,
2710 variables,
2711 Some(into_group_pos),
2712 false,
2713 false,
2714 variable_name_type,
2715 );
2716 let mut into_group_pos = TargetPosition {
2717 before: ItemIndex(into_group_pos.before.0 + variable_refs.len()),
2718 level: pos.level + 1,
2719 };
2720
2721 if let Some(cmd) = cmd {
2722 self.load_variables(cmd);
2723 }
2724
2725 if recursive {
2726 for child in child_scopes.unwrap_or(vec![]) {
2727 into_group_pos =
2728 self.add_scope_as_group(&child, into_group_pos, recursive, variable_name_type);
2729 into_group_pos.level = pos.level + 1;
2730 }
2731 }
2732 into_group_pos
2733 }
2734
2735 fn handle_variable_clipboard_operation<F>(
2736 &self,
2737 vidx: MessageTarget<VisibleItemIndex>,
2738 get_text: F,
2739 ) where
2740 F: FnOnce(&WaveData, DisplayedItemRef) -> Option<String>,
2741 {
2742 let Some(waves) = &self.user.waves else {
2743 return;
2744 };
2745 let vidx = if let MessageTarget::Explicit(vidx) = vidx {
2746 vidx
2747 } else if let Some(focused) = waves.focused_item {
2748 focused
2749 } else {
2750 return;
2751 };
2752 let Some(item_ref) = waves.items_tree.get_visible(vidx).map(|node| node.item_ref) else {
2753 return;
2754 };
2755
2756 if let Some(text) = get_text(waves, item_ref)
2757 && let Some(ctx) = &self.context
2758 {
2759 ctx.copy_text(text);
2760 }
2761 }
2762}
2763
2764fn dump_tree(waves: &WaveData) {
2765 let mut result = String::new();
2766 for (idx, node) in waves.items_tree.iter().enumerate() {
2767 for _ in 0..node.level.saturating_sub(1) {
2768 result.push(' ');
2769 }
2770
2771 if node.level > 0 {
2772 match waves.items_tree.get(ItemIndex(idx + 1)) {
2773 Some(next) if next.level < node.level => result.push_str("╰╴"),
2774 _ => result.push_str("├╴"),
2775 }
2776 }
2777
2778 result.push_str(
2779 &waves
2780 .displayed_items
2781 .get(&node.item_ref)
2782 .map_or("?".to_owned(), displayed_item::DisplayedItem::name),
2783 );
2784 result.push_str(&format!(" ({:?})", node.item_ref));
2785 if node.selected {
2786 result.push_str(" !SEL! ");
2787 }
2788 result.push('\n');
2789 }
2790 info!("tree: \n{}", &result);
2791}
2792
2793pub struct StateWrapper(Arc<RwLock<SystemState>>);
2794impl App for StateWrapper {
2795 fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
2796 App::ui(&mut *self.0.write().unwrap(), ui, frame);
2797 }
2798}