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