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