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