GNU Octave 7.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
qt-interpreter-events.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2011-2022 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 <iostream>
31#include <sstream>
32
33#include <QDialog>
34#include <QDir>
35#include <QIcon>
36#include <QMetaType>
37#include <QPushButton>
38#include <QStringList>
39
40#include "dialog.h"
41#include "gui-preferences-ed.h"
42#include "octave-qobject.h"
44#include "qt-utils.h"
45
47#include "oct-env.h"
48#include "str-vec.h"
49
50#include "builtin-defun-decls.h"
51#include "error.h"
52#include "interpreter-private.h"
53#include "load-path.h"
54#include "oct-map.h"
55#include "octave.h"
56#include "ov.h"
57#include "syminfo.h"
58#include "utils.h"
59
60Q_DECLARE_METATYPE (octave_value)
61Q_DECLARE_METATYPE (octave::symbol_info_list)
62Q_DECLARE_METATYPE (octave::fcn_callback)
63Q_DECLARE_METATYPE (octave::meth_callback)
64
65namespace octave
66{
67 static QStringList
68 make_qstring_list (const std::list<std::string>& lst)
69 {
70 QStringList retval;
71
72 for (const auto& s : lst)
73 retval.append (QString::fromStdString (s));
74
75 return retval;
76 }
77
78 static QStringList
79 make_filter_list (const event_manager::filter_list& lst)
80 {
81 QStringList retval;
82
83 // We have pairs of data, first being the list of extensions
84 // exta;exb;extc etc second the name to use as filter name
85 // (optional). Qt wants a list of filters in the format of
86 // 'FilterName (space separated exts)'.
87
88 for (const auto& ext_name : lst)
89 {
90 QString ext = QString::fromStdString (ext_name.first);
91 QString name = QString::fromStdString (ext_name.second);
92
93 // Strip out extensions from name and replace ';' with spaces in list.
94
95 name.replace (QRegExp (R"(\‍(.*\))"), "");
96 ext.replace (";", " ");
97
98 if (name.isEmpty ())
99 {
100 // No name field. Build one from the extensions.
101 name = ext.toUpper () + " Files";
102 }
103
104 retval.append (name + " (" + ext + ')');
105 }
106
107 return retval;
108 }
109
110 qt_interpreter_events::qt_interpreter_events (base_qobject& oct_qobj)
111 : interpreter_events (), m_octave_qobj (oct_qobj),
112 m_uiwidget_creator (oct_qobj), m_result (), m_mutex (),
113 m_waitcondition ()
114 {
115 qRegisterMetaType<QIntList> ("QIntList");
116 qRegisterMetaType<QFloatList> ("QFloatList");
117
118 qRegisterMetaType<octave_value> ("octave_value");
119 qRegisterMetaType<symbol_info_list> ("symbol_info_list");
120
121 qRegisterMetaType<fcn_callback> ("fcn_callback");
122 qRegisterMetaType<meth_callback> ("meth_callback");
123
124 connect (this, &qt_interpreter_events::confirm_shutdown_signal,
125 this, &qt_interpreter_events::confirm_shutdown_octave);
126
127 connect (this, &qt_interpreter_events::get_named_icon_signal,
128 this, &qt_interpreter_events::get_named_icon_slot);
129
130 connect (this, &qt_interpreter_events::gui_preference_signal,
131 this, &qt_interpreter_events::gui_preference_slot);
132 }
133
134 void qt_interpreter_events::start_gui (bool gui_app)
135 {
136 if (m_octave_qobj.experimental_terminal_widget ())
137 emit start_gui_signal (gui_app);
138 }
139
140 void qt_interpreter_events::close_gui (void)
141 {
142 if (m_octave_qobj.experimental_terminal_widget ())
143 emit close_gui_signal ();
144 }
145
146 std::list<std::string>
147 qt_interpreter_events::file_dialog (const filter_list& filter,
148 const std::string& title,
149 const std::string& filename,
150 const std::string& dirname,
151 const std::string& multimode)
152 {
153 QStringList lst
154 = m_uiwidget_creator.file_dialog (make_filter_list (filter),
156 QString::fromStdString (filename),
158 QString::fromStdString (multimode));
159
160 std::list<std::string> retval;
161
162 for (const auto& s : lst)
163 retval.push_back (s.toStdString ());
164
165 return retval;
166 }
167
168 std::list<std::string>
169 qt_interpreter_events::input_dialog (const std::list<std::string>& prompt,
170 const std::string& title,
171 const std::list<float>& nr,
172 const std::list<float>& nc,
173 const std::list<std::string>& defaults)
174 {
175 QStringList lst
176 = m_uiwidget_creator.input_dialog (make_qstring_list (prompt),
178 std_list_to_qt_list<float> (nr),
179 std_list_to_qt_list<float> (nc),
180 make_qstring_list (defaults));
181 std::list<std::string> retval;
182
183 for (const auto& s : lst)
184 retval.push_back (s.toStdString ());
185
186 return retval;
187 }
188
189 std::pair<std::list<int>, int>
190 qt_interpreter_events::list_dialog (const std::list<std::string>& list,
191 const std::string& mode,
192 int width, int height,
193 const std::list<int>& initial,
194 const std::string& name,
195 const std::list<std::string>& prompt,
196 const std::string& ok_string,
197 const std::string& cancel_string)
198 {
199 QPair<QIntList, int> result
200 = m_uiwidget_creator.list_dialog (make_qstring_list (list),
202 width, height,
203 std_list_to_qt_list<int> (initial),
205 make_qstring_list (prompt),
206 QString::fromStdString (ok_string),
207 QString::fromStdString (cancel_string));
208
209 QIntList& lst = result.first;
210 return std::pair<std::list<int>, int> (std::list<int> (lst.begin (),
211 lst.end ()),
212 result.second);
213 }
214
215 std::string
216 qt_interpreter_events::question_dialog (const std::string& msg,
217 const std::string& title,
218 const std::string& btn1,
219 const std::string& btn2,
220 const std::string& btn3,
221 const std::string& btndef)
222 {
223 QString icon = "quest";
224 QStringList buttons;
225 QStringList role;
226
227 // Must use ResetRole which is left-aligned for all OS and WM.
228 role << "ResetRole" << "ResetRole" << "ResetRole";
229
230 buttons << QString::fromStdString (btn1);
231 if (btn2 == "")
232 role.removeAt (0);
233 else
234 buttons << QString::fromStdString (btn2);
235 buttons << QString::fromStdString (btn3);
236
237 QString answer
238 = m_uiwidget_creator.message_dialog (QString::fromStdString (msg),
240 icon, buttons,
241 QString::fromStdString (btndef),
242 role);
243
244 return answer.toStdString ();
245 }
246
247 void qt_interpreter_events::update_path_dialog (void)
248 {
249 emit update_path_dialog_signal ();
250 }
251
252 void qt_interpreter_events::show_preferences (void)
253 {
254 emit show_preferences_signal ();
255 }
256
257 void qt_interpreter_events::apply_preferences (void)
258 {
259 emit apply_new_settings ();
260 }
261
262 void qt_interpreter_events::show_terminal_window (void)
263 {
264 emit show_terminal_window_signal ();
265 }
266
267 bool qt_interpreter_events::show_documentation (const std::string& file)
268 {
269 emit show_documentation_signal (QString::fromStdString (file));
270
271 return true;
272 }
273
274 void qt_interpreter_events::show_file_browser (void)
275 {
276 emit show_file_browser_signal ();
277 }
278
279 void qt_interpreter_events::show_command_history (void)
280 {
281 emit show_command_history_signal ();
282 }
283
284 void qt_interpreter_events::show_workspace (void)
285 {
286 emit show_workspace_signal ();
287 }
288
289 void qt_interpreter_events::show_community_news (int serial)
290 {
291 emit show_community_news_signal (serial);
292 }
293
294 void qt_interpreter_events::show_release_notes (void)
295 {
296 emit show_release_notes_signal ();
297 }
298
299 bool qt_interpreter_events::edit_file (const std::string& file)
300 {
301 emit edit_file_signal (QString::fromStdString (file));
302
303 return true;
304 }
305
306 void qt_interpreter_events::edit_variable (const std::string& expr,
307 const octave_value& val)
308 {
309 emit edit_variable_signal (QString::fromStdString (expr), val);
310 }
311
312 bool qt_interpreter_events::confirm_shutdown (void)
313 {
314 QMutexLocker autolock (&m_mutex);
315
316 emit confirm_shutdown_signal ();
317
318 // Wait for result.
319 wait ();
320
321 return m_result.toBool ();
322 }
323
324 bool qt_interpreter_events::prompt_new_edit_file (const std::string& file)
325 {
326 resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
327 gui_settings *settings = rmgr.get_settings ();
328
329 if (! settings || settings->value (ed_create_new_file).toBool ())
330 return true;
331
332 std::string abs_fname = sys::env::make_absolute (file);
333
334 QStringList btn;
335 QStringList role;
336 role << "YesRole" << "RejectRole";
337 btn << tr ("Create") << tr ("Cancel");
338
339 QString answer = m_uiwidget_creator.message_dialog
340 (tr ("File\n%1\ndoes not exist. Do you want to create it?").
341 arg (QString::fromStdString (abs_fname)),
342 tr ("Octave Editor"), "quest", btn, tr ("Create"), role);
343
344 return (answer == tr ("Create"));
345 }
346
347 // Prompt to allow file to be run by setting cwd (or if
348 // addpath_option==true, alternatively setting the path).
349
350 int
351 qt_interpreter_events::debug_cd_or_addpath_error (const std::string& file,
352 const std::string& dir,
353 bool addpath_option)
354 {
355 int retval = -1;
356
357 QString qdir = QString::fromStdString (dir);
358 QString qfile = QString::fromStdString (file);
359 QString msg
360 = (addpath_option
361 ? tr ("The file %1 does not exist in the load path. To run or debug the function you are editing, you must either change to the directory %2 or add that directory to the load path.").arg (qfile).arg (qdir)
362 : tr ("The file %1 is shadowed by a file with the same name in the load path. To run or debug the function you are editing, change to the directory %2.").arg (qfile).arg (qdir));
363
364 QString title = tr ("Change Directory or Add Directory to Load Path");
365
366 QString cd_txt = tr ("&Change Directory");
367 QString addpath_txt = tr ("&Add Directory to Load Path");
368 QString cancel_txt = tr ("Cancel");
369
370 QStringList btn;
371 QStringList role;
372 btn << cd_txt;
373 role << "YesRole";
374 if (addpath_option)
375 {
376 btn << addpath_txt;
377 role << "AcceptRole";
378 }
379 btn << cancel_txt;
380 role << "RejectRole";
381
382 QString result
383 = m_uiwidget_creator.message_dialog (msg, title, "quest", btn,
384 cancel_txt, role);
385
386 if (result == cd_txt)
387 retval = 1;
388 else if (result == addpath_txt)
389 retval = 2;
390
391 return retval;
392 }
393
394 uint8NDArray qt_interpreter_events::get_named_icon (const std::string& name)
395 {
396 QMutexLocker autolock (&m_mutex);
397
398 emit get_named_icon_signal (QString::fromStdString (name));
399
400 // Wait for result.
401 wait ();
402
403 uint8NDArray empty_img;
404
405 QIcon icon = m_result.value<QIcon> ();
406
407 if (icon.isNull ())
408 return empty_img;
409
410 QImage img = icon.pixmap (QSize (32, 32)).toImage ();
411
412 if (img.format () != QImage::Format_ARGB32_Premultiplied)
413 return empty_img;
414
415 dim_vector dims (img.height (), img.width (), 4);
416
417 uint8NDArray retval (dims, 0);
418
419 uint8_t *bits = img.bits ();
420
421 for (int i = 0; i < img.height (); i++)
422 {
423 for (int j = 0; j < img.width (); j++)
424 {
425 retval(i, j, 2) = bits[0];
426 retval(i, j, 1) = bits[1];
427 retval(i, j, 0) = bits[2];
428 retval(i, j, 3) = bits[3];
429
430 bits += 4;
431 }
432 }
433
434 return retval;
435 }
436
437 void qt_interpreter_events::get_named_icon_slot (const QString& name)
438 {
439 QMutexLocker autolock (&m_mutex);
440
441 resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
442 m_result = QVariant::fromValue (rmgr.icon (name));
443
444 wake_all ();
445 }
446
447 std::string
448 qt_interpreter_events::gui_preference (const std::string& key,
449 const std::string& value)
450 {
451 QString pref_value;
452
453 QMutexLocker autolock (&m_mutex);
454
455 // Emit the signal for changing or getting a preference
456 emit gui_preference_signal (QString::fromStdString (key),
457 QString::fromStdString (value));
458
459 // Wait for response (pref_value).
460 wait ();
461
462 QString pref = m_result.toString ();
463
464 return pref.toStdString ();
465 }
466
467 bool qt_interpreter_events::copy_image_to_clipboard (const std::string& file)
468 {
469 emit copy_image_to_clipboard_signal (QString::fromStdString (file), true);
470
471 return true;
472 }
473
474 void qt_interpreter_events::focus_window (const std::string win_name)
475 {
476 emit focus_window_signal (QString::fromStdString (win_name));
477 }
478
479 void qt_interpreter_events::execute_command_in_terminal
480 (const std::string& command)
481 {
482 emit execute_command_in_terminal_signal (QString::fromStdString (command));
483 }
484
485 void qt_interpreter_events::register_documentation (const std::string& file)
486 {
487 emit register_documentation_signal (QString::fromStdString (file));
488 }
489
490 void qt_interpreter_events::unregister_documentation (const std::string& file)
491 {
492 emit unregister_documentation_signal (QString::fromStdString (file));
493 }
494
495 void qt_interpreter_events::interpreter_output (const std::string& msg)
496 {
497 if (m_octave_qobj.experimental_terminal_widget ()
498 && m_octave_qobj.have_terminal_window ())
499 emit interpreter_output_signal (QString::fromStdString (msg));
500 else
501 {
502 // FIXME: is this the correct thing to do?
503 std::cout << msg;
504 }
505 }
506
507 void qt_interpreter_events::display_exception (const execution_exception& ee,
508 bool beep)
509 {
510 if (m_octave_qobj.experimental_terminal_widget ()
511 && m_octave_qobj.have_terminal_window ())
512 {
513 std::ostringstream buf;
514 ee.display (buf);
515 emit interpreter_output_signal (QString::fromStdString (buf.str ()));
516 }
517 else
518 {
519 if (beep)
520 std::cerr << "\a";
521
522 ee.display (std::cerr);
523 }
524 }
525
526 void qt_interpreter_events::gui_status_update (const std::string& feature,
527 const std::string& status)
528 {
529 emit gui_status_update_signal (QString::fromStdString (feature),
530 QString::fromStdString (status));
531 }
532
533 void qt_interpreter_events::update_gui_lexer (void)
534 {
535 emit update_gui_lexer_signal (true);
536 }
537
538 void qt_interpreter_events::directory_changed (const std::string& dir)
539 {
540 emit directory_changed_signal (QString::fromStdString (dir));
541 }
542
543 void qt_interpreter_events::file_remove (const std::string& old_name,
544 const std::string& new_name)
545 {
546 QMutexLocker autolock (&m_mutex);
547
548 // Emit the signal for the editor for closing the file if it is open
549 emit file_remove_signal (QString::fromStdString (old_name),
550 QString::fromStdString (new_name));
551
552 // Wait for file removal to complete before continuing.
553 wait ();
554 }
555
556 void qt_interpreter_events::file_renamed (bool load_new)
557 {
558 emit file_renamed_signal (load_new);
559 }
560
561 void qt_interpreter_events::set_workspace (bool top_level, bool debug,
562 const symbol_info_list& syminfo,
563 bool update_variable_editor)
564 {
565 if (! top_level && ! debug)
566 return;
567
568 emit set_workspace_signal (top_level, debug, syminfo);
569
570 if (update_variable_editor)
571 emit refresh_variable_editor_signal ();
572 }
573
574 void qt_interpreter_events::clear_workspace (void)
575 {
576 emit clear_workspace_signal ();
577 }
578
579 void qt_interpreter_events::update_prompt (const std::string& prompt)
580 {
581 emit update_prompt_signal (QString::fromStdString (prompt));
582 }
583
584 void qt_interpreter_events::set_history (const string_vector& hist)
585 {
586 QStringList qt_hist;
587
588 for (octave_idx_type i = 0; i < hist.numel (); i++)
589 qt_hist.append (QString::fromStdString (hist[i]));
590
591 emit set_history_signal (qt_hist);
592 }
593
594 void qt_interpreter_events::append_history (const std::string& hist_entry)
595 {
596 emit append_history_signal (QString::fromStdString (hist_entry));
597 }
598
599 void qt_interpreter_events::clear_history (void)
600 {
601 emit clear_history_signal ();
602 }
603
604 void qt_interpreter_events::pre_input_event (void)
605 { }
606
607 void qt_interpreter_events::post_input_event (void)
608 { }
609
610 void qt_interpreter_events::enter_debugger_event (const std::string& /*fcn_name*/,
611 const std::string& fcn_file_name,
612 int line)
613 {
614 if (fcn_file_name.empty ())
615 return;
616
617 insert_debugger_pointer (fcn_file_name, line);
618
619 emit enter_debugger_signal ();
620 }
621
622 void
623 qt_interpreter_events::execute_in_debugger_event (const std::string& file,
624 int line)
625 {
626 delete_debugger_pointer (file, line);
627 }
628
629 void qt_interpreter_events::exit_debugger_event (void)
630 {
631 emit exit_debugger_signal ();
632 }
633
634 // Display (if @insert true) or remove the appropriate symbol for a breakpoint
635 // in @file at @line with condition @cond.
636 void qt_interpreter_events::update_breakpoint (bool insert,
637 const std::string& file,
638 int line,
639 const std::string& cond)
640 {
641 emit update_breakpoint_marker_signal (insert, QString::fromStdString (file),
642 line, QString::fromStdString (cond));
643 }
644
645 void
646 qt_interpreter_events::insert_debugger_pointer (const std::string& file,
647 int line)
648 {
649 emit insert_debugger_pointer_signal (QString::fromStdString (file), line);
650 }
651
652 void
653 qt_interpreter_events::delete_debugger_pointer (const std::string& file,
654 int line)
655 {
656 emit delete_debugger_pointer_signal (QString::fromStdString (file), line);
657 }
658
659 void
660 qt_interpreter_events::confirm_shutdown_octave (void)
661 {
662 QMutexLocker autolock (&m_mutex);
663
664 m_result = m_octave_qobj.confirm_shutdown ();
665
666 wake_all ();
667 }
668
669 // If VALUE is empty, return current value of preference named by KEY.
670 //
671 // If VALUE is not empty, set preference named by KEY to VALUE return
672 // previous value.
673 //
674 // FIXME: should we have separate get and set functions? With only
675 // one, we don't allow a preference value to be set to the empty
676 // string.
677
678 void
679 qt_interpreter_events::gui_preference_slot (const QString& key,
680 const QString& value)
681 {
682 QMutexLocker autolock (&m_mutex);
683
684 resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
685 gui_settings *settings = rmgr.get_settings ();
686
687 QString read_value = settings->value (key).toString ();
688
689 // Some preferences need extra handling
690 QString adjusted_value = gui_preference_adjust (key, value);
691
692 if (! adjusted_value.isEmpty () && (read_value != adjusted_value))
693 {
694 // Change settings only for new, non-empty values
695 settings->setValue (key, QVariant (adjusted_value));
696
697 emit settings_changed (settings, true); // true: changed by worker
698 }
699
700 m_result = read_value;
701
702 wake_all ();
703 }
704
705 QString
706 qt_interpreter_events::gui_preference_adjust (const QString& key,
707 const QString& value)
708 {
709 // Immediately return if no new value is given.
710
711 if (value.isEmpty ())
712 return value;
713
714 QString adjusted_value = value;
715
716 // Not all encodings are available. Encodings are uppercase and do
717 // not use CPxxx but IBMxxx or WINDOWS-xxx.
718
719 if (key == ed_default_enc.key)
720 {
721 adjusted_value = adjusted_value.toUpper ();
722
723 QStringList codecs;
724 resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
725 rmgr.get_codecs (&codecs);
726
727 QRegExp re ("^CP(\\d+)$");
728
729 if (adjusted_value == "SYSTEM")
730 adjusted_value =
731 QString ("SYSTEM (") +
732 QString (octave_locale_charset_wrapper ()).toUpper () +
733 QString (")");
734 else if (re.indexIn (adjusted_value) > -1)
735 {
736 if (codecs.contains ("IBM" + re.cap (1)))
737 adjusted_value = "IBM" + re.cap (1);
738 else if (codecs.contains ("WINDOWS-" + re.cap (1)))
739 adjusted_value = "WINDOWS-" + re.cap (1);
740 else
741 adjusted_value.clear ();
742 }
743 else if (! codecs.contains (adjusted_value))
744 adjusted_value.clear ();
745 }
746
747 return adjusted_value;
748 }
749}
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
std::list< std::pair< std::string, std::string > > filter_list
qt_interpreter_events(base_qobject &oct_qobj)
octave_idx_type numel(void) const
Definition: str-vec.h:100
QList< int > QIntList
Definition: dialog.h:40
OCTAVE_NAMESPACE_BEGIN typedef std::function< void(void)> fcn_callback
Definition: event-manager.h:47
std::function< void(interpreter &)> meth_callback
Definition: event-manager.h:48
OCTAVE_NAMESPACE_BEGIN MArray< T > filter(MArray< T > &b, MArray< T > &a, MArray< T > &x, MArray< T > &si, int dim=0)
Definition: filter.cc:48
QString name
const gui_pref ed_create_new_file("editor/create_new_file", QVariant(false))
const gui_pref ed_default_enc("editor/default_encoding", QVariant("UTF-8"))
const char * octave_locale_charset_wrapper(void)
QString fromStdString(const std::string &s)
std::string dirname(const std::string &path)
Definition: file-ops.cc:358
T read_value(std::istream &is)
Definition: lo-utils.cc:183
const QString key
static string_vector make_absolute(const string_vector &sv)
Definition: utils.cc:535