URI: 
       terminal_window.rs - icy_draw - [fork] icy_draw is the successor to mystic draw.
  HTML git clone https://git.drkhsh.at/icy_draw.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
       ---
       terminal_window.rs (31774B)
       ---
            1 #![allow(clippy::float_cmp)]
            2 use eframe::{
            3     egui::{self, CursorIcon, PointerButton},
            4     epaint::Vec2,
            5 };
            6 use egui::{ImageButton, Margin, Modifiers, RichText};
            7 use i18n_embed_fl::fl;
            8 use icy_engine::{Position, Selection, TextPane};
            9 use web_time::Duration;
           10 
           11 use crate::{
           12     icons::{CALL, DOWNLOAD, KEY, LOGOUT, MENU, UPLOAD},
           13     VERSION,
           14 };
           15 
           16 use super::{dialogs, MainWindow, MainWindowMode};
           17 
           18 fn encode_mouse_button(button: i32) -> char {
           19     unsafe { char::from_u32_unchecked(b' '.saturating_add(button as u8) as u32) }
           20 }
           21 fn encode_mouse_position(pos: i32) -> char {
           22     unsafe { char::from_u32_unchecked(b'!'.saturating_add(pos as u8) as u32) }
           23 }
           24 
           25 impl MainWindow {
           26     pub fn update_terminal_window(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame, show_dialing_directory: bool) {
           27         let toolbar_bg_color = ctx.style().visuals.extreme_bg_color;
           28         let button_frame = egui::containers::Frame::none().fill(toolbar_bg_color).inner_margin(Margin::same(6.0));
           29 
           30         let enable_ui = matches!(self.get_mode(), MainWindowMode::ShowTerminal);
           31 
           32         if !self.is_fullscreen_mode {
           33             egui::TopBottomPanel::top("button_bar").frame(button_frame).show(ctx, |ui| {
           34                 if !enable_ui {
           35                     ui.disable();
           36                 }
           37                 ui.horizontal(|ui| {
           38                     let r = ui.add(ImageButton::new(UPLOAD.clone().tint(crate::ui::button_tint(ui)))).on_hover_ui(|ui| {
           39                         ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "terminal-upload")).small());
           40                     });
           41 
           42                     if r.clicked() {
           43                         self.set_mode(ctx, MainWindowMode::SelectProtocol(false));
           44                     }
           45 
           46                     let r = ui.add(ImageButton::new(DOWNLOAD.clone().tint(crate::ui::button_tint(ui)))).on_hover_ui(|ui| {
           47                         ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "terminal-download")).small());
           48                     });
           49 
           50                     if r.clicked() {
           51                         self.set_mode(ctx, MainWindowMode::SelectProtocol(true));
           52                     }
           53                     let mut send_login = false;
           54                     if let Some(auto_login) = &mut self.terminal_thread.lock().auto_login {
           55                         if !auto_login.logged_in {
           56                             let r = ui.add(ImageButton::new(KEY.clone().tint(crate::ui::button_tint(ui)))).on_hover_ui(|ui| {
           57                                 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "terminal-autologin")).small());
           58                             });
           59 
           60                             if r.clicked() {
           61                                 send_login = true;
           62                                 auto_login.logged_in = true;
           63                             }
           64                         }
           65                     }
           66                     if send_login {
           67                         self.send_login();
           68                     }
           69 
           70                     let r: egui::Response = ui.add(ImageButton::new(CALL.clone().tint(crate::ui::button_tint(ui)))).on_hover_ui(|ui| {
           71                         ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "terminal-dialing_directory")).small());
           72                     });
           73 
           74                     if r.clicked() {
           75                         self.set_mode(ctx, MainWindowMode::ShowDialingDirectory);
           76                     }
           77 
           78                     let mut mode = None;
           79                     if let Some(auto_login) = &mut self.terminal_thread.lock().auto_login {
           80                         if auto_login.iemsi.isi.is_some() {
           81                             if self.get_mode() == MainWindowMode::ShowIEMSI {
           82                                 let r: egui::Response = ui.add(egui::Button::new(RichText::new(fl!(crate::LANGUAGE_LOADER, "toolbar-hide-iemsi"))));
           83 
           84                                 if r.clicked() {
           85                                     mode = Some(MainWindowMode::ShowTerminal);
           86                                 }
           87                             } else {
           88                                 let r: egui::Response = ui.add(egui::Button::new(RichText::new(fl!(crate::LANGUAGE_LOADER, "toolbar-show-iemsi"))));
           89 
           90                                 if r.clicked() {
           91                                     mode = Some(MainWindowMode::ShowIEMSI);
           92                                 }
           93                             }
           94                         }
           95                     }
           96 
           97                     if let Some(mode) = mode {
           98                         self.set_mode(ctx, mode);
           99                     }
          100 
          101                     if self.terminal_thread.lock().sound_thread.lock().is_playing() {
          102                         let button_text = match self.terminal_thread.lock().sound_thread.lock().stop_button {
          103                             0 => fl!(crate::LANGUAGE_LOADER, "toolbar-stop-playing1"),
          104                             1 => fl!(crate::LANGUAGE_LOADER, "toolbar-stop-playing2"),
          105                             2 => fl!(crate::LANGUAGE_LOADER, "toolbar-stop-playing3"),
          106                             3 => fl!(crate::LANGUAGE_LOADER, "toolbar-stop-playing4"),
          107                             4 => fl!(crate::LANGUAGE_LOADER, "toolbar-stop-playing5"),
          108                             _ => fl!(crate::LANGUAGE_LOADER, "toolbar-stop-playing6"),
          109                         };
          110 
          111                         let r: egui::Response = ui.add(egui::Button::new(RichText::new(button_text)));
          112                         if r.clicked() {
          113                             self.terminal_thread.lock().sound_thread.lock().clear();
          114                         }
          115                     }
          116 
          117                     if self.terminal_thread.lock().capture_dialog.capture_session {
          118                         let r: egui::Response = ui.add(egui::Button::new(RichText::new(fl!(crate::LANGUAGE_LOADER, "toolbar-stop-capture"))));
          119 
          120                         if r.clicked() {
          121                             self.terminal_thread.lock().capture_dialog.capture_session = false;
          122                         }
          123                     }
          124 
          125                     let size = ui.available_size_before_wrap();
          126                     ui.add_space(size.x - 70.0);
          127 
          128                     let r = ui.add(ImageButton::new(LOGOUT.clone().tint(crate::ui::button_tint(ui)))).on_hover_ui(|ui| {
          129                         ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "terminal-hangup")).small());
          130                     });
          131                     if r.clicked() {
          132                         self.hangup(ctx);
          133                     }
          134                     ui.menu_image_button(MENU.clone().tint(crate::ui::button_tint(ui)), |ui| {
          135                         let r = ui.hyperlink_to(
          136                             fl!(crate::LANGUAGE_LOADER, "menu-item-discuss"),
          137                             "https://github.com/mkrueger/icy_tools/discussions",
          138                         );
          139                         if r.clicked() {
          140                             ui.close_menu();
          141                         }
          142                         let r = ui.hyperlink_to(
          143                             fl!(crate::LANGUAGE_LOADER, "menu-item-report-bug"),
          144                             "https://github.com/mkrueger/icy_tools/issues/new",
          145                         );
          146                         if r.clicked() {
          147                             ui.close_menu();
          148                         }
          149                         let r = ui.hyperlink_to(
          150                             fl!(crate::LANGUAGE_LOADER, "menu-item-check-releases"),
          151                             "https://github.com/mkrueger/icy_tools/releases",
          152                         );
          153                         if r.clicked() {
          154                             ui.close_menu();
          155                         }
          156                         ui.separator();
          157                         #[cfg(not(target_arch = "wasm32"))]
          158                         if ui.button(fl!(crate::LANGUAGE_LOADER, "menu-item-capture-dialog")).clicked() {
          159                             self.set_mode(ctx, MainWindowMode::ShowCaptureDialog);
          160                             ui.close_menu();
          161                         }
          162 
          163                         if ui.button(fl!(crate::LANGUAGE_LOADER, "menu-item-settings")).clicked() {
          164                             self.set_mode(ctx, MainWindowMode::ShowSettings);
          165                             ui.close_menu();
          166                         }
          167                     });
          168                 });
          169             });
          170         }
          171         let frame_no_margins = egui::containers::Frame::none().outer_margin(Margin::same(0.0)).inner_margin(Margin::same(0.0));
          172 
          173         egui::CentralPanel::default().frame(frame_no_margins).show(ctx, |ui| {
          174             if !enable_ui {
          175                 ui.disable();
          176             }
          177             let rect = ui.available_rect_before_wrap();
          178 
          179             self.show_terminal_area(ui);
          180             let msg = if self.show_find_dialog { self.find_dialog.show_ui(ui, rect) } else { None };
          181 
          182             match msg {
          183                 Some(dialogs::find_dialog::Message::ChangePattern(pattern)) => {
          184                     self.find_dialog.pattern = pattern.chars().collect();
          185                     let lock = &mut self.buffer_view.lock();
          186                     let (buffer, _, parser) = lock.get_edit_state_mut().get_buffer_and_caret_mut();
          187                     self.find_dialog.search_pattern(buffer, (*parser).as_ref());
          188                     self.find_dialog.update_pattern(lock);
          189                 }
          190                 Some(dialogs::find_dialog::Message::FindNext) => {
          191                     self.find_dialog.find_next(&mut self.buffer_view.lock());
          192                 }
          193                 Some(dialogs::find_dialog::Message::FindPrev) => {
          194                     self.find_dialog.find_prev(&mut self.buffer_view.lock());
          195                 }
          196                 Some(dialogs::find_dialog::Message::CloseDialog) => {
          197                     self.show_find_dialog = false;
          198                 }
          199                 Some(dialogs::find_dialog::Message::SetCasing(case_sensitive)) => {
          200                     self.find_dialog.case_sensitive = case_sensitive;
          201                     let lock = &mut self.buffer_view.lock();
          202                     let (buffer, _, parser) = lock.get_edit_state_mut().get_buffer_and_caret_mut();
          203                     self.find_dialog.search_pattern(buffer, (*parser).as_ref());
          204                     self.find_dialog.update_pattern(lock);
          205                 }
          206 
          207                 None => {}
          208             }
          209         });
          210 
          211         if show_dialing_directory {
          212             dialogs::dialing_directory_dialog::view_dialing_directory(self, ctx);
          213         }
          214 
          215         let take = self.terminal_thread.lock().auto_transfer.take();
          216         if let Some((protocol_type, download, file_name)) = take {
          217             self.initiate_file_transfer(ctx, protocol_type, download, file_name);
          218         }
          219 
          220         if self.terminal_thread_handle.is_some() && self.terminal_thread_handle.as_ref().unwrap().is_finished() {
          221             let handle = self.terminal_thread_handle.take().unwrap();
          222             if let Err(err) = handle.join() {
          223                 let err = format!("Error update thread crashed: {:?}", err.downcast_ref::<&str>());
          224                 log::error!("{err}");
          225                 self.output_string(&err);
          226             }
          227         }
          228         ctx.request_repaint_after(Duration::from_millis(250));
          229     }
          230 
          231     fn show_terminal_area(&mut self, ui: &mut egui::Ui) {
          232         let mut monitor_settings = self.get_options().monitor_settings.clone();
          233 
          234         monitor_settings.selection_fg = self.screen_mode.get_selection_fg();
          235         monitor_settings.selection_bg = self.screen_mode.get_selection_bg();
          236         /*  if ui.input(|i| i.key_down(egui::Key::W)) {
          237             let enabled = self.buffer_update_thread.lock().enabled;
          238             self.buffer_update_thread.lock().enabled = !enabled;
          239         }*/
          240 
          241         let opt = icy_engine_gui::TerminalOptions {
          242             filter: self.get_options().scaling.get_filter(),
          243             monitor_settings,
          244             stick_to_bottom: true,
          245             use_terminal_height: true,
          246             ..Default::default()
          247         };
          248         let (mut response, calc) = icy_engine_gui::show_terminal_area(ui, self.buffer_view.clone(), opt);
          249         let inner_response = response.context_menu(|ui| terminal_context_menu(ui, self));
          250         if let Some(inner_response) = inner_response {
          251             response = inner_response.response;
          252         }
          253 
          254         if matches!(self.get_mode(), MainWindowMode::ShowTerminal) && ui.is_enabled() && !self.show_find_dialog {
          255             let events = ui.input(|i| i.events.clone());
          256             for e in events {
          257                 match e {
          258                     egui::Event::PointerButton {
          259                         button: PointerButton::Middle,
          260                         pressed: true,
          261                         ..
          262                     } => {
          263                         self.copy_to_clipboard();
          264                     }
          265                     egui::Event::Paste(text) => {
          266                         self.output_string(&text);
          267                     }
          268                     /*egui::Event::CompositionEnd(text) |*/
          269                     egui::Event::Text(text) => {
          270                         for c in text.chars() {
          271                             self.output_char(c);
          272                         }
          273                     }
          274 
          275                     egui::Event::PointerButton {
          276                         pos,
          277                         button,
          278                         pressed: true,
          279                         modifiers,
          280                     } => {
          281                         if calc.buffer_rect.contains(pos - calc.terminal_rect.left_top().to_vec2()) && !calc.vert_scrollbar_rect.contains(pos) {
          282                             let buffer_view = self.buffer_view.clone();
          283                             let click_pos = calc.calc_click_pos(pos);
          284                             let mode: icy_engine::MouseMode = buffer_view.lock().get_buffer().terminal_state.mouse_mode;
          285 
          286                             match mode {
          287                                 icy_engine::MouseMode::VT200 | icy_engine::MouseMode::VT200_Highlight => {
          288                                     let mut modifier_mask = 0;
          289                                     if matches!(button, PointerButton::Secondary) {
          290                                         modifier_mask |= 1;
          291                                     }
          292                                     if modifiers.shift {
          293                                         modifier_mask |= 4;
          294                                     }
          295                                     if modifiers.alt {
          296                                         modifier_mask |= 8;
          297                                     }
          298                                     if modifiers.ctrl || modifiers.mac_cmd {
          299                                         modifier_mask |= 16;
          300                                     }
          301                                     self.output_string(
          302                                         format!(
          303                                             "\x1b[M{}{}{}",
          304                                             encode_mouse_button(modifier_mask),
          305                                             encode_mouse_position(click_pos.x as i32),
          306                                             encode_mouse_position(click_pos.y as i32 - calc.first_line as i32)
          307                                         )
          308                                         .as_str(),
          309                                     );
          310                                 }
          311                                 icy_engine::MouseMode::X10 => {
          312                                     self.output_string(
          313                                         format!(
          314                                             "\x1b[M{}{}{}",
          315                                             encode_mouse_button(0),
          316                                             encode_mouse_position(click_pos.x as i32),
          317                                             encode_mouse_position(click_pos.y as i32)
          318                                         )
          319                                         .as_str(),
          320                                     );
          321                                 }
          322                                 _ => {} /*
          323                                         icy_engine::MouseMode::ButtonEvents => todo!(),
          324                                         icy_engine::MouseMode::AnyEvents => todo!(),
          325                                         icy_engine::MouseMode::FocusEvent => todo!(),
          326                                         icy_engine::MouseMode::AlternateScroll => todo!(),
          327                                         icy_engine::MouseMode::ExtendedMode => todo!(),
          328                                         icy_engine::MouseMode::SGRExtendedMode => todo!(),
          329                                         icy_engine::MouseMode::URXVTExtendedMode => todo!(),
          330                                         icy_engine::MouseMode::PixelPosition => todo!(),*/
          331                             }
          332                         }
          333                     }
          334                     egui::Event::PointerButton {
          335                         pos,
          336                         button: PointerButton::Primary,
          337                         pressed: false,
          338                         modifiers,
          339                         ..
          340                     } => {
          341                         if calc.buffer_rect.contains(pos - calc.terminal_rect.left_top().to_vec2()) && !calc.vert_scrollbar_rect.contains(pos) {
          342                             let mode: icy_engine::MouseMode = self.buffer_view.lock().get_buffer().terminal_state.mouse_mode;
          343                             match mode {
          344                                 icy_engine::MouseMode::VT200 | icy_engine::MouseMode::VT200_Highlight => {
          345                                     if calc.buffer_rect.contains(pos) {
          346                                         let click_pos = calc.calc_click_pos(pos);
          347                                         let mut modifier_mask = 3; // 3 means realease
          348                                         if modifiers.shift {
          349                                             modifier_mask |= 4;
          350                                         }
          351                                         if modifiers.alt {
          352                                             modifier_mask |= 8;
          353                                         }
          354                                         if modifiers.ctrl || modifiers.mac_cmd {
          355                                             modifier_mask |= 16;
          356                                         }
          357                                         self.output_string(
          358                                             format!(
          359                                                 "\x1b[M{}{}{}",
          360                                                 encode_mouse_button(modifier_mask),
          361                                                 encode_mouse_position(click_pos.x as i32),
          362                                                 encode_mouse_position(click_pos.y as i32)
          363                                             )
          364                                             .as_str(),
          365                                         );
          366                                     }
          367                                 }
          368                                 _ => {}
          369                             }
          370                         }
          371                     }
          372 
          373                     egui::Event::PointerMoved(pos) => {
          374                         if calc.buffer_rect.contains(pos - calc.terminal_rect.left_top().to_vec2()) && !calc.vert_scrollbar_rect.contains(pos) {
          375                             // Dev feature in debug mode - print char under cursor
          376                             // when shift is pressed
          377                             if cfg!(debug_assertions) && ui.input(|i| i.modifiers.shift_only()) {
          378                                 let click_pos: Vec2 = calc.calc_click_pos(pos);
          379                                 let buffer_view = self.buffer_view.clone();
          380 
          381                                 let ch = buffer_view.lock().get_buffer().get_char((click_pos.x as usize, click_pos.y as usize));
          382                                 println!("Char under cursor: {ch:?}");
          383                             }
          384                         }
          385                     }
          386 
          387                     egui::Event::Cut => {
          388                         self.handle_key_press(ui, &response, egui::Key::X, Modifiers::CTRL);
          389                     }
          390                     egui::Event::Copy => {
          391                         self.handle_key_press(ui, &response, egui::Key::C, Modifiers::CTRL);
          392                     }
          393                     egui::Event::Key {
          394                         key, pressed: true, modifiers, ..
          395                     } => {
          396                         self.handle_key_press(ui, &response, key, modifiers);
          397                     }
          398                     _ => {}
          399                 }
          400             }
          401             if self.use_rip {
          402                 if response.clicked_by(PointerButton::Primary) {
          403                     if let Some(mouse_pos) = response.hover_pos() {
          404                         let mouse_pos = mouse_pos.to_vec2() - calc.buffer_rect.left_top().to_vec2();
          405 
          406                         let x = (mouse_pos.x / calc.buffer_rect.width() * 640.0) as i32;
          407                         let y = (mouse_pos.y / calc.buffer_rect.height() * 350.0) as i32;
          408                         let mut found_field = None;
          409                         for mouse_field in &self.terminal_thread.lock().mouse_field {
          410                             if !mouse_field.style.is_mouse_button() {
          411                                 continue;
          412                             }
          413                             if mouse_field.contains(x, y) {
          414                                 if let Some(found_field) = &found_field {
          415                                     if mouse_field.contains_field(found_field) {
          416                                         continue;
          417                                     }
          418                                 }
          419                                 found_field = Some(mouse_field.clone());
          420                             }
          421                         }
          422 
          423                         if let Some(mouse_field) = &found_field {
          424                             if let Some(cmd) = &mouse_field.host_command {
          425                                 if mouse_field.style.reset_screen_after_click() {
          426                                     let mut buffer = self.buffer_view.lock();
          427                                     buffer.get_buffer_mut().terminal_state.clear_margins_left_right();
          428                                     buffer.get_buffer_mut().terminal_state.clear_margins_top_bottom();
          429                                     buffer.clear_buffer_screen();
          430                                 }
          431                                 self.output_string(cmd);
          432                                 return;
          433                             }
          434                         }
          435                     }
          436                 }
          437 
          438                 if response.hovered() {
          439                     let hover_pos_opt = ui.input(|i| i.pointer.hover_pos());
          440                     if let Some(hover_pos) = hover_pos_opt {
          441                         let hover_pos = hover_pos.to_vec2() - calc.buffer_rect.left_top().to_vec2();
          442 
          443                         let x = (hover_pos.x / calc.buffer_rect.width() * 640.0) as i32;
          444                         let y = (hover_pos.y / calc.buffer_rect.height() * 350.0) as i32;
          445                         let fields = &self.terminal_thread.lock().mouse_field;
          446                         for mouse_field in fields {
          447                             if !mouse_field.style.is_mouse_button() {
          448                                 continue;
          449                             }
          450                             if mouse_field.contains(x, y) {
          451                                 ui.output_mut(|o: &mut egui::PlatformOutput| o.cursor_icon = CursorIcon::PointingHand);
          452                                 break;
          453                             }
          454                         }
          455                     }
          456                 }
          457                 return;
          458             }
          459 
          460             if response.clicked_by(PointerButton::Primary) {
          461                 if let Some(mouse_pos) = response.hover_pos() {
          462                     if calc.buffer_rect.contains(mouse_pos) && !calc.vert_scrollbar_rect.contains(mouse_pos) {
          463                         self.buffer_view.lock().clear_selection();
          464                     }
          465                 }
          466             }
          467 
          468             if response.drag_started_by(PointerButton::Primary) {
          469                 self.drag_start = None;
          470                 if let Some(mouse_pos) = response.hover_pos() {
          471                     if calc.buffer_rect.contains(mouse_pos) && !calc.vert_scrollbar_rect.contains(mouse_pos) {
          472                         let click_pos = calc.calc_click_pos(mouse_pos);
          473                         self.last_pos = Position::new(click_pos.x as i32, click_pos.y as i32);
          474                         self.drag_start = Some(click_pos);
          475                         self.buffer_view.lock().get_edit_state_mut().set_mask_size();
          476                         self.buffer_view.lock().set_selection(Selection::new((click_pos.x, click_pos.y)));
          477                         self.buffer_view.lock().get_selection().as_mut().unwrap().shape = if response.ctx.input(|i| i.modifiers.alt) {
          478                             icy_engine::Shape::Rectangle
          479                         } else {
          480                             icy_engine::Shape::Lines
          481                         };
          482                     }
          483                 }
          484                 self.last_pos = Position::new(-1, -1);
          485             }
          486 
          487             if response.dragged_by(PointerButton::Primary) && self.drag_start.is_some() {
          488                 if let Some(mouse_pos) = response.hover_pos() {
          489                     let click_pos = calc.calc_click_pos(mouse_pos);
          490                     let cur = Position::new(click_pos.x as i32, click_pos.y as i32);
          491 
          492                     if cur != self.last_pos {
          493                         self.last_pos = cur;
          494                         let mut l = self.buffer_view.lock();
          495                         l.get_edit_state_mut().set_mask_size();
          496 
          497                         if let Some(sel) = &mut l.get_selection() {
          498                             if !sel.locked {
          499                                 sel.lead = Position::new(click_pos.x as i32, click_pos.y as i32);
          500                                 sel.shape = if ui.input(|i| i.modifiers.alt) {
          501                                     icy_engine::Shape::Rectangle
          502                                 } else {
          503                                     icy_engine::Shape::Lines
          504                                 };
          505                                 l.clear_selection();
          506                                 l.set_selection(*sel);
          507                                 let _ = l.get_edit_state_mut().add_selection_to_mask();
          508                                 l.redraw_view();
          509                             }
          510                         }
          511                     }
          512                 }
          513             }
          514 
          515             if response.drag_stopped_by(PointerButton::Primary) && self.drag_start.is_some() {
          516                 self.shift_pressed_during_selection = ui.input(|i| i.modifiers.shift);
          517                 if response.hover_pos().is_some() {
          518                     let l = self.buffer_view.lock();
          519                     if let Some(sel) = &mut l.get_selection() {
          520                         sel.locked = true;
          521                     }
          522                 }
          523                 self.last_pos = Position::new(-1, -1);
          524 
          525                 self.drag_start = None;
          526             }
          527 
          528             if response.hovered() {
          529                 let hover_pos_opt = ui.input(|i| i.pointer.hover_pos());
          530                 if let Some(hover_pos) = hover_pos_opt {
          531                     if calc.buffer_rect.contains(hover_pos) {
          532                         let click_pos = calc.calc_click_pos(hover_pos);
          533                         let mut hovered_link = false;
          534                         let lock = self.buffer_view.lock();
          535                         let buffer = lock.get_buffer();
          536                         for hyper_link in buffer.layers[0].hyperlinks() {
          537                             if buffer.is_position_in_range(Position::new(click_pos.x as i32, click_pos.y as i32), hyper_link.position, hyper_link.length) {
          538                                 ui.output_mut(|o: &mut egui::PlatformOutput| o.cursor_icon = CursorIcon::PointingHand);
          539                                 let url = hyper_link.get_url(buffer);
          540                                 response = response.on_hover_ui_at_pointer(|ui| {
          541                                     ui.hyperlink(url.clone());
          542                                 });
          543                                 hovered_link = true;
          544 
          545                                 if response.clicked_by(PointerButton::Primary)
          546                                 /* && response.is_pointer_button_down_on() */
          547                                 {
          548                                     ui.ctx().output_mut(|o| {
          549                                         o.open_url = Some(egui::output::OpenUrl { url, new_tab: false });
          550                                     });
          551                                 }
          552                                 break;
          553                             }
          554                         }
          555                         if !hovered_link && !calc.vert_scrollbar_rect.contains(hover_pos) {
          556                             ui.output_mut(|o| o.cursor_icon = CursorIcon::Text);
          557                         }
          558                     }
          559                 }
          560             }
          561         }
          562     }
          563 
          564     fn handle_key_press(&mut self, ui: &mut egui::Ui, response: &egui::Response, key: egui::Key, modifiers: egui::Modifiers) {
          565         let im = self.screen_mode.get_input_mode();
          566         let key_map = im.cur_map();
          567         let mut key_code = key as u32;
          568         if modifiers.ctrl || modifiers.command {
          569             key_code |= icy_engine_gui::ui::CTRL_MOD;
          570         }
          571         if modifiers.shift {
          572             key_code |= icy_engine_gui::ui::SHIFT_MOD;
          573         }
          574         for (k, m) in key_map {
          575             if *k == key_code {
          576                 if self.terminal_thread.lock().is_connected {
          577                     self.send_vec(m.to_vec());
          578                 } else {
          579                     for c in *m {
          580                         self.print_char(*c);
          581                     }
          582                 }
          583                 response.request_focus();
          584 
          585                 ui.input_mut(|i| i.consume_key(modifiers, key));
          586                 break;
          587             }
          588         }
          589     }
          590 
          591     fn copy_to_clipboard(&mut self) {
          592         let buffer_view = self.buffer_view.clone();
          593         let mut l = buffer_view.lock();
          594         if self.shift_pressed_during_selection {
          595             if let Some(data) = l.get_edit_state().get_clipboard_data() {
          596                 if let Err(err) = icy_engine::util::push_data(icy_engine::util::BUFFER_DATA, &data) {
          597                     log::error!("error while copy:{err}");
          598                 }
          599                 return;
          600             }
          601         }
          602 
          603         if let Some(txt) = l.get_copy_text() {
          604             let mut clipboard = arboard::Clipboard::new().unwrap();
          605             clipboard.set_text(txt).unwrap();
          606         }
          607         l.clear_selection();
          608     }
          609 }
          610 
          611 fn terminal_context_menu(ui: &mut egui::Ui, window: &mut MainWindow) {
          612     ui.input_mut(|i| i.events.clear());
          613 
          614     if ui.button(fl!(crate::LANGUAGE_LOADER, "terminal-menu-copy")).clicked() {
          615         window.copy_to_clipboard();
          616         ui.close_menu();
          617     }
          618 
          619     if ui.button(fl!(crate::LANGUAGE_LOADER, "terminal-menu-paste")).clicked() {
          620         let mut clipboard = arboard::Clipboard::new().unwrap();
          621         if let Ok(text) = clipboard.get_text() {
          622             let im = window.screen_mode.get_input_mode();
          623             let key_map = im.cur_map();
          624             let mut first = true;
          625             let mut txt = String::new();
          626             text.lines().for_each(|line| {
          627                 if first {
          628                     first = false;
          629                 } else {
          630                     for (k, m) in key_map {
          631                         if *k == eframe::egui::Key::Enter as u32 {
          632                             for c in *m {
          633                                 txt.push(*c as char);
          634                             }
          635                         }
          636                     }
          637                 }
          638                 txt.push_str(line);
          639             });
          640             window.output_string(&txt);
          641         }
          642         ui.close_menu();
          643     }
          644 
          645     #[cfg(not(target_arch = "wasm32"))]
          646     {
          647         ui.separator();
          648         if ui
          649             .add(egui::Button::new(fl!(crate::LANGUAGE_LOADER, "terminal-menu-export")).wrap_mode(egui::TextWrapMode::Truncate))
          650             .clicked()
          651         {
          652             window.init_export_dialog(ui.ctx());
          653             ui.close_menu();
          654         }
          655     }
          656 }