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