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