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