1 //////////////////////////////////////////////////////////////////////// 2 // 3 // Copyright (C) 2011-2021 The Octave Project Developers 4 // 5 // See the file COPYRIGHT.md in the top-level directory of this 6 // distribution or <https://octave.org/copyright/>. 7 // 8 // This file is part of Octave. 9 // 10 // Octave is free software: you can redistribute it and/or modify it 11 // under the terms of the GNU General Public License as published by 12 // the Free Software Foundation, either version 3 of the License, or 13 // (at your option) any later version. 14 // 15 // Octave is distributed in the hope that it will be useful, but 16 // WITHOUT ANY WARRANTY; without even the implied warranty of 17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 // GNU General Public License for more details. 19 // 20 // You should have received a copy of the GNU General Public License 21 // along with Octave; see the file COPYING. If not, see 22 // <https://www.gnu.org/licenses/>. 23 // 24 //////////////////////////////////////////////////////////////////////// 25 26 #if defined (HAVE_CONFIG_H) 27 # include "config.h" 28 #endif 29 30 #include "builtin-defun-decls.h" 31 #include "cmd-edit.h" 32 #include "defun.h" 33 #include "event-manager.h" 34 #include "interpreter.h" 35 #include "interpreter-private.h" 36 #include "oct-env.h" 37 #include "oct-mutex.h" 38 #include "ovl.h" 39 #include "pager.h" 40 #include "syminfo.h" 41 42 namespace octave 43 { readline_event_hook(void)44 static int readline_event_hook (void) 45 { 46 event_manager& evmgr = __get_event_manager__ ("octave_readline_hook"); 47 48 evmgr.process_events (); 49 50 return 0; 51 } 52 event_manager(interpreter & interp)53 event_manager::event_manager (interpreter& interp) 54 : m_interpreter (interp), instance (nullptr), 55 event_queue_mutex (new mutex ()), gui_event_queue (), 56 debugging (false), link_enabled (false) 57 { 58 command_editor::add_event_hook (readline_event_hook); 59 } 60 ~event_manager(void)61 event_manager::~event_manager (void) 62 { 63 delete event_queue_mutex; 64 } 65 66 // Programming Note: It is possible to disable the link without deleting 67 // the connection. This allows it to be temporarily disabled. But if 68 // the link is removed, we also set the link_enabled flag to false 69 // because if there is no link, it can't be enabled. Also, access to 70 // instance is only protected by a check on the link_enabled flag. 71 72 void connect_link(const std::shared_ptr<interpreter_events> & obj)73 event_manager::connect_link (const std::shared_ptr<interpreter_events>& obj) 74 { 75 if (! obj) 76 disable (); 77 78 instance = obj; 79 } 80 enable(void)81 bool event_manager::enable (void) 82 { 83 bool retval = link_enabled; 84 85 if (instance) 86 link_enabled = true; 87 else 88 warning ("event_manager: must have connected link to enable"); 89 90 return retval; 91 } 92 process_events(bool disable_flag)93 void event_manager::process_events (bool disable_flag) 94 { 95 if (enabled ()) 96 { 97 if (disable_flag) 98 disable (); 99 100 event_queue_mutex->lock (); 101 102 gui_event_queue.run (); 103 104 event_queue_mutex->unlock (); 105 } 106 } 107 discard_events(void)108 void event_manager::discard_events (void) 109 { 110 if (enabled ()) 111 { 112 event_queue_mutex->lock (); 113 114 gui_event_queue.discard (); 115 116 event_queue_mutex->unlock (); 117 } 118 } 119 set_workspace(void)120 void event_manager::set_workspace (void) 121 { 122 if (enabled ()) 123 { 124 tree_evaluator& tw = m_interpreter.get_evaluator (); 125 126 instance->set_workspace (tw.at_top_level (), debugging, 127 tw.get_symbol_info (), true); 128 } 129 } 130 } 131 132 DEFMETHOD (__event_manager_enabled__, interp, , , 133 doc: /* -*- texinfo -*- 134 @deftypefn {} {} __event_manager_enabled__ () 135 Undocumented internal function. 136 @end deftypefn */) 137 { 138 octave::event_manager& evmgr = interp.get_event_manager (); 139 140 return ovl (evmgr.enabled ()); 141 } 142 143 DEFMETHOD (__event_manager_edit_file__, interp, args, , 144 doc: /* -*- texinfo -*- 145 @deftypefn {} {} __event_manager_edit_file__ (@var{file}) 146 Undocumented internal function. 147 @end deftypefn */) 148 { 149 octave_value retval; 150 151 octave::event_manager& evmgr = interp.get_event_manager (); 152 153 if (args.length () == 1) 154 { 155 std::string file 156 = args(0).xstring_value ("first argument must be filename"); 157 158 octave::flush_stdout (); 159 160 retval = evmgr.edit_file (file); 161 } 162 else if (args.length () == 2) 163 { 164 std::string file 165 = args(0).xstring_value ("first argument must be filename"); 166 167 octave::flush_stdout (); 168 169 retval = evmgr.prompt_new_edit_file (file); 170 } 171 172 return retval; 173 } 174 175 DEFMETHOD (__event_manager_question_dialog__, interp, args, , 176 doc: /* -*- texinfo -*- 177 @deftypefn {} {} __event_manager_question_dialog__ (@var{msg}, @var{title}, @var{btn1}, @var{btn2}, @var{btn3}, @var{default}) 178 Undocumented internal function. 179 @end deftypefn */) 180 { 181 octave_value retval; 182 183 if (args.length () == 6) 184 { 185 std::string msg = args(0).xstring_value ("invalid arguments"); 186 std::string title = args(1).xstring_value ("invalid arguments"); 187 std::string btn1 = args(2).xstring_value ("invalid arguments"); 188 std::string btn2 = args(3).xstring_value ("invalid arguments"); 189 std::string btn3 = args(4).xstring_value ("invalid arguments"); 190 std::string btndef = args(5).xstring_value ("invalid arguments"); 191 192 octave::flush_stdout (); 193 194 octave::event_manager& evmgr = interp.get_event_manager (); 195 196 retval = evmgr.question_dialog (msg, title, btn1, btn2, btn3, btndef); 197 } 198 199 return retval; 200 } 201 202 DEFMETHOD (__event_manager_file_dialog__, interp, args, , 203 doc: /* -*- texinfo -*- 204 @deftypefn {} {} __event_manager_file_dialog__ (@var{filterlist}, @var{title}, @var{filename}, @var{size} @var{multiselect}, @var{pathname}) 205 Undocumented internal function. 206 @end deftypefn */) 207 { 208 if (args.length () != 6) 209 return ovl (); 210 211 octave_value_list retval (3); 212 213 const Array<std::string> flist = args(0).cellstr_value (); 214 std::string title = args(1).string_value (); 215 std::string filename = args(2).string_value (); 216 Matrix pos = args(3).matrix_value (); 217 std::string multi_on = args(4).string_value (); // on, off, create 218 std::string pathname = args(5).string_value (); 219 220 octave_idx_type nel; 221 222 octave::event_manager::filter_list filter_lst; 223 224 for (octave_idx_type i = 0; i < flist.rows (); i++) 225 filter_lst.push_back (std::make_pair (flist(i,0), 226 (flist.columns () > 1 227 ? flist(i,1) : ""))); 228 229 octave::flush_stdout (); 230 231 octave::event_manager& evmgr = interp.get_event_manager (); 232 233 std::list<std::string> items_lst 234 = evmgr.file_dialog (filter_lst, title, filename, pathname, multi_on); 235 236 nel = items_lst.size (); 237 238 // If 3, then retval is filename, directory, and selected index. 239 if (nel <= 3) 240 { 241 if (items_lst.front ().empty ()) 242 retval = ovl (octave_value (0.), octave_value (0.), octave_value (0.)); 243 else 244 { 245 int idx = 0; 246 for (auto& str : items_lst) 247 { 248 if (idx != 2) 249 retval(idx++) = str; 250 else 251 retval(idx++) = atoi (str.c_str ()); 252 } 253 } 254 } 255 else 256 { 257 // Multiple files. 258 nel -= 2; 259 Cell items (dim_vector (1, nel)); 260 261 auto it = items_lst.begin (); 262 263 for (int idx = 0; idx < nel; idx++, it++) 264 items.xelem (idx) = *it; 265 266 retval = ovl (items, *it++, atoi (it->c_str ())); 267 } 268 269 return retval; 270 } 271 272 DEFMETHOD (__event_manager_list_dialog__, interp, args, , 273 doc: /* -*- texinfo -*- 274 @deftypefn {} {} __event_manager_list_dialog__ (@var{list}, @var{mode}, @var{size}, @var{initial}, @var{name}, @var{prompt}, @var{ok_string}, @var{cancel_string}) 275 Undocumented internal function. 276 @end deftypefn */) 277 { 278 if (args.length () != 8) 279 return ovl (); 280 281 Cell list = args(0).cell_value (); 282 const Array<std::string> tlist = list.cellstr_value (); 283 octave_idx_type nel = tlist.numel (); 284 std::list<std::string> list_lst; 285 for (octave_idx_type i = 0; i < nel; i++) 286 list_lst.push_back (tlist(i)); 287 288 std::string mode = args(1).string_value (); 289 290 Matrix size_matrix = args(2).matrix_value (); 291 int width = size_matrix(0); 292 int height = size_matrix(1); 293 294 Matrix initial_matrix = args(3).matrix_value (); 295 nel = initial_matrix.numel (); 296 std::list<int> initial_lst; 297 for (octave_idx_type i = 0; i < nel; i++) 298 initial_lst.push_back (initial_matrix(i)); 299 300 std::string name = args(4).string_value (); 301 list = args(5).cell_value (); 302 const Array<std::string> plist = list.cellstr_value (); 303 nel = plist.numel (); 304 std::list<std::string> prompt_lst; 305 for (octave_idx_type i = 0; i < nel; i++) 306 prompt_lst.push_back (plist(i)); 307 std::string ok_string = args(6).string_value (); 308 std::string cancel_string = args(7).string_value (); 309 310 octave::flush_stdout (); 311 312 octave::event_manager& evmgr = interp.get_event_manager (); 313 314 std::pair<std::list<int>, int> result 315 = evmgr.list_dialog (list_lst, mode, width, height, initial_lst, 316 name, prompt_lst, ok_string, cancel_string); 317 318 std::list<int> items_lst = result.first; 319 nel = items_lst.size (); 320 Matrix items (dim_vector (1, nel)); 321 octave_idx_type i = 0; 322 for (const auto& int_el : items_lst) 323 items.xelem(i++) = int_el; 324 325 return ovl (items, result.second); 326 } 327 328 DEFMETHOD (__event_manager_input_dialog__, interp, args, , 329 doc: /* -*- texinfo -*- 330 @deftypefn {} {} __event_manager_input_dialog__ (@var{prompt}, @var{title}, @var{rowscols}, @var{defaults}) 331 Undocumented internal function. 332 @end deftypefn */) 333 { 334 if (args.length () != 4) 335 return ovl (); 336 337 Cell prompt = args(0).cell_value (); 338 Array<std::string> tmp = prompt.cellstr_value (); 339 octave_idx_type nel = tmp.numel (); 340 std::list<std::string> prompt_lst; 341 for (octave_idx_type i = 0; i < nel; i++) 342 prompt_lst.push_back (tmp(i)); 343 344 std::string title = args(1).string_value (); 345 346 Matrix rc = args(2).matrix_value (); 347 nel = rc.rows (); 348 std::list<float> nr; 349 std::list<float> nc; 350 for (octave_idx_type i = 0; i < nel; i++) 351 { 352 nr.push_back (rc(i,0)); 353 nc.push_back (rc(i,1)); 354 } 355 356 Cell defaults = args(3).cell_value (); 357 tmp = defaults.cellstr_value (); 358 nel = tmp.numel (); 359 std::list<std::string> defaults_lst; 360 for (octave_idx_type i = 0; i < nel; i++) 361 defaults_lst.push_back (tmp(i)); 362 363 octave::flush_stdout (); 364 365 octave::event_manager& evmgr = interp.get_event_manager (); 366 367 std::list<std::string> items_lst 368 = evmgr.input_dialog (prompt_lst, title, nr, nc, defaults_lst); 369 370 nel = items_lst.size (); 371 Cell items (dim_vector (nel, 1)); 372 octave_idx_type i = 0; 373 for (const auto& str_el : items_lst) 374 items.xelem(i++) = str_el; 375 376 return ovl (items); 377 } 378 379 380 DEFMETHOD (__event_manager_named_icon__, interp, args, , 381 doc: /* -*- texinfo -*- 382 @deftypefn {} {} __event_manager_dialog_icons__ (@var{icon_name}) 383 Undocumented internal function. 384 @end deftypefn */) 385 { 386 uint8NDArray retval; 387 388 if (args.length () > 0) 389 { 390 std::string icon_name = args(0).xstring_value ("invalid arguments"); 391 392 octave::event_manager& evmgr = interp.get_event_manager (); 393 394 retval = evmgr.get_named_icon (icon_name); 395 } 396 397 return ovl (retval); 398 } 399 400 DEFMETHOD (__event_manager_show_preferences__, interp, , , 401 doc: /* -*- texinfo -*- 402 @deftypefn {} {} __event_manager_show_preferences__ () 403 Undocumented internal function. 404 @end deftypefn */) 405 { 406 octave::event_manager& evmgr = interp.get_event_manager (); 407 408 return ovl (evmgr.show_preferences ()); 409 } 410 411 DEFMETHOD (__event_manager_apply_preferences__, interp, , , 412 doc: /* -*- texinfo -*- 413 @deftypefn {} {} __event_manager_apply_preferences__ () 414 Undocumented internal function. 415 @end deftypefn */) 416 { 417 octave::event_manager& evmgr = interp.get_event_manager (); 418 419 return ovl (evmgr.apply_preferences ()); 420 } 421 422 DEFMETHOD (__event_manager_gui_preference__, interp, args, , 423 doc: /* -*- texinfo -*- 424 @deftypefn {} {} __event_manager_gui_preference__ () 425 Undocumented internal function. 426 @end deftypefn */) 427 { 428 std::string key; 429 std::string value = ""; 430 431 if (args.length () >= 1) 432 key = args(0).string_value(); 433 else 434 error ("__event_manager_gui_preference__: " 435 "first argument must be the preference key"); 436 437 if (args.length () >= 2) 438 value = args(1).string_value(); 439 440 if (octave::application::is_gui_running ()) 441 { 442 octave::event_manager& evmgr = interp.get_event_manager (); 443 444 return ovl (evmgr.gui_preference (key, value)); 445 } 446 else 447 return ovl (value); 448 } 449 450 DEFMETHOD (__event_manager_file_remove__, interp, args, , 451 doc: /* -*- texinfo -*- 452 @deftypefn {} {} __event_manager_file_remove__ () 453 Undocumented internal function. 454 @end deftypefn */) 455 { 456 std::string old_name, new_name; 457 458 if (args.length () == 2) 459 { 460 old_name = args(0).string_value(); 461 new_name = args(1).string_value(); 462 } 463 else 464 error ("__event_manager_file_remove__: " 465 "old and new name expected as arguments"); 466 467 octave::event_manager& evmgr = interp.get_event_manager (); 468 469 evmgr.file_remove (old_name, new_name); 470 471 return ovl (); 472 } 473 474 DEFMETHOD (__event_manager_file_renamed__, interp, args, , 475 doc: /* -*- texinfo -*- 476 @deftypefn {} {} __event_manager_file_renamed__ () 477 Undocumented internal function. 478 @end deftypefn */) 479 { 480 bool load_new; 481 482 if (args.length () == 1) 483 load_new = args(0).bool_value(); 484 else 485 error ("__event_manager_file_renamed__: " 486 "first argument must be boolean for reload new named file"); 487 488 octave::event_manager& evmgr = interp.get_event_manager (); 489 490 evmgr.file_renamed (load_new); 491 492 return ovl (); 493 } 494 495 DEFMETHOD (openvar, interp, args, , 496 doc: /* -*- texinfo -*- 497 @deftypefn {} {} openvar (@var{name}) 498 Open the variable @var{name} in the graphical Variable Editor. 499 @end deftypefn */) 500 { 501 if (args.length () != 1) 502 print_usage (); 503 504 if (! args(0).is_string ()) 505 error ("openvar: NAME must be a string"); 506 507 std::string name = args(0).string_value (); 508 509 if (! (Fisguirunning ())(0).is_true ()) 510 warning ("openvar: GUI is not running, can't start Variable Editor"); 511 else 512 { 513 octave_value val = interp.varval (name); 514 515 if (val.is_undefined ()) 516 error ("openvar: '%s' is not a variable", name.c_str ()); 517 518 octave::event_manager& evmgr = interp.get_event_manager (); 519 520 evmgr.edit_variable (name, val); 521 } 522 523 return ovl (); 524 } 525 526 /* 527 %!error openvar () 528 %!error openvar ("a", "b") 529 %!error <NAME must be a string> openvar (1:10) 530 */ 531 532 DEFMETHOD (__event_manager_show_doc__, interp, args, , 533 doc: /* -*- texinfo -*- 534 @deftypefn {} {} __event_manager_show_doc__ (@var{filename}) 535 Undocumented internal function. 536 @end deftypefn */) 537 { 538 std::string file; 539 540 if (args.length () >= 1) 541 file = args(0).string_value(); 542 543 octave::event_manager& evmgr = interp.get_event_manager (); 544 545 return ovl (evmgr.show_doc (file)); 546 } 547 548 DEFMETHOD (__event_manager_register_doc__, interp, args, , 549 doc: /* -*- texinfo -*- 550 @deftypefn {} {} __event_manager_register_doc__ (@var{filename}) 551 Undocumented internal function. 552 @end deftypefn */) 553 { 554 std::string file; 555 556 if (args.length () >= 1) 557 file = args(0).string_value(); 558 559 octave::event_manager& evmgr = interp.get_event_manager (); 560 561 return ovl (evmgr.register_doc (file)); 562 } 563 564 DEFMETHOD (__event_manager_unregister_doc__, interp, args, , 565 doc: /* -*- texinfo -*- 566 @deftypefn {} {} __event_manager_unregister_doc__ (@var{filename}) 567 Undocumented internal function. 568 @end deftypefn */) 569 { 570 std::string file; 571 572 if (args.length () >= 1) 573 file = args(0).string_value(); 574 575 octave::event_manager& evmgr = interp.get_event_manager (); 576 577 return ovl (evmgr.unregister_doc (file)); 578 } 579 580 DEFMETHOD (__event_manager_copy_image_to_clipboard__, interp, args, , 581 doc: /* -*- texinfo -*- 582 @deftypefn {} {} __event_manager_copy_image_to_clipboard__ (@var{filename}) 583 Undocumented internal function. 584 @end deftypefn */) 585 { 586 std::string file; 587 588 if (args.length () >= 1) 589 file = args(0).string_value(); 590 591 octave::event_manager& evmgr = interp.get_event_manager (); 592 evmgr.copy_image_to_clipboard (file); 593 return ovl (); 594 } 595 596 DEFMETHOD (commandhistory, interp, args, , 597 doc: /* -*- texinfo -*- 598 @deftypefn {} {} commandhistory () 599 Show the GUI command history window and give it the keyboard focus. 600 @seealso{commandwindow, filebrowser, workspace} 601 @end deftypefn */) 602 { 603 if (args.length () != 0) 604 print_usage (); 605 606 octave::event_manager& evmgr = interp.get_event_manager (); 607 evmgr.focus_window ("history"); 608 return ovl (); 609 } 610 611 DEFMETHOD (commandwindow, interp, args, , 612 doc: /* -*- texinfo -*- 613 @deftypefn {} {} commandwindow () 614 Show the GUI command window and give it the keyboard focus. 615 @seealso{commandhistory, filebrowser, workspace} 616 @end deftypefn */) 617 { 618 if (args.length () != 0) 619 print_usage (); 620 621 octave::event_manager& evmgr = interp.get_event_manager (); 622 evmgr.focus_window ("command"); 623 return ovl (); 624 } 625 626 DEFMETHOD (filebrowser, interp, args, , 627 doc: /* -*- texinfo -*- 628 @deftypefn {} {} filebrowser () 629 Show the GUI file browser window and give it the keyboard focus. 630 @seealso{commandwindow, commandhistory, workspace} 631 @end deftypefn */) 632 { 633 if (args.length () != 0) 634 print_usage (); 635 636 octave::event_manager& evmgr = interp.get_event_manager (); 637 evmgr.focus_window ("filebrowser"); 638 return ovl (); 639 } 640 641 DEFMETHOD (workspace, interp, args, , 642 doc: /* -*- texinfo -*- 643 @deftypefn {} {} workspace () 644 Show the GUI workspace window and give it the keyboard focus. 645 @seealso{commandwindow, commandhistory, filebrowser} 646 @end deftypefn */) 647 { 648 if (args.length () != 0) 649 print_usage (); 650 651 octave::event_manager& evmgr = interp.get_event_manager (); 652 evmgr.focus_window ("workspace"); 653 return ovl (); 654 } 655