GNU Octave  8.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
file-editor-tab.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2011-2023 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 //! @file file-editor-tab.cc A single GUI file tab.
27 //!
28 //! This interfaces QsciScintilla with the rest of Octave.
29 
30 #if defined (HAVE_CONFIG_H)
31 # include "config.h"
32 #endif
33 
34 #if defined (HAVE_QSCINTILLA)
35 
36 #include <QApplication>
37 #include <QCheckBox>
38 #include <QDateTime>
39 #include <QDesktopServices>
40 #include <QDialogButtonBox>
41 #include <QFileDialog>
42 #include <QInputDialog>
43 #include <QLabel>
44 #include <QMessageBox>
45 #include <QPrintDialog>
46 #include <QPushButton>
47 #include <QScrollBar>
48 #include <QSaveFile>
49 #include <QStyle>
50 #include <QTextBlock>
51 #include <QTextCodec>
52 #include <QTextStream>
53 #include <QVBoxLayout>
54 #if defined (HAVE_QSCI_QSCILEXEROCTAVE_H)
55 # define HAVE_LEXER_OCTAVE 1
56 # include <Qsci/qscilexeroctave.h>
57 #elif defined (HAVE_QSCI_QSCILEXERMATLAB_H)
58 # define HAVE_LEXER_MATLAB 1
59 # include <Qsci/qscilexermatlab.h>
60 #endif
61 #include <Qsci/qscilexerbash.h>
62 #include <Qsci/qscilexerbatch.h>
63 #include <Qsci/qscilexercpp.h>
64 #include <Qsci/qscilexerjava.h>
65 #include <Qsci/qscilexerdiff.h>
66 #include <Qsci/qscilexerperl.h>
67 #include <Qsci/qsciprinter.h>
68 
69 #include "file-editor-tab.h"
70 #include "file-editor.h"
71 #include "gui-preferences-cs.h"
72 #include "gui-preferences-ed.h"
73 #include "gui-preferences-global.h"
74 #include "gui-utils.h"
75 #include "marker.h"
76 #include "octave-qobject.h"
77 #include "octave-qtutils.h"
78 #include "octave-txt-lexer.h"
79 
80 #include "cmd-edit.h"
81 #include "file-ops.h"
82 #include "localcharset-wrapper.h"
83 #include "uniconv-wrappers.h"
84 
85 #include "bp-table.h"
86 #include "builtin-defun-decls.h"
87 #include "interpreter-private.h"
88 #include "interpreter.h"
89 #include "load-path.h"
90 #include "oct-map.h"
91 #include "ov-usr-fcn.h"
92 #include "qt-interpreter-events.h"
93 #include "symtab.h"
94 #include "unwind-prot.h"
95 #include "utils.h"
96 #include "version.h"
97 
99 
100 //! A file_editor_tab object consists of a text area and three left margins.
101 //! The first holds breakpoints, bookmarks, and the debug program counter.
102 //! The second holds line numbers. The third holds "fold" marks, to hide
103 //! sections of text.
104 
105 // Make parent null for the file editor tab so that warning WindowModal
106 // messages don't affect grandparents.
108  const QString& directory_arg)
109 : m_octave_qobj (oct_qobj)
110 {
111  m_lexer_apis = nullptr;
112  m_is_octave_file = true;
113  m_lines_changed = false;
114  m_autoc_active = false;
115 
116  m_ced = directory_arg;
117 
118  m_file_name = "";
119  m_file_system_watcher.setObjectName ("_qt_autotest_force_engine_poller");
120 
121  m_edit_area = new octave_qscintilla (this, m_octave_qobj);
122  m_line = 0;
123  m_col = 0;
124 
125  m_bp_lines.clear (); // start with empty lists of breakpoints
126  m_bp_conditions.clear ();
127  m_bp_restore_count = 0;
128 
129  m_breakpoint_info.remove_next = false;
130  m_breakpoint_info.remove_line = -1;
131 
132  // Initialize last modification date to now
133  m_last_modified = QDateTime::currentDateTimeUtc();
134 
135  connect (m_edit_area, SIGNAL (cursorPositionChanged (int, int)),
136  this, SLOT (handle_cursor_moved (int,int)));
137 
138  connect (m_edit_area, SIGNAL (SCN_CHARADDED (int)),
139  this, SLOT (handle_char_added (int)));
140 
141  connect (m_edit_area, SIGNAL (SCN_DOUBLECLICK (int, int, int)),
142  this, SLOT (handle_double_click (int, int, int)));
143 
144  connect (m_edit_area, SIGNAL (linesChanged ()),
145  this, SLOT (handle_lines_changed ()));
146 
147  connect (m_edit_area, &octave_qscintilla::context_menu_edit_signal,
149 
152 
153  // create statusbar for row/col indicator and eol mode
154  m_status_bar = new QStatusBar (this);
155 
156  // row- and col-indicator
157  m_row_indicator = new QLabel ("", this);
158  QFontMetrics fm = m_row_indicator->fontMetrics ();
159  m_row_indicator->setMinimumSize (4.5*fm.averageCharWidth (), 0);
160  QLabel *row_label = new QLabel (tr ("line:"), this);
161  m_col_indicator = new QLabel ("", this);
162  m_col_indicator->setMinimumSize (4*fm.averageCharWidth (), 0);
163  QLabel *col_label = new QLabel (tr ("col:"), this);
164  m_status_bar->addWidget (row_label, 0);
165  m_status_bar->addWidget (m_row_indicator, 0);
166  m_status_bar->addWidget (col_label, 0);
167  m_status_bar->addWidget (m_col_indicator, 0);
168 
169  // status bar: encoding
170  QLabel *enc_label = new QLabel (tr ("encoding:"), this);
171  m_enc_indicator = new QLabel ("", this);
172  m_status_bar->addWidget (enc_label, 0);
173  m_status_bar->addWidget (m_enc_indicator, 0);
174  m_status_bar->addWidget (new QLabel (" ", this), 0);
175 
176  // status bar: eol mode
177  QLabel *eol_label = new QLabel (tr ("eol:"), this);
178  m_eol_indicator = new QLabel ("", this);
179  m_status_bar->addWidget (eol_label, 0);
180  m_status_bar->addWidget (m_eol_indicator, 0);
181  m_status_bar->addWidget (new QLabel (" ", this), 0);
182 
183  // symbols
184  m_edit_area->setMarginType (1, QsciScintilla::SymbolMargin);
185  m_edit_area->setMarginSensitivity (1, true);
186  m_edit_area->markerDefine (QsciScintilla::RightTriangle, marker::bookmark);
187  m_edit_area->setMarkerBackgroundColor (QColor (0, 0, 232), marker::bookmark);
188  m_edit_area->markerDefine (QsciScintilla::Circle, marker::breakpoint);
189  m_edit_area->setMarkerBackgroundColor (QColor (192, 0, 0), marker::breakpoint);
190  m_edit_area->markerDefine (QsciScintilla::Circle, marker::cond_break);
191  m_edit_area->setMarkerBackgroundColor (QColor (255, 127, 0), marker::cond_break);
192  m_edit_area->markerDefine (QsciScintilla::RightArrow,
194  m_edit_area->setMarkerBackgroundColor (QColor (255, 255, 0),
196  m_edit_area->markerDefine (QsciScintilla::RightArrow,
198  m_edit_area->setMarkerBackgroundColor (QColor (192, 192, 192),
200 
201  connect (m_edit_area, SIGNAL (marginClicked (int, int,
202  Qt::KeyboardModifiers)),
203  this, SLOT (handle_margin_clicked (int, int,
204  Qt::KeyboardModifiers)));
205 
208 
209  // line numbers
210  m_edit_area->setMarginsForegroundColor (QColor (96, 96, 96));
211  m_edit_area->setMarginsBackgroundColor (QColor (232, 232, 220));
212  m_edit_area->setMarginType (2, QsciScintilla::TextMargin);
213 
214  // other features
215  m_edit_area->setBraceMatching (QsciScintilla::StrictBraceMatch);
216  m_edit_area->setAutoIndent (true);
217  m_edit_area->setIndentationWidth (2);
218  m_edit_area->setIndentationsUseTabs (false);
219 
220  m_edit_area->setUtf8 (true);
221 
222  // auto completion
223  m_edit_area->SendScintilla (QsciScintillaBase::SCI_AUTOCSETCANCELATSTART, false);
224 
225  QVBoxLayout *edit_area_layout = new QVBoxLayout ();
226  edit_area_layout->addWidget (m_edit_area);
227  edit_area_layout->addWidget (m_status_bar);
228  edit_area_layout->setMargin (0);
229  edit_area_layout->setSpacing (0);
230  setLayout (edit_area_layout);
231 
232  // Any interpreter_event signal from a file_editor_tab_widget is
233  // handled the same as for the parent main_window object.
234 
237 
240 
241  // connect modified signal
242  connect (m_edit_area, SIGNAL (modificationChanged (bool)),
243  this, SLOT (update_window_title (bool)));
244 
245  connect (m_edit_area, SIGNAL (copyAvailable (bool)),
246  this, SLOT (handle_copy_available (bool)));
247 
248  connect (&m_file_system_watcher, &QFileSystemWatcher::fileChanged,
249  this, [=] (const QString& path) { file_has_changed (path); });
250 
251  connect (this, &file_editor_tab::maybe_remove_next,
253 
254  connect (this, &file_editor_tab::dbstop_if,
256 
259 
260  connect (this, &file_editor_tab::api_entries_added,
262 
265 
266  connect (this, &file_editor_tab::do_save_file_signal,
268 
269  resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
271  if (settings)
272  notice_settings (settings, true);
273 
274  // encoding, not updated with the settings
275  m_encoding = settings->value (ed_default_enc.key, "UTF-8").toString ();
276  m_enc_indicator->setText (m_encoding);
277  // no changes in encoding yet
278  m_new_encoding = m_encoding;
279 }
280 
282 {
283  // Tell all connected markers to self-destruct.
285  emit remove_all_positions ();
286 
287  // Destroy lexer attached to m_edit_area, which is not the parent
288  // of lexer
289  QsciLexer *lexer = m_edit_area->lexer ();
290  if (lexer)
291  {
292  delete lexer;
293  m_edit_area->setLexer (nullptr);
294  }
295 }
296 
297 void file_editor_tab::set_encoding (const QString& new_encoding)
298 {
299  if (new_encoding.isEmpty ())
300  return;
301 
302  m_encoding = new_encoding;
303  m_enc_indicator->setText (m_encoding);
304  if (! m_edit_area->text ().isEmpty ())
305  set_modified (true);
306 }
307 
308 void file_editor_tab::closeEvent (QCloseEvent *e)
309 {
310  int save_dialog = check_file_modified (true);
311  if ((save_dialog == QMessageBox::Cancel) ||
312  (save_dialog == QMessageBox::Save))
313  {
314  // Ignore close event if file is saved or user cancels
315  // closing this window. In case of saving, tab is closed after
316  // successful saving.
317  e->ignore ();
318  }
319  else
320  {
321  e->accept ();
322  emit tab_remove_request ();
323  }
324 }
325 
326 void file_editor_tab::set_current_directory (const QString& dir)
327 {
328  m_ced = dir;
329 }
330 
331 void file_editor_tab::handle_context_menu_edit (const QString& word_at_cursor)
332 {
333  // Search for a subfunction in actual file (this is done first because
334  // Octave finds this function before others with the same name in the
335  // search path.
336  QRegExp rxfun1 ("^[\t ]*function[^=]+=[\t ]*"
337  + word_at_cursor + "[\t ]*\\([^\\)]*\\)[\t ]*$");
338  QRegExp rxfun2 ("^[\t ]*function[\t ]+"
339  + word_at_cursor + "[\t ]*\\([^\\)]*\\)[\t ]*$");
340  QRegExp rxfun3 ("^[\t ]*function[\t ]+"
341  + word_at_cursor + "[\t ]*$");
342  QRegExp rxfun4 ("^[\t ]*function[^=]+=[\t ]*"
343  + word_at_cursor + "[\t ]*$");
344 
345  int pos_fct = -1;
346  QStringList lines = m_edit_area->text ().split ("\n");
347 
348  int line;
349  for (line = 0; line < lines.count (); line++)
350  {
351  if ((pos_fct = rxfun1.indexIn (lines.at (line))) != -1)
352  break;
353  if ((pos_fct = rxfun2.indexIn (lines.at (line))) != -1)
354  break;
355  if ((pos_fct = rxfun3.indexIn (lines.at (line))) != -1)
356  break;
357  if ((pos_fct = rxfun4.indexIn (lines.at (line))) != -1)
358  break;
359  }
360 
361  if (pos_fct > -1)
362  {
363  // reg expr. found: it is an internal function
364  m_edit_area->setCursorPosition (line, pos_fct);
365  m_edit_area->SendScintilla (2232, line); // SCI_ENSUREVISIBLE
366  // SCI_VISIBLEFROMDOCLINE
367  int vis_line = m_edit_area->SendScintilla (2220, line);
368  m_edit_area->SendScintilla (2613, vis_line); // SCI_SETFIRSTVISIBLELINE
369  return;
370  }
371 
372  emit edit_mfile_request (word_at_cursor, m_file_name, m_ced, -1);
373 }
374 
375 // If "dbstop if ..." selected from context menu, create a conditional
376 // breakpoint. The default condition is (a) the existing condition if there
377 // is already a breakpoint, (b) any selected text, or (c) empty
379 {
380  // Ensure editor line numbers match Octave core's line numbers.
381  // Give users the option to save modifications if necessary.
382  if (! unchanged_or_saved ())
383  return;
384 
385  QString cond;
386 
387  // Search for previous condition. FIXME: is there a more direct way?
388  if (m_edit_area->markersAtLine (linenr) & (1 << marker::cond_break))
389  {
391  for (int i = 0; i < m_bp_lines.length (); i++)
392  if (m_bp_lines.value (i) == linenr)
393  {
394  cond = m_bp_conditions.value (i);
395  break;
396  }
397  m_bp_lines.clear ();
398  m_bp_conditions.clear ();
399  }
400 
401  // If text selected by the mouse, default to that instead
402  // If both present, use the OR of them, to avoid accidental overwriting
403  // FIXME: If both are present, show old condition unselected and
404  // the selection (in edit area) selected (in the dialog).
405  if (m_edit_area->hasSelectedText ())
406  {
407  if (cond == "")
408  cond = m_edit_area->selectedText ();
409  else
410  cond = '(' + cond + ") || (" + m_edit_area->selectedText () + ')';
411  }
412 
413  emit dbstop_if ("dbstop if", linenr+1, cond);
414 }
415 
416 // Display dialog in GUI thread to get condition, then emit
417 // interpreter_event signal to check it in the interpreter thread.
418 // If the dialog returns a valid condition, then either emit a signal
419 // to add the breakpoint in the editor tab or a signal to display a
420 // new dialog.
421 
422 void file_editor_tab::handle_dbstop_if (const QString& prompt, int line,
423  const QString& cond)
424 {
425  bool ok;
426  QString new_cond
427  = QInputDialog::getText (this, tr ("Breakpoint condition"),
428  prompt, QLineEdit::Normal, cond, &ok);
429 
430  // If cancel, don't change breakpoint condition.
431 
432  if (ok && ! new_cond.isEmpty ())
433  {
434  // The interpreter_event callback function below emits a signal.
435  // Because we don't control when that happens, use a guarded
436  // pointer so that the callback can abort if this object is no
437  // longer valid.
438 
439  QPointer<file_editor_tab> this_fetab (this);
440 
441  emit interpreter_event
442  ([=] (interpreter& interp)
443  {
444  // INTERPRETER THREAD
445 
446  // We are intentionally skipping any side effects that may
447  // occur in the evaluation of NEW_COND if THIS_FETAB is no
448  // longer valid.
449 
450  if (this_fetab.isNull ())
451  return;
452 
453  error_system& es = interp.get_error_system ();
454 
455  unwind_protect frame;
456 
457  // Prevent an error in the evaluation here from sending us
458  // into the debugger.
459 
460  es.interpreter_try (frame);
461 
462  bool eval_error = false;
463  std::string msg;
464 
465  try
466  {
467  tree_evaluator& tw = interp.get_evaluator ();
468  bp_table& bptab = tw.get_bp_table ();
469 
470  bptab.condition_valid (new_cond.toStdString ());
471 
472  // The condition seems OK, so set the conditional
473  // breakpoint.
474 
475  emit request_add_breakpoint (line, new_cond);
476  }
477  catch (const execution_exception& ee)
478  {
479  interp.recover_from_exception ();
480 
481  msg = ee.message ();
482  eval_error = true;
483  }
484  catch (const interrupt_exception&)
485  {
486  interp.recover_from_exception ();
487 
488  msg = "evaluation interrupted";
489  eval_error = true;
490  }
491 
492  if (eval_error)
493  {
494  // Try again with a prompt that indicates the last
495  // attempt was an error.
496 
497  QString new_prompt = (tr ("ERROR: ")
498  + QString::fromStdString (msg)
499  + "\n\ndbstop if");
500 
501  emit dbstop_if (new_prompt, line, "");
502  }
503  });
504  }
505 }
506 
507 void file_editor_tab::set_file_name (const QString& fileName)
508 {
509  // update tracked file if we really have a file on disk
510  QStringList trackedFiles = m_file_system_watcher.files ();
511  if (! trackedFiles.isEmpty ())
512  m_file_system_watcher.removePath (m_file_name);
513  if (! fileName.isEmpty () && QFile::exists (fileName))
514  {
515  m_file_system_watcher.addPath (fileName);
516  m_last_modified = QFileInfo (fileName).lastModified ().toUTC ();
517  }
518 
519  // update lexer and file name variable if file name changes
520  if (m_file_name != fileName)
521  {
522  m_file_name = fileName;
523  update_lexer ();
524  }
525 
526  // set the window title to actual filename (not modified)
527  update_window_title (m_edit_area->isModified ());
528 
529  // update the file editor with current editing directory
531  m_edit_area->isModified ());
532 
533  // add the new file to the most-recently-used list
535 }
536 
537 // valid_file_name (file): checks whether "file" names a file.
538 // By default, "file" is empty; then m_file_name is checked
539 bool file_editor_tab::valid_file_name (const QString& file)
540 {
541  if (file.isEmpty ())
542  {
543  if (m_file_name.isEmpty ())
544  return false;
545  else
546  return true;
547  }
548 
549  return true;
550 }
551 
553 {
554  if (do_enable)
556  else
557  m_file_system_watcher.removePath (m_file_name);
558 }
559 
560 // We cannot create a breakpoint when the file is modified
561 // because the line number the editor is providing might
562 // not match what Octave core is interpreting in the
563 // file on disk. This function gives the user the option
564 // to save before creating the breakpoint.
566 {
567  bool retval = true;
568  if (m_edit_area->isModified () || ! valid_file_name ())
569  {
570  int ans = QMessageBox::question (nullptr, tr ("Octave Editor"),
571  tr ("Cannot add breakpoint to modified or unnamed file.\n"
572  "Save and add breakpoint, or cancel?"),
573  QMessageBox::Save | QMessageBox::Cancel, QMessageBox::Save);
574 
575  if (ans == QMessageBox::Save)
576  save_file (m_file_name, false);
577  else
578  retval = false;
579  }
580 
581  return retval;
582 }
583 
584 // Toggle a breakpoint at the editor_linenr or, if this was called by
585 // a click with CTRL pressed, toggle a bookmark at that point.
586 void file_editor_tab::handle_margin_clicked (int margin, int editor_linenr,
587  Qt::KeyboardModifiers state)
588 {
589  if (margin == 1)
590  {
591  unsigned int markers_mask = m_edit_area->markersAtLine (editor_linenr);
592 
593  if (state & Qt::ControlModifier)
594  {
595  if (markers_mask & (1 << marker::bookmark))
596  m_edit_area->markerDelete (editor_linenr, marker::bookmark);
597  else
598  m_edit_area->markerAdd (editor_linenr, marker::bookmark);
599  }
600  else
601  {
602  if (markers_mask & ((1 << marker::breakpoint)
603  | (1 << marker::cond_break)))
604  handle_request_remove_breakpoint (editor_linenr + 1);
605  else
606  {
607  if (unchanged_or_saved ())
608  handle_request_add_breakpoint (editor_linenr + 1, "");
609  }
610  }
611  }
612 }
613 
615 {
616  // Create a new lexer
617  QsciLexer *lexer = nullptr;
618 
619  m_is_octave_file = false;
620 
621  // Find the required lexer from file extensions
622  if (m_file_name.endsWith (".m")
623  || m_file_name.endsWith ("octaverc")
624  || m_file_name.endsWith (".cc-tst"))
625  {
626 #if defined (HAVE_LEXER_OCTAVE)
627  lexer = new QsciLexerOctave ();
628 #elif defined (HAVE_LEXER_MATLAB)
629  lexer = new QsciLexerMatlab ();
630 #else
631  lexer = new octave_txt_lexer ();
632 #endif
633  m_is_octave_file = true;
634  }
635 
636  if (! lexer)
637  {
638  if (m_file_name.endsWith (".c")
639  || m_file_name.endsWith (".cc")
640  || m_file_name.endsWith (".cpp")
641  || m_file_name.endsWith (".cxx")
642  || m_file_name.endsWith (".c++")
643  || m_file_name.endsWith (".h")
644  || m_file_name.endsWith (".hh")
645  || m_file_name.endsWith (".hpp")
646  || m_file_name.endsWith (".h++"))
647  {
648  lexer = new QsciLexerCPP ();
649  }
650  else if (m_file_name.endsWith (".java"))
651  {
652  lexer = new QsciLexerJava ();
653  }
654  else if (m_file_name.endsWith (".pl"))
655  {
656  lexer = new QsciLexerPerl ();
657  }
658  else if (m_file_name.endsWith (".bat"))
659  {
660  lexer = new QsciLexerBatch ();
661  }
662  else if (m_file_name.endsWith (".diff"))
663  {
664  lexer = new QsciLexerDiff ();
665  }
666  else if (m_file_name.endsWith (".sh"))
667  {
668  lexer = new QsciLexerBash ();
669  }
670  else if (! valid_file_name ())
671  {
672  // new, not yet named file: let us assume it is octave
673 #if defined (HAVE_LEXER_OCTAVE)
674  lexer = new QsciLexerOctave ();
675  m_is_octave_file = true;
676 #elif defined (HAVE_LEXER_MATLAB)
677  lexer = new QsciLexerMatlab ();
678  m_is_octave_file = true;
679 #else
680  lexer = new octave_txt_lexer ();
681 #endif
682  }
683  else
684  {
685  // other or no extension
686  lexer = new octave_txt_lexer ();
687  }
688  }
689 
690  // Get any existing lexer
691  QsciLexer *old_lexer = m_edit_area->lexer ();
692 
693  // If new file, no lexer, or lexer has changed,
694  // delete old one and set the newly created as current lexer
695  if (! old_lexer || ! valid_file_name ()
696  || QString(old_lexer->lexer ()) != QString(lexer->lexer ()))
697  {
698  // Delete and set new lexer
699  if (old_lexer)
700  delete old_lexer;
701  m_edit_area->setLexer (lexer);
702 
703  // Build information for auto completion (APIs)
704  m_lexer_apis = new QsciAPIs (lexer);
705 
708 
709  // Get the settings for this new lexer
711  }
712  else
713  {
714  // Otherwise, delete the newly created lexer and
715  // use the old, existing one.
716  delete lexer;
717  }
718 }
719 
720 // Update settings, which are lexer related and have to be updated
721 // when
722 // a) the lexer changes,
723 // b) the settings have changed, or
724 // c) a package was loaded/unloaded
725 void file_editor_tab::update_lexer_settings (bool update_apis_only)
726 {
727  QsciLexer *lexer = m_edit_area->lexer ();
728 
731 
732  if (m_lexer_apis)
733  {
734  m_lexer_apis->cancelPreparation (); // stop preparing if apis exists
735 
736  bool update_apis = false; // flag, whether update of apis files
737 
738  // Get path to prepared api info (cache). Temporarily set the
739  // application name to 'octave' instead of 'GNU Octave' name for
740  // not having blanks in the path.
741  QString tmp_app_name = QCoreApplication::applicationName ();
742  QCoreApplication::setApplicationName ("octave"); // Set new name
743 
744 #if defined (HAVE_QSTANDARDPATHS)
745  QString local_data_path
746  = QStandardPaths::writableLocation (QStandardPaths::CacheLocation);
747 #else
748  QString local_data_path
749  = QDesktopServices::storageLocation (QDesktopServices::CacheLocation);
750 #endif
751 
752  QCoreApplication::setApplicationName ("octave"); // Set temp. name
753 
755  = local_data_path + "/" + QString (OCTAVE_VERSION) + "/qsci/";
756 
757  // get settings which infos are used for octave
758  bool octave_builtins
759  = settings->value (ed_code_completion_octave_builtins).toBool ();
760  bool octave_functions
761  = settings->value (ed_code_completion_octave_functions).toBool ();
762 
763  QCoreApplication::setApplicationName (tmp_app_name); // Restore name
764 
765  if (m_is_octave_file)
766  {
767  // Keywords and Builtins do not change, this information can be
768  // stored in prepared form in a file. Information on function are
769  // changing frequently, then if functions should also be auto-
770  // completed, the date of any existing file is checked.
771 
772  // Keywords are always used
774 
775  // Builtins are only used if the user settings say so
776  if (octave_builtins)
777  m_prep_apis_file += 'b';
778 
779  if (octave_functions)
780  m_prep_apis_file += 'f';
781 
782  m_prep_apis_file += ".pap"; // final name of apis file
783 
784  // check whether the APIs info needs to be prepared and saved
785  QFileInfo apis_file = QFileInfo (m_prep_apis_file);
786 
787  // flag whether apis file needs update
788  update_apis = ! apis_file.exists ();
789 
790  if (octave_functions)
791  {
792  // Functions may change frequently. Update the apis data
793  // if the file is older than a few minutes preventing from
794  // re-preparing data when the user opens several files.
795  QDateTime apis_time = apis_file.lastModified ();
796  if (update_apis_only
797  || QDateTime::currentDateTime () > apis_time.addSecs (180))
798  update_apis = true;
799  }
800 
801  }
802  else
803  {
804  // No octave file, just add extension.
805  m_prep_apis_file = m_prep_apis_path + lexer->lexer () + ".pap";
806  }
807 
808  // Make sure the apis file is usable, otherwise the gui might crash,
809  // e.g., in case of max. number of opened files
810  QFile f (m_prep_apis_file);
811 
812  bool apis_usable = f.open (QIODevice::ReadOnly);
813  if (! apis_usable)
814  {
815  QDir ().mkpath (QFileInfo (f).absolutePath ());
816  apis_usable = f.open (QIODevice::WriteOnly);
817  }
818  if (apis_usable)
819  f.close ();
820 
821  if (apis_usable
822  && (update_apis || ! m_lexer_apis->loadPrepared (m_prep_apis_file)))
823  {
824  // either we have decided to update the apis file or
825  // no prepared info was loaded, prepare and save if possible
826 
827  // create raw apis info
828 
829  m_lexer_apis->clear (); // Clear current contents
830 
831  if (m_is_octave_file)
832  {
833  // The interpreter_event callback function below emits a
834  // signal. Because we don't control when that happens,
835  // use a guarded pointer so that the callback can abort if
836  // this object is no longer valid.
837 
838  QPointer<file_editor_tab> this_fetab (this);
839 
840  emit interpreter_event
841  ([=] (interpreter& interp)
842  {
843  // INTERPRETER THREAD
844 
845  // We can skip the entire callback function because it
846  // does not make any changes to the interpreter
847  // state.
848 
849  if (this_fetab.isNull ())
850  return;
851 
852  QStringList api_entries;
853 
854  octave_value_list tmp = Fiskeyword ();
855  const Cell ctmp = tmp(0).cell_value ();
856  for (octave_idx_type i = 0; i < ctmp.numel (); i++)
857  {
858  std::string kw = ctmp(i).string_value ();
859  api_entries.append (QString::fromStdString (kw));
860  }
861 
862  if (octave_builtins)
863  {
864  symbol_table& symtab = interp.get_symbol_table ();
865 
866  string_vector bfl = symtab.built_in_function_names ();
867 
868  for (octave_idx_type i = 0; i < bfl.numel (); i++)
869  api_entries.append (QString::fromStdString (bfl[i]));
870  }
871 
872  if (octave_functions)
873  {
874  load_path& lp = interp.get_load_path ();
875 
876  string_vector ffl = lp.fcn_names ();
877  string_vector afl = interp.autoloaded_functions ();
878 
879  for (octave_idx_type i = 0; i < ffl.numel (); i++)
880  api_entries.append (QString::fromStdString (ffl[i]));
881 
882  for (octave_idx_type i = 0; i < afl.numel (); i++)
883  api_entries.append (QString::fromStdString (afl[i]));
884  }
885 
886  emit request_add_octave_apis (api_entries);
887  });
888  }
889  else
890  {
891  for (int i = 1; i <= 3; i++)
892  {
893  // Get list, split, and add to API.
894 
895  QString keyword = QString (lexer->keywords (i));
896 
897  QStringList keyword_list
898  = keyword.split (QRegExp (R"(\s+)"));
899 
900  for (int j = 0; j < keyword_list.size (); j++)
901  m_lexer_apis->add (keyword_list.at (j));
902  }
903 
904  emit api_entries_added ();
905  }
906  }
907  }
908 
909  if (update_apis_only)
910  return; // We are done here
911 
912  int mode = settings->value (ed_color_mode).toInt ();
913  rmgr.read_lexer_settings (lexer, settings, mode);
914 
915  m_edit_area->setCaretForegroundColor (lexer->color (0));
916  m_edit_area->setIndentationGuidesForegroundColor (lexer->color (0));
917 
918  // set some colors depending on selected background color of the lexer
919  QColor bg = lexer->paper (0);
920  QColor fg = lexer->color (0);
921 
922  // margin and current line marker colors
923  QColor bgm, fgm;
924 
925  bgm = interpolate_color (bg, fg, 0.5, 0.2);
926  m_edit_area->setEdgeColor (bgm);
927 
928  m_edit_area->setMarkerForegroundColor (lexer->color (0));
929  m_edit_area->setMarginsForegroundColor (lexer->color (0));
930 
931  bgm = interpolate_color (bg, fg, 0.5, 0.125);
932  fgm = interpolate_color (bg, fg, 0.5, 0.25);
933  m_edit_area->setMarginsBackgroundColor (bgm);
934  m_edit_area->setFoldMarginColors (bgm, fgm);
935 
936  QColor current_line_bg
937  = settings->color_value (ed_highlight_current_line_color, mode);
938  if (current_line_bg == settings_color_no_change)
939  bgm = interpolate_color (bg, fg, 0.5, 0.1); // It is the "auto" color
940  else
941  bgm = current_line_bg; // Specific color given
942 
943  m_edit_area->setCaretLineBackgroundColor (bgm);
944 
945  // color indicator for highlighting all occurrences:
946  // applications highlight color with more transparency
947  QColor hg = QApplication::palette ().color (QPalette::Highlight);
949 
950  // fix line number width with respect to the font size of the lexer and
951  // set the line numbers font depending on the lexer's font
952  if (settings->value (ed_show_line_numbers).toBool ())
953  {
954  // Line numbers width
956 
957  // Line numbers font
958  QFont line_numbers_font = lexer->defaultFont ();
959  int font_size = line_numbers_font.pointSize ();
960  font_size = font_size
961  + settings->value (ed_line_numbers_size).toInt ();
962  if (font_size < 4)
963  font_size = 4;
964  line_numbers_font.setPointSize (font_size);
965 
966  m_edit_area->setMarginsFont (line_numbers_font);
967  }
968  else
969  m_edit_area->setMarginWidth (2, 0);
970 }
971 
972 // function for adding entries to the octave lexer's APIs
973 void file_editor_tab::handle_add_octave_apis (const QStringList& api_entries)
974 {
975  for (int idx = 0; idx < api_entries.size (); idx++)
976  m_lexer_apis->add (api_entries.at (idx));
977 
978  emit api_entries_added ();
979 }
980 
982 {
983  // disconnect slot for saving prepared info if already connected
984  disconnect (m_lexer_apis, &QsciAPIs::apiPreparationFinished,
985  nullptr, nullptr);
986 
987  // check whether path for prepared info exists or can be created
988  if (QDir ("/").mkpath (m_prep_apis_path))
989  {
990  // path exists, apis info can be saved there
991  connect (m_lexer_apis, &QsciAPIs::apiPreparationFinished,
993  }
994 
995  m_lexer_apis->prepare (); // prepare apis info
996 }
997 
999 {
1000  m_lexer_apis->savePrepared (m_prep_apis_file);
1001 }
1002 
1003 // slot for fetab_set_focus: sets the focus to the current edit area
1005 {
1006  if (ID != this)
1007  return;
1008  m_edit_area->setFocus ();
1009  emit edit_area_changed (m_edit_area); // update the edit area in find dlg
1010 }
1011 
1012 void file_editor_tab::context_help (const QWidget *ID, bool doc)
1013 {
1014  if (ID != this)
1015  return;
1016 
1018 }
1019 
1021 {
1022  if (ID != this)
1023  return;
1024 
1026 }
1027 
1029 {
1030  if (ID != this)
1031  return;
1032 
1034 }
1035 
1036 void file_editor_tab::save_file (const QWidget *ID, const QString& fileName,
1037  bool remove_on_success)
1038 {
1039  if (ID != this)
1040  return;
1041 
1042  save_file (fileName, remove_on_success);
1043 }
1044 
1046 {
1047  if (ID != this)
1048  return;
1049 
1050  save_file_as ();
1051 }
1052 
1054 {
1055  if (ID != this)
1056  return;
1057 
1058  QsciPrinter *printer = new QsciPrinter (QPrinter::HighResolution);
1059 
1060  QPrintDialog printDlg (printer, this);
1061 
1062  if (printDlg.exec () == QDialog::Accepted)
1063  printer->printRange (m_edit_area);
1064 
1065  delete printer;
1066 }
1067 
1068 void file_editor_tab::run_file (const QWidget *ID, bool step_into)
1069 {
1070  if (ID != this)
1071  return;
1072 
1073  if (m_edit_area->isModified () || ! valid_file_name ())
1074  {
1075  save_file (m_file_name); // save file dialog
1076 
1077  // Running a file is disabled for non-octave files. But when saving
1078  // a new file, an octave file is assumed but might actually saved
1079  // as another file or with an invalid file name.
1080  if (! (m_is_octave_file && valid_file_name ()))
1081  return;
1082  }
1083 
1084  if (step_into)
1085  {
1086  // Get current first breakpoint and set breakpoint waiting for
1087  // the returned line number. Store whether to remove this breakpoint
1088  // afterwards.
1089  int first_bp_line
1090  = m_edit_area->markerFindNext (0, (1 << marker::breakpoint)) + 1;
1091 
1092  // Set flag for storing the line number of the breakpoint
1094  m_breakpoint_info.do_not_remove_line = first_bp_line;
1095 
1096  // Add breakpoint, storing its line number
1097  handle_request_add_breakpoint (1, QString ());
1098  }
1099 
1100  QFileInfo info (m_file_name);
1101  emit run_file_signal (info);
1102 }
1103 
1105 {
1106  if (ID != this)
1107  return;
1108 
1110 }
1111 
1113 {
1114  if (ID != this)
1115  return;
1116 
1117  int line, cur;
1118  m_edit_area->getCursorPosition (&line, &cur);
1119 
1120  if (m_edit_area->markersAtLine (line) & (1 << marker::bookmark))
1121  m_edit_area->markerDelete (line, marker::bookmark);
1122  else
1123  m_edit_area->markerAdd (line, marker::bookmark);
1124 }
1125 
1126 // Move the text cursor to the closest bookmark
1127 // after the current line.
1129 {
1130  if (ID != this)
1131  return;
1132 
1133  int line, cur;
1134  m_edit_area->getCursorPosition (&line, &cur);
1135 
1136  line++; // Find bookmark strictly after the current line.
1137 
1138  int nextline = m_edit_area->markerFindNext (line, (1 << marker::bookmark));
1139 
1140  // Wrap.
1141  if (nextline == -1)
1142  nextline = m_edit_area->markerFindNext (1, (1 << marker::bookmark));
1143 
1144  m_edit_area->setCursorPosition (nextline, 0);
1145 }
1146 
1147 // Move the text cursor to the closest bookmark
1148 // before the current line.
1150 {
1151  if (ID != this)
1152  return;
1153 
1154  int line, cur;
1155  m_edit_area->getCursorPosition (&line, &cur);
1156 
1157  line--; // Find bookmark strictly before the current line.
1158 
1159  int prevline = m_edit_area->markerFindPrevious (line, (1 << marker::bookmark));
1160 
1161  // Wrap. Should use the last line of the file, not 1<<15
1162  if (prevline == -1)
1163  prevline = m_edit_area->markerFindPrevious (m_edit_area->lines (),
1164  (1 << marker::bookmark));
1165 
1166  m_edit_area->setCursorPosition (prevline, 0);
1167 }
1168 
1170 {
1171  QString bmlist;
1172  int line = 0;
1173 
1174  while (line > -1)
1175  {
1176  line = m_edit_area->markerFindNext (line, (1 << marker::bookmark));
1177  if (line > -1)
1178  {
1179  if (! bmlist.isEmpty ())
1180  bmlist += ",";
1181  bmlist += QString::number (line);
1182  line++; // search from next line, otherwise same line found again
1183  }
1184  }
1185 
1186  return bmlist;
1187 }
1188 
1190 {
1191  if (ID != this)
1192  return;
1193 
1194  m_edit_area->markerDeleteAll (marker::bookmark);
1195 }
1196 
1197 void
1199  const QString& condition)
1200 {
1201  if (! m_is_octave_file)
1202  return;
1203 
1204  add_breakpoint_event (line, condition);
1205 }
1206 
1208 {
1209  emit interpreter_event
1210  ([=] (interpreter& interp)
1211  {
1212  // INTERPRETER THREAD
1213 
1214  tree_evaluator& tw = interp.get_evaluator ();
1215  bp_table& bptab = tw.get_bp_table ();
1216 
1217  bptab.remove_breakpoint_from_file (m_file_name.toStdString (), line);
1218  });
1219 }
1220 
1222 {
1223  if (ID != this)
1224  return;
1225 
1226  int editor_linenr, cur;
1227  m_edit_area->getCursorPosition (&editor_linenr, &cur);
1228 
1229  if (m_edit_area->markersAtLine (editor_linenr) & (1 << marker::breakpoint))
1231  else
1232  {
1233  if (unchanged_or_saved ())
1234  handle_request_add_breakpoint (editor_linenr + 1, "");
1235  }
1236 }
1237 
1238 // Move the text cursor to the closest breakpoint (conditional or unconditional)
1239 // after the current line.
1241 {
1242  if (ID != this)
1243  return;
1244 
1245  int line, cur;
1246  m_edit_area->getCursorPosition (&line, &cur);
1247 
1248  line++; // Find breakpoint strictly after the current line.
1249 
1250  int nextline = m_edit_area->markerFindNext (line, (1 << marker::breakpoint));
1251  int nextcond = m_edit_area->markerFindNext (line, (1 << marker::cond_break));
1252 
1253  // Check if the next conditional breakpoint is before next unconditional one.
1254  if (nextcond != -1 && (nextcond < nextline || nextline == -1))
1255  nextline = nextcond;
1256 
1257  m_edit_area->setCursorPosition (nextline, 0);
1258 }
1259 
1260 // Move the text cursor to the closest breakpoint (conditional or unconditional)
1261 // before the current line.
1263 {
1264  if (ID != this)
1265  return;
1266 
1267  int line, cur, prevline, prevcond;
1268  m_edit_area->getCursorPosition (&line, &cur);
1269 
1270  line--; // Find breakpoint strictly before the current line.
1271 
1272  prevline = m_edit_area->markerFindPrevious (line, (1 << marker::breakpoint));
1273  prevcond = m_edit_area->markerFindPrevious (line, (1 << marker::cond_break));
1274 
1275  // Check if the prev conditional breakpoint is closer than the unconditional.
1276  if (prevcond != -1 && prevcond > prevline)
1277  prevline = prevcond;
1278 
1279  m_edit_area->setCursorPosition (prevline, 0);
1280 }
1281 
1283 {
1284  if (ID != this)
1285  return;
1286 
1287  emit interpreter_event
1288  ([=] (interpreter& interp)
1289  {
1290  // INTERPRETER THREAD
1291 
1292  tree_evaluator& tw = interp.get_evaluator ();
1293  bp_table& bptab = tw.get_bp_table ();
1294 
1295  bptab.remove_all_breakpoints_from_file (m_file_name.toStdString (),
1296  true);
1297  });
1298 }
1299 
1301  unsigned int sci_msg)
1302 {
1303  if (ID != this)
1304  return;
1305 
1306  m_edit_area->SendScintilla (sci_msg);
1307 }
1308 
1310  bool input_str)
1311 {
1312  if (ID != this)
1313  return;
1314 
1315  do_comment_selected_text (true, input_str);
1316 }
1317 
1319 {
1320  if (ID != this)
1321  return;
1322 
1323  do_comment_selected_text (false);
1324 }
1325 
1327 {
1328  if (ID != this)
1329  return;
1330 
1331  do_indent_selected_text (true);
1332 }
1333 
1335 {
1336  if (ID != this)
1337  return;
1338 
1339  do_indent_selected_text (false);
1340 }
1341 
1343 {
1344  if (ID != this)
1345  return;
1346 
1348 }
1349 
1351  QsciScintilla::EolMode eol_mode)
1352 {
1353  if (ID != this)
1354  return;
1355 
1356  m_edit_area->convertEols (eol_mode);
1357  m_edit_area->setEolMode (eol_mode);
1359 }
1360 
1362 {
1363  if (ID != this)
1364  return;
1365 
1366  m_edit_area->zoomIn (1);
1367  auto_margin_width ();
1368 }
1369 
1371 {
1372  if (ID != this)
1373  return;
1374 
1375  m_edit_area->zoomOut (1);
1376  auto_margin_width ();
1377 }
1378 
1380 {
1381  if (ID != this)
1382  return;
1383 
1384  m_edit_area->zoomTo (0);
1385  auto_margin_width ();
1386 }
1387 
1388 void file_editor_tab::add_breakpoint_event (int line, const QString& cond)
1389 {
1390  // The interpreter_event callback function below emits a signal.
1391  // Because we don't control when that happens, use a guarded pointer
1392  // so that the callback can abort if this object is no longer valid.
1393 
1394  QPointer<file_editor_tab> this_fetab (this);
1395 
1396  emit interpreter_event
1397  ([=] (interpreter& interp)
1398  {
1399  // INTERPRETER THREAD
1400 
1401  // If THIS_FETAB is no longer valid, we still want to set the
1402  // breakpoint in the interpreter but we can't emit the signal
1403  // associated with THIS_FETAB.
1404 
1405  // FIXME: note duplication with the code in
1406  // handle_context_menu_break_condition.
1407 
1408  tree_evaluator& tw = interp.get_evaluator ();
1409  bp_table& bptab = tw.get_bp_table ();
1410 
1411  int lineno = bptab.add_breakpoint_in_file (m_file_name.toStdString (),
1412  line, cond.toStdString ());
1413  if (this_fetab.isNull ())
1414  return;
1415 
1416  if (lineno)
1417  emit maybe_remove_next (lineno);
1418  });
1419 }
1420 
1422 {
1423  // Store some info breakpoint
1425  {
1426  m_breakpoint_info.remove_line = remove_line;
1428  }
1429 }
1430 
1431 void file_editor_tab::goto_line (const QWidget *ID, int line)
1432 {
1433  if (ID != this)
1434  return;
1435 
1436  if (m_bp_restore_count > 0)
1437  {
1438  // This goto-line request is invoked by restoring a breakpoint during
1439  // saving the file, thus, do not go to the related line
1441  return;
1442  }
1443 
1444  if (line <= 0) // ask for desired line
1445  {
1446  bool ok = false;
1447  int index;
1448  m_edit_area->getCursorPosition (&line, &index);
1449  line = QInputDialog::getInt (m_edit_area, tr ("Goto line"),
1450  tr ("Line number"), line+1, 1,
1451  m_edit_area->lines (), 1, &ok);
1452  if (ok)
1453  m_edit_area->setCursorPosition (line-1, 0);
1454  }
1455  else // go to given line without dialog
1456  m_edit_area->setCursorPosition (line-1, 0);
1457 
1458  center_current_line (false); // only center line if at top or bottom
1459 }
1460 
1461 void file_editor_tab::move_match_brace (const QWidget *ID, bool select)
1462 {
1463  if (ID != this)
1464  return;
1465 
1466  if (select)
1467  m_edit_area->selectToMatchingBrace ();
1468  else
1469  m_edit_area->moveToMatchingBrace ();
1470 }
1471 
1473 {
1474  if (ID != this)
1475  return;
1476 
1477  m_autoc_active = true;
1478 
1479  QsciScintilla::AutoCompletionSource s = m_edit_area->autoCompletionSource ();
1480  switch (s)
1481  {
1482  case QsciScintilla::AcsAll:
1483  m_edit_area->autoCompleteFromAll ();
1484  break;
1485 
1486  case QsciScintilla::AcsAPIs:
1487  m_edit_area->autoCompleteFromAPIs ();
1488  break;
1489 
1490  case QsciScintilla::AcsDocument:
1491  m_edit_area->autoCompleteFromDocument ();
1492  break;
1493 
1494  case QsciScintilla::AcsNone:
1495  break;
1496  }
1497 }
1498 
1500 {
1501  // FIXME:
1502  m_edit_area->beginUndoAction ();
1503 
1504  if (m_edit_area->hasSelectedText ())
1505  {
1506  int lineFrom, lineTo, colFrom, colTo;
1507  m_edit_area->getSelection (&lineFrom, &colFrom, &lineTo, &colTo);
1508 
1509  if (colTo == 0) // the beginning of last line is not selected
1510  lineTo--; // stop at line above
1511 
1512  for (int i = lineFrom; i <= lineTo; i++)
1513  {
1514  if (indent)
1515  m_edit_area->indent (i);
1516  else
1517  m_edit_area->unindent (i);
1518  }
1519  //set selection on (un)indented section
1520  m_edit_area->setSelection (lineFrom, 0, lineTo,
1521  m_edit_area->text (lineTo).length ()-1);
1522  }
1523  else
1524  {
1525  int cpline, col;
1526  m_edit_area->getCursorPosition (&cpline, &col);
1527  if (indent)
1528  m_edit_area->indent (cpline);
1529  else
1530  m_edit_area->unindent (cpline);
1531  }
1532 
1533  m_edit_area->endUndoAction ();
1534 }
1535 
1537 {
1538  m_edit_area->beginUndoAction ();
1539 
1540  int lineFrom, lineTo;
1541 
1542  if (m_edit_area->hasSelectedText ())
1543  {
1544  int colFrom, colTo;
1545  m_edit_area->getSelection (&lineFrom, &colFrom, &lineTo, &colTo);
1546 
1547  if (colTo == 0) // the beginning of last line is not selected
1548  lineTo--; // stop at line above
1549  }
1550  else
1551  {
1552  int col;
1553  m_edit_area->getCursorPosition (&lineFrom, &col);
1554 
1555  lineTo = lineFrom;
1556  }
1557 
1558  m_edit_area->smart_indent_line_or_selected_text (lineFrom, lineTo);
1559 
1560  m_edit_area->endUndoAction ();
1561 }
1562 
1563 void file_editor_tab::do_comment_selected_text (bool comment, bool input_str)
1564 {
1565  QRegExp rxc;
1566  QString ws = "^(?:[ \\t]*)";
1567  QStringList comment_str = m_edit_area->comment_string (comment);
1568  QString used_comment_str = comment_str.at (0);
1569 
1570  if (comment)
1571  {
1572  if (input_str)
1573  {
1574  bool ok;
1576  gui_settings *settings = rmgr.get_settings ();
1577 
1578  used_comment_str
1579  = QInputDialog::getText (this, tr ("Comment selected text"),
1580  tr ("Comment string to use:\n"),
1581  QLineEdit::Normal,
1582  settings->value (ed_last_comment_str, comment_str.at (0)).toString (),
1583  &ok);
1584 
1585  if ((! ok) || used_comment_str.isEmpty ())
1586  return; // No input, do nothing
1587  else
1588  settings->setValue (ed_last_comment_str, used_comment_str); // Store last
1589  }
1590  }
1591  else
1592  {
1593  // Uncommenting (several strings possible)
1594 
1595  // Sort strings according their length
1596  QStringList comment_str_sorted (comment_str.at (0));
1597  bool inserted;
1598 
1599  for (int i = 1; i < comment_str.length (); i++)
1600  {
1601  inserted = false;
1602  for (int j = 0; j < comment_str_sorted.length (); j++)
1603  {
1604  if (comment_str.at (i).length () > comment_str_sorted.at (j).length ())
1605  {
1606  comment_str_sorted.insert (j, comment_str.at (i));
1607  inserted = true;
1608  break;
1609  }
1610  }
1611  if (! inserted)
1612  comment_str_sorted << comment_str.at (i);
1613  }
1614 
1615  // Create regular expression
1616  QString regexp;
1617  for (int i = 0; i < comment_str_sorted.length (); i++)
1618  {
1619  if (i > 0)
1620  regexp = regexp + QString ("|");
1621  regexp = regexp + comment_str_sorted.at (i);
1622  }
1623  rxc = QRegExp (ws + "(" + regexp + ")");
1624  }
1625 
1626  // Do the commenting/uncommenting
1627  int len = 0, lenc = 0;
1628  m_edit_area->beginUndoAction ();
1629 
1630  if (m_edit_area->hasSelectedText ())
1631  {
1632  int lineFrom, lineTo, colFrom, colTo;
1633  int change_col_from = 1;
1634  int change_col_to = 1;
1635  bool removed;
1636 
1637  m_edit_area->getSelection (&lineFrom, &colFrom, &lineTo, &colTo);
1638 
1639  if (colTo == 0) // the beginning of last line is not selected
1640  lineTo--; // stop at line above
1641 
1642  for (int i = lineFrom; i <= lineTo; i++)
1643  {
1644  if (comment)
1645  {
1646  m_edit_area->insertAt (used_comment_str, i, 0);
1647  }
1648  else
1649  {
1650  QString line (m_edit_area->text (i));
1651  if ((removed = line.contains (rxc)))
1652  {
1653  len = rxc.matchedLength (); // complete length
1654  QString matched_text = rxc.capturedTexts ().at (0);
1655  lenc = matched_text.remove (QRegExp (ws)).length (); // only comment string
1656  m_edit_area->setSelection (i, len-lenc, i, len);
1657  m_edit_area->removeSelectedText ();
1658  }
1659 
1660  // handle case, where the selection remains unchanged
1661  if (i == lineFrom && (colFrom < len-lenc || ! removed))
1662  change_col_from = 0; // do not change start of selection
1663  if (i == lineTo && (colTo < len-lenc || ! removed))
1664  change_col_to = 0; // do not change end of selection
1665  }
1666  }
1667 
1668  // update the selection area
1669  if (comment)
1670  {
1671  colFrom = colFrom + lenc; // shift start position by comment length
1672  if (colTo > 0)
1673  colTo = colTo + lenc; // shift end position by comment length
1674  else
1675  lineTo++; // colTo == 0 , fully select previous line
1676  }
1677  else
1678  {
1679  if (colTo == 0)
1680  lineTo++; // colTo == 0 , fully select previous line
1681  colFrom = colFrom - change_col_from*lenc;
1682  colTo = colTo - change_col_to*lenc;
1683  }
1684 
1685  // set updated selection area
1686  m_edit_area->setSelection (lineFrom, colFrom, lineTo, colTo);
1687  }
1688  else
1689  {
1690  int cpline, col;
1691  m_edit_area->getCursorPosition (&cpline, &col);
1692  if (comment)
1693  m_edit_area->insertAt (used_comment_str, cpline, 0);
1694  else
1695  {
1696  QString line (m_edit_area->text (cpline));
1697  if (line.contains (rxc))
1698  {
1699  len = rxc.matchedLength (); // complete length
1700  QString matched_text = rxc.capturedTexts ().at (0);
1701  lenc = matched_text.remove (QRegExp (ws)).length (); // only comment string
1702  m_edit_area->setSelection (cpline, len-lenc, cpline, len);
1703  m_edit_area->removeSelectedText ();
1704  }
1705  }
1706  }
1707  m_edit_area->endUndoAction ();
1708 }
1709 
1711 {
1712  QString title ("");
1713  QString tooltip ("");
1714 
1715  if (! valid_file_name ())
1716  title = tr ("<unnamed>");
1717  else
1718  {
1719  QFileInfo file (m_file_name);
1720  title = file.fileName ();
1721  tooltip = m_file_name;
1722  }
1723 
1724  emit file_name_changed (title, tooltip, modified);
1725 }
1726 
1728 {
1729  m_copy_available = enableCopy;
1731  m_edit_area->isModified ());
1732 }
1733 
1734 // show_dialog: shows a modal or non modal dialog depending on input arg
1735 void file_editor_tab::show_dialog (QDialog *dlg, bool modal)
1736 {
1737  dlg->setAttribute (Qt::WA_DeleteOnClose);
1738  if (modal)
1739  dlg->exec ();
1740  else
1741  {
1742  dlg->setWindowModality (Qt::NonModal);
1743  dlg->show ();
1744  }
1745 }
1746 
1748 {
1749  int decision = QMessageBox::Yes;
1750  if (m_edit_area->isModified ())
1751  {
1752  // File is modified but not saved, ask user what to do. The file
1753  // editor tab can't be made parent because it may be deleted depending
1754  // upon the response. Instead, change the m_edit_area to read only.
1755  QMessageBox::StandardButtons buttons = QMessageBox::Save |
1756  QMessageBox::Discard |
1757  QMessageBox::Cancel;
1758 
1759  // For now, just a warning message about closing a tab that has been
1760  // modified seems sufficient. Exit-condition-specific messages could
1761  // be achieved by making 'available_actions' a function input string.
1762  QString available_actions =
1763  tr ("Do you want to cancel closing, save or discard the changes?");
1764 
1765  QString file;
1766  if (valid_file_name ())
1767  file = m_file_name;
1768  else
1769  file = tr ("<unnamed>");
1770 
1771  QMessageBox *msgBox
1772  = new QMessageBox (QMessageBox::Warning, tr ("Octave Editor"),
1773  tr ("The file\n\n"
1774  " %1\n\n"
1775  "is about to be closed but has been modified. "
1776  "%2").
1777  arg (file). arg (available_actions),
1778  buttons, qobject_cast<QWidget *> (parent ()));
1779 
1780  msgBox->setDefaultButton (QMessageBox::Save);
1781  m_edit_area->setReadOnly (true);
1782 
1783  decision = msgBox->exec (); // show_dialog (msgBox, true);
1784 
1785  if (decision == QMessageBox::Cancel)
1786  m_edit_area->setReadOnly (false);
1787  else if (decision == QMessageBox::Save)
1788  save_file (m_file_name, remove, false);
1789  else
1790  emit tab_ready_to_close ();
1791  }
1792  else
1793  {
1794  emit tab_ready_to_close ();
1795  }
1796 
1797  return decision;
1798 }
1799 
1800 void file_editor_tab::set_modified (bool modified)
1801 {
1802  m_edit_area->setModified (modified);
1803 }
1804 
1806 {
1807  // reset the possibly still existing read only state
1808  m_edit_area->setReadOnly (false);
1809 
1810  // if we are in this slot and the list of breakpoints is not empty,
1811  // then this tab was saved during an exit of the applications (not
1812  // restoring the breakpoints and not emptying the list) and the user
1813  // canceled this closing late on.
1815 }
1816 
1818 {
1819  if (! m_bp_lines.isEmpty ())
1820  {
1821  // At least one breakpoint is present.
1822  // Get rid of breakpoints at old (now possibly invalid) linenumbers
1823  remove_all_breakpoints (this);
1824 
1825  // and set breakpoints at the new linenumbers
1826  m_bp_restore_count = m_bp_lines.length ();
1827  for (int i = 0; i < m_bp_lines.length (); i++)
1829  m_bp_conditions.value (i));
1830 
1831  // Keep the list of breakpoints empty, except after explicit requests.
1832  m_bp_lines.clear ();
1833  m_bp_conditions.clear ();
1834  }
1835 }
1836 
1837 QString file_editor_tab::load_file (const QString& fileName)
1838 {
1839  // get the absolute path
1840  QFileInfo file_info = QFileInfo (fileName);
1841  QString file_to_load;
1842  if (file_info.exists ())
1843  file_to_load = file_info.canonicalFilePath ();
1844  else
1845  file_to_load = fileName;
1846  QFile file (file_to_load);
1847  if (!file.open(QIODevice::ReadOnly))
1848  return file.errorString ();
1849 
1850  int col = 0, line = 0;
1851  if (fileName == m_file_name)
1852  {
1853  // We have to reload the current file, thus get current cursor position
1854  line = m_line;
1855  col = m_col;
1856  }
1857 
1858  QApplication::setOverrideCursor (Qt::WaitCursor);
1859 
1860  // read the file binary, decoding later
1861  QByteArray text_data = file.readAll ();
1862 
1863  // remove newline at end of file if we add one again when saving
1865  gui_settings *settings = rmgr.get_settings ();
1866 
1867  if (settings->value (ed_force_newline).toBool ())
1868  {
1869  const QByteArray eol_lf = QByteArray (1, 0x0a);
1870  const QByteArray eol_cr = QByteArray (1, 0x0d);
1871 
1872  if (text_data.endsWith (eol_lf))
1873  text_data.chop (1); // remove LF
1874 
1875  if (text_data.endsWith (eol_cr)) // remove CR (altogether CRLF, too)
1876  text_data.chop (1);
1877  }
1878 
1879  // decode
1880  QTextCodec::ConverterState st;
1881  QTextCodec *codec = QTextCodec::codecForName (m_encoding.toLatin1 ());
1882  if (codec == nullptr)
1883  codec = QTextCodec::codecForLocale ();
1884 
1885  const QString text = codec->toUnicode(text_data.constData(),
1886  text_data.size(), &st);
1887 
1888  // Decoding with invalid characters?
1889  if (st.invalidChars > 0)
1890  {
1891  // Set read only
1892  m_edit_area->setReadOnly (true);
1893 
1894  // Message box for user decision
1895  QString msg = tr ("There were problems reading the file\n"
1896  "%1\n"
1897  "with the selected encoding %2.\n\n"
1898  "Modifying and saving the file might "
1899  "cause data loss!")
1900  .arg (file_to_load).arg (m_encoding);
1901  QMessageBox *msg_box = new QMessageBox ();
1902  msg_box->setIcon (QMessageBox::Warning);
1903  msg_box->setText (msg);
1904  msg_box->setWindowTitle (tr ("Octave Editor"));
1905  msg_box->addButton (tr ("&Edit anyway"), QMessageBox::YesRole);
1906  msg_box->addButton (tr ("Chan&ge encoding"), QMessageBox::AcceptRole);
1907  msg_box->addButton (tr ("&Close"), QMessageBox::RejectRole);
1908 
1909  connect (msg_box, &QMessageBox::buttonClicked,
1911 
1912  msg_box->setWindowModality (Qt::WindowModal);
1913  msg_box->setAttribute (Qt::WA_DeleteOnClose);
1914  msg_box->show ();
1915  }
1916 
1917  m_edit_area->setText (text);
1918  m_edit_area->setEolMode (detect_eol_mode ());
1919 
1920  QApplication::restoreOverrideCursor ();
1921 
1922  m_copy_available = false; // no selection yet available
1923  m_edit_area->setModified (false); // loaded file is not modified yet
1924  set_file_name (file_to_load);
1925 
1927 
1928  m_edit_area->setCursorPosition (line, col);
1929 
1930  return QString ();
1931 }
1932 
1934 {
1935  QString txt = btn->text ();
1936 
1937  if (txt == tr ("&Close"))
1938  {
1939  // Just close the file
1940  close ();
1941  return;
1942  }
1943 
1944  if (txt == tr ("Chan&ge encoding"))
1945  {
1946  // Dialog for reloading the file with another encoding
1947  QDialog dlg;
1948  dlg.setWindowTitle (tr ("Select new default encoding"));
1949 
1950  QLabel *text
1951  = new QLabel (tr ("Please select a new encoding\n"
1952  "for reloading the current file.\n\n"
1953  "This does not change the default encoding.\n"));
1954 
1955  QComboBox *enc_combo = new QComboBox ();
1957  rmgr.combo_encoding (enc_combo);
1958  m_new_encoding = enc_combo->currentText ();
1959  connect (enc_combo, &QComboBox::currentTextChanged,
1961 
1962  QDialogButtonBox *buttons
1963  = new QDialogButtonBox (QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
1964  Qt::Horizontal);
1965  connect (buttons, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
1966  connect (buttons, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
1967 
1968  QGridLayout *main_layout = new QGridLayout;
1969  main_layout->setSizeConstraint (QLayout::SetFixedSize);
1970  main_layout->addWidget (text, 0, 0);
1971  main_layout->addWidget (enc_combo, 1, 0);
1972  main_layout->addWidget (buttons, 2, 0);
1973  dlg.setLayout (main_layout);
1974 
1975  int answer = dlg.exec ();
1976 
1977  if (answer == QDialog::Accepted)
1978  {
1979  // Reload the file with new encoding but using the same tab
1980  QString reload_file_name = m_file_name; // store file name
1981  m_file_name = ""; // force reuse of this tab when opening a new file
1982  emit request_open_file (reload_file_name, m_new_encoding);
1983  }
1984  }
1985 
1986  // Continue editing, set writable again
1987  m_edit_area->setReadOnly (false);
1988 }
1989 
1991 {
1992  m_new_encoding = enc;
1993 }
1994 
1995 QsciScintilla::EolMode file_editor_tab::detect_eol_mode (void)
1996 {
1997  QByteArray text = m_edit_area->text ().toLatin1 ();
1998 
1999  QByteArray eol_lf = QByteArray (1, 0x0a);
2000  QByteArray eol_cr = QByteArray (1, 0x0d);
2001  QByteArray eol_crlf = eol_cr;
2002  eol_crlf.append (eol_lf);
2003 
2004  int count_crlf = text.count (eol_crlf);
2005  int count_lf = text.count (eol_lf) - count_crlf; // isolated lf
2006  int count_cr = text.count (eol_cr) - count_crlf; // isolated cr
2007 
2009  gui_settings *settings = rmgr.get_settings ();
2010  QsciScintilla::EolMode eol_mode
2011  = static_cast<QsciScintilla::EolMode> (settings->value (ed_default_eol_mode).toInt ());
2012 
2013  int count_max = 0;
2014 
2015  if (count_crlf > count_max)
2016  {
2017  eol_mode = QsciScintilla::EolWindows;
2018  count_max = count_crlf;
2019  }
2020  if (count_lf > count_max)
2021  {
2022  eol_mode = QsciScintilla::EolUnix;
2023  count_max = count_lf;
2024  }
2025  if (count_cr > count_max)
2026  {
2027  eol_mode = QsciScintilla::EolMac;
2028  }
2029 
2030  return eol_mode;
2031 }
2032 
2034 {
2035  switch (m_edit_area->eolMode ())
2036  {
2037  case QsciScintilla::EolWindows:
2038  m_eol_indicator->setText ("CRLF");
2039  break;
2040  case QsciScintilla::EolMac:
2041  m_eol_indicator->setText ("CR");
2042  break;
2043  case QsciScintilla::EolUnix:
2044  m_eol_indicator->setText ("LF");
2045  break;
2046  }
2047 }
2048 
2050 {
2051  if (m_file_name.isEmpty ())
2052  return;
2053 
2054  // Create and queue the command object.
2055 
2056  // The interpreter_event callback function below emits a signal.
2057  // Because we don't control when that happens, use a guarded pointer
2058  // so that the callback can abort if this object is no longer valid.
2059 
2060  QPointer<file_editor_tab> this_fetab (this);
2061 
2062  emit interpreter_event
2063  ([=] (interpreter& interp)
2064  {
2065  // INTERPRETER THREAD
2066 
2067  // We can skip the entire callback function because it does not
2068  // make any changes to the interpreter state.
2069 
2070  if (this_fetab.isNull ())
2071  return;
2072 
2073  octave_value_list argout = Fdbstatus (interp, ovl (), 1);
2074 
2077  Qt::QueuedConnection);
2078 
2079  emit update_breakpoints_signal (argout);
2080  });
2081 }
2082 
2084 {
2085  octave_map dbg = argout(0).map_value ();
2086  octave_idx_type n_dbg = dbg.numel ();
2087 
2088  Cell file = dbg.contents ("file");
2089  Cell line = dbg.contents ("line");
2090  Cell cond = dbg.contents ("cond");
2091 
2092  for (octave_idx_type i = 0; i < n_dbg; i++)
2093  {
2094  if (file (i).string_value () == m_file_name.toStdString ())
2095  do_breakpoint_marker (true, this, line (i).int_value (),
2096  QString::fromStdString (cond (i).string_value ()));
2097  }
2098 }
2099 
2100 void file_editor_tab::new_file (const QString& commands)
2101 {
2102  update_window_title (false); // window title (no modification)
2103 
2105  gui_settings *settings = rmgr.get_settings ();
2106 
2107  // set the eol mode from the settings or depending on the OS if the entry is
2108  // missing in the settings
2109  m_edit_area->setEolMode (static_cast<QsciScintilla::EolMode> (settings->value (ed_default_eol_mode).toInt ()));
2110 
2112 
2113  update_lexer ();
2114 
2115  m_edit_area->setText (commands);
2116  m_edit_area->setModified (!commands.isEmpty ());
2117 }
2118 
2119 void file_editor_tab::confirm_dbquit_and_save (const QString& file_to_save,
2120  const QString& base_name,
2121  bool remove_on_success,
2122  bool restore_breakpoints)
2123 {
2124  int ans = QMessageBox::question (nullptr, tr ("Debug or Save"),
2125  tr ("This file is currently being executed.\n"
2126  "Quit debugging and save?"),
2127  QMessageBox::Save | QMessageBox::Cancel);
2128 
2129  if (ans == QMessageBox::Save)
2130  {
2131  // The interpreter_event callback function below emits a signal.
2132  // Because we don't control when that happens, use a guarded
2133  // pointer so that the callback can abort if this object is no
2134  // longer valid.
2135 
2136  QPointer<file_editor_tab> this_fetab (this);
2137 
2138  emit interpreter_event
2139  ([=] (interpreter& interp)
2140  {
2141  // INTERPRETER THREAD
2142 
2143  // If THIS_FETAB is no longer valid, we still want to perform
2144  // the actions in the interpreter but we can't emit the signal
2145  // associated with THIS_FETAB.
2146 
2147  tree_evaluator& tw = interp.get_evaluator ();
2148 
2149  tw.dbquit (true);
2150 
2152 
2153  std::string std_base_name = base_name.toStdString ();
2154 
2155  symbol_table& symtab = interp.get_symbol_table ();
2156 
2157  symtab.clear_user_function (std_base_name);
2158 
2159  if (this_fetab.isNull ())
2160  return;
2161 
2162  emit do_save_file_signal (file_to_save, remove_on_success,
2163  restore_breakpoints);
2164  });
2165  }
2166 }
2167 
2168 void file_editor_tab::save_file (const QString& saveFileName,
2169  bool remove_on_success,
2170  bool restore_breakpoints)
2171 {
2172  // If it is a new file with no name, signal that saveFileAs
2173  // should be performed.
2174  if (! valid_file_name (saveFileName))
2175  {
2176  save_file_as (remove_on_success);
2177  return;
2178  }
2179 
2180  m_encoding = m_new_encoding; // consider a possible new encoding
2181 
2182  // set the desired codec (if suitable for contents)
2183  QTextCodec *codec = check_valid_codec ();
2184  if (! codec)
2185  return; // No valid codec
2186 
2187  // Get a list of breakpoint line numbers, before exiting debug mode
2188  // and clearing function in interpreter_event action below.
2189 
2191 
2192  // get the absolute path (if existing)
2193  QFileInfo file_info = QFileInfo (saveFileName);
2194  QString file_to_save;
2195  if (file_info.exists ())
2196  {
2197  file_to_save = file_info.canonicalFilePath ();
2198  QString base_name = file_info.baseName ();
2199 
2200  // The interpreter_event callback function below emits a signal.
2201  // Because we don't control when that happens, use a guarded
2202  // pointer so that the callback can abort if this object is no
2203  // longer valid.
2204 
2205  QPointer<file_editor_tab> this_fetab (this);
2206 
2207  emit interpreter_event
2208  ([=] (interpreter& interp)
2209  {
2210  // INTERPRETER THREAD
2211 
2212  // We are intentionally skipping any side effects that may
2213  // occur in the callback function if THIS_FETAB is no longer
2214  // valid. If the editor tab has disappeared, there is not
2215  // much point in reloading the function to restore breakpoint
2216  // info in the GUI.
2217 
2218  if (this_fetab.isNull ())
2219  return;
2220 
2221  // Force reloading of a file after it is saved.
2222  // This is needed to get the right line numbers for
2223  // breakpoints (bug #46632).
2224 
2225  tree_evaluator& tw = interp.get_evaluator ();
2226 
2227  symbol_table& symtab = interp.get_symbol_table ();
2228 
2229  std::string std_base_name = base_name.toStdString ();
2230 
2231  if (tw.in_debug_repl ())
2232  {
2233  octave_value sym;
2234  try
2235  {
2236  sym = symtab.find_user_function (std_base_name);
2237  }
2238  catch (const execution_exception&)
2239  {
2240  interp.recover_from_exception ();
2241 
2242  // Ignore syntax error. It was in the old file on disk;
2243  // the user may have fixed it already.
2244  }
2245 
2246  // Return early if this file is not loaded in the symbol table
2247  if (! sym.is_defined () || ! sym.is_user_code ())
2248  {
2249  emit do_save_file_signal (file_to_save, remove_on_success,
2250  restore_breakpoints);
2251  return;
2252  }
2253 
2254  octave_user_code *fcn = sym.user_code_value ();
2255 
2256  std::string full_name = file_to_save.toStdString ();
2257 
2258  if (sys::canonicalize_file_name (full_name)
2259  != sys::canonicalize_file_name (fcn->fcn_file_name ()))
2260  {
2261  emit do_save_file_signal (file_to_save, remove_on_success,
2262  restore_breakpoints);
2263  return;
2264  }
2265 
2266  // If this file is loaded, check that we aren't currently
2267  // running it.
2268  // FIXME: is there a better way to get this info?
2269 
2270  octave_idx_type curr_frame = -1;
2271 
2272  octave_map stk = tw.backtrace (curr_frame, false);
2273 
2274  Cell names = stk.contents ("name");
2275 
2276  for (octave_idx_type i = names.numel () - 1; i >= 0; i--)
2277  {
2278  if (names(i).string_value () == std_base_name)
2279  {
2280  emit confirm_dbquit_and_save_signal
2281  (file_to_save, base_name, remove_on_success,
2282  restore_breakpoints);
2283  return;
2284  }
2285  }
2286  }
2287 
2288  symtab.clear_user_function (std_base_name);
2289 
2290  emit do_save_file_signal (file_to_save, remove_on_success,
2291  restore_breakpoints);
2292  });
2293  }
2294  else
2295  emit do_save_file_signal (saveFileName, remove_on_success,
2296  restore_breakpoints);
2297 }
2298 
2299 void file_editor_tab::do_save_file (const QString& file_to_save,
2300  bool remove_on_success,
2301  bool restore_breakpoints)
2302 {
2303  QSaveFile file (file_to_save);
2304 
2305  // stop watching file
2306  QStringList trackedFiles = m_file_system_watcher.files ();
2307  if (trackedFiles.contains (file_to_save))
2308  m_file_system_watcher.removePath (file_to_save);
2309 
2310  // Remove trailing white spaces if desired
2312  gui_settings *settings = rmgr.get_settings ();
2313 
2314  if (settings->value (ed_rm_trailing_spaces).toBool ())
2315  {
2316  // Replace trailing spaces, make sure edit area is writable,
2317  // which is not the case when saving at exit or when closing
2318  // the modified file.
2319  bool ro = m_edit_area->isReadOnly ();
2320  m_edit_area->setReadOnly (false); // allow writing for replace_all
2321  m_edit_area->replace_all ("[ \\t]+$", "", true, false, false);
2322  m_edit_area->setReadOnly (ro); // recover read only state
2323  }
2324 
2325  // open the file for writing (use QIODevice::ReadWrite for avoiding
2326  // truncating the previous file contents)
2327  if (! file.open (QIODevice::WriteOnly))
2328  {
2329  // Unsuccessful, begin watching file again if it was being
2330  // watched previously.
2331  if (trackedFiles.contains (file_to_save))
2332  m_file_system_watcher.addPath (file_to_save);
2333 
2334  // Create a NonModal message about error.
2335  QMessageBox *msgBox
2336  = new QMessageBox (QMessageBox::Critical,
2337  tr ("Octave Editor"),
2338  tr ("Could not open file %1 for write:\n%2.").
2339  arg (file_to_save).arg (file.errorString ()),
2340  QMessageBox::Ok, nullptr);
2341  show_dialog (msgBox, false);
2342 
2343  return;
2344  }
2345 
2346  // save the contents into the file
2347 
2348  // write the file
2349  QTextStream out (&file);
2350 
2351  // set the desired codec (if suitable for contents)
2352  QTextCodec *codec = check_valid_codec ();
2353  if (! codec)
2354  return; // No valid codec
2355 
2356  // Save the file
2357  out.setCodec (codec);
2358 
2359  QApplication::setOverrideCursor (Qt::WaitCursor);
2360 
2361  out << m_edit_area->text ();
2362  if (settings->value (ed_force_newline).toBool ()
2363  && m_edit_area->text ().length ())
2364  out << m_edit_area->eol_string (); // Add newline if desired
2365 
2366  out.flush ();
2367  QApplication::restoreOverrideCursor ();
2368 
2369  // Finish writing by committing the changes to disk,
2370  // where nothing is done when an error occurred while writing above
2371  bool writing_ok = file.commit ();
2372 
2373  if (writing_ok)
2374  {
2375  // Writing was successful: file exists now
2376  QFileInfo file_info = QFileInfo (file.fileName ());
2377  QString full_file_to_save = file_info.canonicalFilePath ();
2378 
2379  // file is save -> not modified, update encoding in statusbar
2380  m_edit_area->setModified (false);
2381  m_enc_indicator->setText (m_encoding);
2382 
2383  // save filename after closing file as set_file_name starts watching again
2384  set_file_name (full_file_to_save); // make absolute
2385 
2386  emit tab_ready_to_close ();
2387 
2388  if (remove_on_success)
2389  {
2390  emit tab_remove_request ();
2391  return; // Don't touch member variables after removal
2392  }
2393 
2394  // Attempt to restore the breakpoints if that is desired.
2395  // This is only allowed if the tab is not closing since changing
2396  // breakpoints would reopen the tab in this case.
2397  if (restore_breakpoints)
2399  }
2400  else
2401  {
2402  QMessageBox::critical (nullptr,
2403  tr ("Octave Editor"),
2404  tr ("The changes could not be saved to the file\n"
2405  "%1")
2406  .arg (file.fileName ())
2407  );
2408  }
2409 }
2410 
2411 void file_editor_tab::save_file_as (bool remove_on_success)
2412 {
2413  // Simply put up the file chooser dialog box with a slot connection
2414  // then return control to the system waiting for a file selection.
2415 
2416  // reset m_new_encoding
2418 
2419  // If the tab is removed in response to a QFileDialog signal, the tab
2420  // can't be a parent.
2421  QFileDialog *fileDialog;
2422  if (remove_on_success)
2423  {
2424  // If tab is closed, "this" cannot be parent in which case modality
2425  // has no effect. Disable editing instead.
2426  m_edit_area->setReadOnly (true);
2427  fileDialog = new QFileDialog ();
2428  }
2429  else
2430  fileDialog = new QFileDialog (this);
2431 
2432  // add the possible filters and the default suffix
2433  QStringList filters;
2434  filters << tr ("Octave Files (*.m)")
2435  << tr ("All Files (*)");
2436  fileDialog->setNameFilters (filters);
2437  fileDialog->setDefaultSuffix ("m");
2438 
2439  if (valid_file_name ())
2440  {
2441  fileDialog->selectFile (m_file_name);
2442  QFileInfo file_info (m_file_name);
2443  if (file_info.suffix () != "m")
2444  {
2445  // it is not an octave file
2446  fileDialog->selectNameFilter (filters.at (1)); // "All Files"
2447  fileDialog->setDefaultSuffix (""); // no default suffix
2448  }
2449  }
2450  else
2451  {
2452  fileDialog->selectFile ("");
2453  fileDialog->setDirectory (m_ced);
2454 
2455  // propose a name corresponding to the function name
2456  // if the new file contains a function
2457  QString fname = get_function_name ();
2458  if (! fname.isEmpty ())
2459  fileDialog->selectFile (fname + ".m");
2460  }
2461 
2462  fileDialog->setAcceptMode (QFileDialog::AcceptSave);
2463  fileDialog->setViewMode (QFileDialog::Detail);
2464  fileDialog->setOption (QFileDialog::HideNameFilterDetails, false);
2465 
2466  // FIXME: Remove, if for all common KDE versions (bug #54607) is resolved.
2468  gui_settings *settings = rmgr.get_settings ();
2469  if (! settings->value (global_use_native_dialogs).toBool ())
2470  {
2471  // Qt file dialogs
2472  fileDialog->setOption(QFileDialog::DontUseNativeDialog);
2473  }
2474  else
2475  {
2476  // Native file dialogs: Test for already existing files is done manually
2477  // since native file dialogs might not consider the automatically
2478  // appended default extension when checking if the file already exists
2479  fileDialog->setOption(QFileDialog::DontConfirmOverwrite);
2480  }
2481 
2482  connect (fileDialog, &QFileDialog::filterSelected,
2484 
2485  if (remove_on_success)
2486  {
2487  connect (fileDialog, &QFileDialog::fileSelected,
2489 
2490  connect (fileDialog, &QFileDialog::rejected,
2492  }
2493  else
2494  {
2495  connect (fileDialog, &QFileDialog::fileSelected,
2497  }
2498 
2499  show_dialog (fileDialog, ! valid_file_name ());
2500 }
2501 
2503 {
2504  // On some systems, the filterSelected signal is emitted without user
2505  // action and with an empty filter string when the file dialog is shown.
2506  // Just return in this case and do not remove the current default suffix.
2507  if (filter.isEmpty ())
2508  return;
2509 
2510  QFileDialog *file_dialog = qobject_cast<QFileDialog *> (sender ());
2511 
2512  QRegExp rx ("\\*\\.([^ ^\\)]*)[ \\)]"); // regexp for suffix in filter
2513  int index = rx.indexIn (filter, 0); // get first suffix in filter
2514 
2515  if (index > -1)
2516  file_dialog->setDefaultSuffix (rx.cap (1)); // found a suffix, set default
2517  else
2518  file_dialog->setDefaultSuffix (""); // not found, clear default
2519 }
2520 
2522 {
2523  QFileInfo file = QFileInfo (file_name);
2524  QString base_name = file.baseName ();
2525 
2526  if ((file.suffix () == "m")
2527  && (! valid_identifier (base_name.toStdString ())))
2528  {
2529  int ans = QMessageBox::question (nullptr, tr ("Octave Editor"),
2530  tr ("\"%1\"\n"
2531  "is not a valid identifier.\n\n"
2532  "If you keep this filename, you will not be able to\n"
2533  "call your script using its name as an Octave command.\n\n"
2534  "Do you want to choose another name?").arg (base_name),
2535  QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
2536 
2537  if (ans == QMessageBox::Yes)
2538  return true;
2539  }
2540 
2541  return false;
2542 }
2543 
2545 {
2546  QTextCodec *codec = QTextCodec::codecForName (m_encoding.toLatin1 ());
2547 
2548  // "SYSTEM" is used as alias for the locale encoding.
2549  if ((! codec) && m_encoding.startsWith("SYSTEM"))
2550  codec = QTextCodec::codecForLocale ();
2551 
2552  if (! codec)
2553  {
2554  QMessageBox::critical (nullptr,
2555  tr ("Octave Editor"),
2556  tr ("The current encoding %1\n"
2557  "can not be applied.\n\n"
2558  "Please select another one!").arg (m_encoding));
2559 
2560  return nullptr;
2561  }
2562 
2563  QString editor_text = m_edit_area->text ();
2564  bool can_encode = codec->canEncode (editor_text);
2565 
2566  // We cannot rely on QTextCodec::canEncode because it uses the
2567  // ConverterState of convertFromUnicode which isn't updated by some
2568  // implementations.
2569  if (can_encode)
2570  {
2571  QVector<uint> u32_str = editor_text.toUcs4 ();
2572  const uint32_t *src = reinterpret_cast<const uint32_t *>
2573  (u32_str.data ());
2574 
2575  std::size_t length;
2576  const std::string encoding = m_encoding.toStdString ();
2577  char *res_str =
2579  u32_str.size (), &length);
2580  if (! res_str)
2581  {
2582  if (errno == EILSEQ)
2583  can_encode = false;
2584  }
2585  else
2586  ::free (static_cast<void *> (res_str));
2587  }
2588 
2589  if (! can_encode)
2590  {
2591  QMessageBox::StandardButton pressed_button
2592  = QMessageBox::critical (nullptr,
2593  tr ("Octave Editor"),
2594  tr ("The current editor contents can not be encoded\n"
2595  "with the selected encoding %1.\n"
2596  "Using it would result in data loss!\n\n"
2597  "Please select another one!").arg (m_encoding),
2598  QMessageBox::Cancel | QMessageBox::Ignore,
2599  QMessageBox::Cancel);
2600 
2601  if (pressed_button == QMessageBox::Ignore)
2602  return codec;
2603  else
2604  return nullptr;
2605  }
2606 
2607  return codec;
2608 }
2609 
2610 void file_editor_tab::handle_save_file_as_answer (const QString& save_file_name)
2611 {
2612  QString saveFileName = save_file_name;
2613  QFileInfo file (saveFileName);
2614  QFileDialog *file_dialog = qobject_cast<QFileDialog *> (sender ());
2615 
2616  // Test if the file dialog should have added a default file
2617  // suffix, but the selected file still has no suffix (see Qt bug
2618  // https://bugreports.qt.io/browse/QTBUG-59401)
2619  if ((! file_dialog->defaultSuffix ().isEmpty ()) && file.suffix ().isEmpty ())
2620  {
2621  saveFileName = saveFileName + "." + file_dialog->defaultSuffix ();
2622  }
2623 
2624  file.setFile (saveFileName);
2625 
2626  // If overwrite confirmation was not done by the file dialog (in case
2627  // of native file dialogs, see above), do it here
2628  if (file_dialog->testOption (QFileDialog::DontConfirmOverwrite) && file.exists ())
2629  {
2630  int ans = QMessageBox::question (file_dialog,
2631  tr ("Octave Editor"),
2632  tr ("%1\n already exists\n"
2633  "Do you want to overwrite it?").arg (saveFileName),
2634  QMessageBox::Yes | QMessageBox::No);
2635  if (ans != QMessageBox::Yes)
2636  {
2637  // Try again, if edit area is read only, remove on success
2638  save_file_as (m_edit_area->isReadOnly ());
2639  return;
2640  }
2641  }
2642 
2643  if (saveFileName == m_file_name)
2644  {
2645  save_file (saveFileName);
2646  }
2647  else
2648  {
2649  // Have editor check for conflict, do not delete tab after save.
2650  if (check_valid_identifier (saveFileName))
2651  save_file_as (false);
2652  else
2653  emit editor_check_conflict_save (saveFileName, false);
2654  }
2655 }
2656 
2657 void file_editor_tab::handle_save_file_as_answer_close (const QString& saveFileName)
2658 {
2659  // saveFileName == m_file_name can not happen, because we only can get here
2660  // when we close a tab and m_file_name is not a valid filename yet
2661 
2662  // Have editor check for conflict, delete tab after save.
2663  if (check_valid_identifier (saveFileName))
2664  save_file_as (true);
2665  else
2666  emit editor_check_conflict_save (saveFileName, true);
2667 }
2668 
2670 {
2671  // User canceled, allow editing again.
2672  m_edit_area->setReadOnly (false);
2673 }
2674 
2675 void file_editor_tab::file_has_changed (const QString&, bool do_close)
2676 {
2677  bool file_exists = QFile::exists (m_file_name);
2678 
2679  if (file_exists && ! do_close)
2680  {
2681  // Test if file is really modified or if just the timezone has
2682  // changed. In the latter, just return without doing anything.
2683  QDateTime modified = QFileInfo (m_file_name).lastModified ().toUTC ();
2684 
2685  if (modified <= m_last_modified)
2686  return;
2687 
2688  m_last_modified = modified;
2689  }
2690 
2691  // Prevent popping up multiple message boxes when the file has
2692  // been changed multiple times by temporarily removing from the
2693  // file watcher.
2694  QStringList trackedFiles = m_file_system_watcher.files ();
2695  if (! trackedFiles.isEmpty ())
2696  m_file_system_watcher.removePath (m_file_name);
2697 
2698  if (file_exists && ! do_close)
2699  {
2700 
2701  // The file is modified
2704 
2705  else
2706  {
2707  // give editor and this tab the focus,
2708  // possibly making the editor visible if it is hidden
2709  emit set_focus_editor_signal (this);
2710  m_edit_area->setFocus ();
2711 
2712  // Create a WindowModal message that blocks the edit area
2713  // by making m_edit_area parent.
2714  QMessageBox *msgBox
2715  = new QMessageBox (QMessageBox::Warning,
2716  tr ("Octave Editor"),
2717  tr ("It seems that \'%1\' has been modified by another application. Do you want to reload it?").
2718  arg (m_file_name),
2719  QMessageBox::Yes | QMessageBox::No, this);
2720 
2721  connect (msgBox, &QMessageBox::finished,
2723 
2724  msgBox->setWindowModality (Qt::WindowModal);
2725  msgBox->setAttribute (Qt::WA_DeleteOnClose);
2726  msgBox->show ();
2727  }
2728  }
2729  else
2730  {
2731  // If desired and if file is not modified,
2732  // close the file without any user interaction
2733  if (do_close && ! m_edit_area->isModified ())
2734  {
2735  handle_file_resave_answer (QMessageBox::Cancel);
2736  return;
2737  }
2738 
2739  // give editor and this tab the focus,
2740  // possibly making the editor visible if it is hidden
2741  emit set_focus_editor_signal (this);
2742  m_edit_area->setFocus ();
2743 
2744  QString modified = "";
2745  if (m_edit_area->isModified ())
2746  modified = tr ("\n\nWarning: The contents in the editor is modified!");
2747 
2748  // Create a WindowModal message. The file editor tab can't be made
2749  // parent because it may be deleted depending upon the response.
2750  // Instead, change the m_edit_area to read only.
2751  QMessageBox *msgBox
2752  = new QMessageBox (QMessageBox::Warning, tr ("Octave Editor"),
2753  tr ("It seems that the file\n"
2754  "%1\n"
2755  "has been deleted or renamed. Do you want to save it now?%2").
2756  arg (m_file_name).arg (modified),
2757  QMessageBox::Save | QMessageBox::Close, nullptr);
2758 
2759  m_edit_area->setReadOnly (true);
2760 
2761  connect (msgBox, &QMessageBox::finished,
2763 
2764  msgBox->setWindowModality (Qt::WindowModal);
2765  msgBox->setAttribute (Qt::WA_DeleteOnClose);
2766  msgBox->show ();
2767  }
2768 }
2769 
2771 {
2772  if (! settings)
2773  return;
2774 
2775  if (! init)
2777 
2778  // code folding
2779  if (settings->value (ed_code_folding).toBool ())
2780  {
2781  m_edit_area->setMarginType (3, QsciScintilla::SymbolMargin);
2782  m_edit_area->setFolding (QsciScintilla::BoxedTreeFoldStyle, 3);
2783  }
2784  else
2785  {
2786  m_edit_area->setFolding (QsciScintilla::NoFoldStyle, 3);
2787  }
2788 
2789  // status bar
2790  if (settings->value (ed_show_edit_status_bar).toBool ())
2791  m_status_bar->show ();
2792  else
2793  m_status_bar->hide ();
2794 
2795  //highlight current line color
2796  m_edit_area->setCaretLineVisible
2797  (settings->value (ed_highlight_current_line).toBool ());
2798 
2799  // auto completion
2800  bool match_keywords = settings->value
2801  (ed_code_completion_keywords).toBool ();
2802  bool match_document = settings->value
2803  (ed_code_completion_document).toBool ();
2804 
2805  QsciScintilla::AutoCompletionSource source = QsciScintilla::AcsNone;
2806  if (match_keywords)
2807  if (match_document)
2808  source = QsciScintilla::AcsAll;
2809  else
2810  source = QsciScintilla::AcsAPIs;
2811  else if (match_document)
2812  source = QsciScintilla::AcsDocument;
2813  m_edit_area->setAutoCompletionSource (source);
2814 
2815  m_edit_area->setAutoCompletionReplaceWord
2816  (settings->value (ed_code_completion_replace).toBool ());
2817  m_edit_area->setAutoCompletionCaseSensitivity
2818  (settings->value (ed_code_completion_case).toBool ());
2819 
2820  if (settings->value (ed_code_completion).toBool ())
2821  m_edit_area->setAutoCompletionThreshold
2822  (settings->value (ed_code_completion_threshold).toInt ());
2823  else
2824  m_edit_area->setAutoCompletionThreshold (-1);
2825 
2826  if (settings->value (ed_show_white_space).toBool ())
2827  if (settings->value (ed_show_white_space_indent).toBool ())
2828  m_edit_area->setWhitespaceVisibility (QsciScintilla::WsVisibleAfterIndent);
2829  else
2830  m_edit_area->setWhitespaceVisibility (QsciScintilla::WsVisible);
2831  else
2832  m_edit_area->setWhitespaceVisibility (QsciScintilla::WsInvisible);
2833 
2834  m_edit_area->setEolVisibility (settings->value (ed_show_eol_chars).toBool ());
2835 
2836  m_save_as_desired_eol = static_cast<QsciScintilla::EolMode>
2837  (settings->value (ed_default_eol_mode).toInt ());
2838 
2839  if (settings->value (ed_show_line_numbers).toBool ())
2840  {
2841  m_edit_area->setMarginLineNumbers (2, true);
2842  auto_margin_width ();
2843  connect (m_edit_area, SIGNAL (linesChanged ()),
2844  this, SLOT (auto_margin_width ()));
2845  }
2846  else
2847  {
2848  m_edit_area->setMarginLineNumbers (2, false);
2849  disconnect (m_edit_area, SIGNAL (linesChanged ()), nullptr, nullptr);
2850  }
2851 
2852  m_smart_indent = settings->value (ed_auto_indent).toBool ();
2853  m_edit_area->setAutoIndent (m_smart_indent);
2854  m_edit_area->setTabIndents
2855  (settings->value (ed_tab_indents_line).toBool ());
2856  m_edit_area->setBackspaceUnindents
2857  (settings->value (ed_backspace_unindents_line).toBool ());
2858  m_edit_area->setIndentationGuides
2859  (settings->value (ed_show_indent_guides).toBool ());
2860  m_edit_area->setIndentationsUseTabs
2861  (settings->value (ed_indent_uses_tabs).toBool ());
2862  m_edit_area->setIndentationWidth
2863  (settings->value (ed_indent_width).toInt ());
2864 
2865  m_edit_area->setTabWidth
2866  (settings->value (ed_tab_width).toInt ());
2867 
2868  m_ind_char_width = 1;
2869  if (m_edit_area->indentationsUseTabs ())
2870  m_ind_char_width = m_edit_area->tabWidth ();
2871 
2872  m_edit_area->SendScintilla (QsciScintillaBase::SCI_SETHSCROLLBAR,
2873  settings->value (ed_show_hscroll_bar).toBool ());
2874  m_edit_area->SendScintilla (QsciScintillaBase::SCI_SETSCROLLWIDTH,-1);
2875  m_edit_area->SendScintilla (QsciScintillaBase::SCI_SETSCROLLWIDTHTRACKING,true);
2876 
2877  update_window_title (m_edit_area->isModified ());
2878 
2879  m_auto_endif = settings->value (ed_auto_endif).toInt ();
2880 
2881  // long line marker
2882  int line_length = settings->value (ed_long_line_column).toInt ();
2883  m_edit_area->setEdgeColumn (line_length);
2884 
2885  if (settings->value (ed_long_line_marker).toBool ())
2886  {
2887  if (settings->value (ed_long_line_marker_line).toBool ())
2888  m_edit_area->setEdgeMode (QsciScintilla::EdgeLine);
2889  else
2890  {
2892  .toBool ())
2893  m_edit_area->setEdgeMode (QsciScintilla::EdgeBackground);
2894  else
2895  m_edit_area->setEdgeMode (QsciScintilla::EdgeLine);
2896  }
2897  }
2898  else
2899  m_edit_area->setEdgeMode (QsciScintilla::EdgeNone);
2900 
2901  // line wrapping and breaking
2902  m_edit_area->setWrapVisualFlags (QsciScintilla::WrapFlagByBorder);
2903  m_edit_area->setWrapIndentMode (QsciScintilla::WrapIndentSame);
2904 
2905  if (settings->value (ed_wrap_lines).toBool ())
2906  m_edit_area->setWrapMode (QsciScintilla::WrapWord);
2907  else
2908  m_edit_area->setWrapMode (QsciScintilla::WrapNone);
2909 
2910  if (settings->value (ed_break_lines).toBool ())
2911  m_line_break = line_length;
2912  else
2913  m_line_break = 0;
2914 
2916  settings->value (ed_break_lines_comments).toBool ();
2917 
2918  // highlight all occurrences of a word selected by a double click
2920  settings->value (ed_highlight_all_occurrences).toBool ();
2921 
2922  // reload changed files
2924  settings->value (ed_always_reload_changed_files).toBool ();
2925 
2926  // Set cursor blinking depending on the settings.
2927  // QScintilla ignores the application global settings, so some special
2928  // handling is required
2929  bool cursor_blinking;
2930 
2931  if (settings->contains (global_cursor_blinking.key))
2932  cursor_blinking = settings->value (global_cursor_blinking).toBool ();
2933  else
2934  cursor_blinking = settings->value (cs_cursor_blinking).toBool ();
2935 
2936  if (cursor_blinking)
2937  m_edit_area->SendScintilla (QsciScintillaBase::SCI_SETCARETPERIOD, 500);
2938  else
2939  m_edit_area->SendScintilla (QsciScintillaBase::SCI_SETCARETPERIOD, 0);
2940 
2941 }
2942 
2944 {
2945  m_edit_area->setMarginWidth (2, "1" + QString::number (m_edit_area->lines ()));
2946 }
2947 
2948 // the following close request was changed from a signal slot into a
2949 // normal function because we need the return value from close whether
2950 // the tab really was closed (for canceling exiting octave).
2951 // When emitting a signal, only the return value from the last slot
2952 // goes back to the sender
2954 {
2955  return close ();
2956 }
2957 
2959 {
2960  if (ID != this)
2961  return;
2962 
2964  m_edit_area->isModified ());
2965 }
2966 
2968 {
2969  if (decision == QMessageBox::Yes)
2970  {
2971  // reload: file is readded to the file watcher in set_file_name ()
2973  }
2974  else
2975  {
2976  // do not reload: readd to the file watcher
2978  }
2979 }
2980 
2982 {
2983  // check decision of user in dialog
2984  if (decision == QMessageBox::Save)
2985  {
2986  save_file (m_file_name); // readds file to watcher in set_file_name ()
2987  m_edit_area->setReadOnly (false); // delete read only flag
2988  }
2989  else
2990  {
2991  // Definitely close the file.
2992  // Set modified to false to prevent the dialog box when the close event
2993  // is posted. If the user cancels the close in this dialog the tab is
2994  // left open with a non-existing file.
2995  m_edit_area->setModified (false);
2996  close ();
2997  }
2998 }
2999 
3001 {
3002  if (ID != this || ID == nullptr)
3003  return;
3004 
3005  emit remove_all_positions (); // debugger_position, unsure_debugger_position
3006 
3007  if (line > 0)
3008  {
3009  marker *dp;
3010 
3011  if (m_edit_area->isModified ())
3012  {
3013  // The best that can be done if the editor contents have been
3014  // modified is to see if there is a match with the original
3015  // line number of any existing breakpoints. We can put a normal
3016  // debugger pointer at that breakpoint position. Otherwise, it
3017  // isn't certain whether the original line number and current line
3018  // number match.
3019  int editor_linenr = -1;
3020  marker *dummy;
3021  emit find_translated_line_number (line, editor_linenr, dummy);
3022  if (editor_linenr != -1)
3023  {
3024  // Match with an existing breakpoint.
3025  dp = new marker (m_edit_area, line,
3026  marker::debugger_position, editor_linenr);
3027  }
3028  else
3029  {
3030  int original_linenr = -1;
3031  editor_linenr = -1;
3032  emit find_linenr_just_before (line, original_linenr, editor_linenr);
3033  if (original_linenr >= 0)
3034  {
3035  // Make a guess by using an offset from the breakpoint.
3036  int linenr_guess = editor_linenr + line - original_linenr;
3037  dp = new marker (m_edit_area, line,
3039  linenr_guess);
3040  }
3041  else
3042  {
3043  // Can't make a very good guess, so just use the debugger
3044  // line number.
3045  dp = new marker (m_edit_area, line,
3047  }
3048  }
3049  }
3050  else
3051  {
3052  dp = new marker (m_edit_area, line, marker::debugger_position);
3053 
3054  // In case of a not modified file we might have to remove
3055  // a breakpoint here if we have stepped into the file
3056  if (line == m_breakpoint_info.remove_line)
3057  {
3061  }
3062  }
3063 
3066 
3067  connect (this, &file_editor_tab::remove_all_positions,
3068  dp, &marker::handle_remove);
3069 
3070  center_current_line (false);
3071  }
3072 }
3073 
3075 {
3076  if (ID != this || ID == nullptr)
3077  return;
3078 
3079  if (line > 0)
3081 }
3082 
3084  const QWidget *ID, int line,
3085  const QString& cond)
3086 {
3087  if (ID != this || ID == nullptr)
3088  return;
3089 
3090  if (line > 0)
3091  {
3092  if (insert)
3093  {
3094  int editor_linenr = -1;
3095  marker *bp = nullptr;
3096 
3097  // If comes back indicating a non-zero breakpoint marker,
3098  // reuse it if possible
3099  emit find_translated_line_number (line, editor_linenr, bp);
3100  if (bp != nullptr)
3101  {
3102  if ((cond == "") != (bp->get_cond () == ""))
3103  {
3104  // can only reuse conditional bp as conditional
3106  bp = nullptr;
3107  }
3108  else
3109  bp->set_cond (cond);
3110  }
3111 
3112  if (bp == nullptr)
3113  {
3114  bp = new marker (m_edit_area, line,
3115  cond == "" ? marker::breakpoint
3116  : marker::cond_break, cond);
3117 
3123  bp, &marker::handle_remove);
3128  connect (this, &file_editor_tab::report_marker_linenr,
3130  connect (bp, &marker::request_remove,
3132  }
3133  }
3134  else
3136  }
3137 }
3138 
3140 {
3141  long int visible_lines
3142  = m_edit_area->SendScintilla (QsciScintillaBase::SCI_LINESONSCREEN);
3143 
3144  if (visible_lines > 2)
3145  {
3146  int line, index;
3147  m_edit_area->getCursorPosition (&line, &index);
3148  // compensate for "folding":
3149  // step 1: expand the current line, if it was folded
3150  m_edit_area->SendScintilla (2232, line); // SCI_ENSUREVISIBLE
3151 
3152  // step 2: map file line num to "visible" one // SCI_VISIBLEFROMDOCLINE
3153  int vis_line = m_edit_area->SendScintilla (2220, line);
3154 
3155  int first_line = m_edit_area->firstVisibleLine ();
3156 
3157  if (always || vis_line == first_line
3158  || vis_line > first_line + visible_lines - 2)
3159  {
3160  first_line += (vis_line - first_line - (visible_lines - 1) / 2);
3161  m_edit_area->SendScintilla (2613, first_line); // SCI_SETFIRSTVISIBLELINE
3162  }
3163  }
3164 }
3165 
3167 {
3168  // the related signal is emitted before cursor-move-signal!
3169  m_lines_changed = true;
3170 }
3171 
3173 {
3174  // Cursor has moved, first check wether an autocompletion list
3175  // is active or if it was closed. Scintilla provides signals for
3176  // completed or cancelled lists, but not for list that where hidden
3177  // due to a new character not matching anymore with the list entries
3178  if (m_edit_area->SendScintilla (QsciScintillaBase::SCI_AUTOCACTIVE))
3179  m_autoc_active = true;
3180  else if (m_autoc_active)
3181  {
3182  m_autoc_active = false;
3183  emit autoc_closed (); // Tell editor about closed list
3184  }
3185 
3186  // Lines changed? Take care of indentation!
3187  bool do_smart_indent = m_lines_changed && m_is_octave_file
3188  && (line == m_line+1) && (col < m_col)
3189  && (m_smart_indent || m_auto_endif);
3190  m_lines_changed = false;
3191 
3192  // Update line and column indicator in the status bar
3193  int o_line = m_line;
3194  update_rowcol_indicator (line, col);
3195 
3196  // Do smart indent after update of line indicator for having
3197  // consistent indicator data
3198  if (do_smart_indent)
3200  o_line, m_ind_char_width);
3201 }
3202 
3204 {
3205  m_line = line;
3206  m_col = col;
3207  m_row_indicator->setNum (line+1);
3208  m_col_indicator->setNum (col+1);
3209 }
3210 
3211 // Slot that is entered each time a new character was typed.
3212 // It is used for handling line breaking if this is desired.
3213 // The related signal is emitted after the signal for a moved cursor
3214 // such that m_col and m_line can not be used for current position.
3216 {
3217  if (m_line_break)
3218  {
3219  // If line breaking is desired, get the current line and column.
3220  // For taking the tab width into consideration, use own function
3221  int line, col, pos;
3222  m_edit_area->get_current_position (&pos, &line, &col);
3223 
3224  // immediately return if line has not reached the max. line length
3225  if (col <= m_line_break)
3226  return;
3227 
3228  // If line breaking is only desired in comments,
3229  // return if not in a comment
3230  int style_comment = octave_qscintilla::ST_NONE;
3232  {
3233  // line breaking only in comments, check for comment style
3234  style_comment = m_edit_area->is_style_comment ();
3235  if (! style_comment)
3236  return; // no comment, return
3237  }
3238 
3239  // Here we go for breaking the current line by inserting a newline.
3240  // For determining the position of a specific column, we have to get
3241  // the column from the QScintilla function without taking tab lengths
3242  // into account, since the calculation from line/col to position
3243  // ignores this, too.
3244  m_edit_area->getCursorPosition (&line, &col);
3245  int c = 0;
3246  int col_space = col;
3247  int indentation = m_edit_area->indentation (line);
3248 
3249  // Search the first occurrence of space or tab backwards starting from
3250  // the current column (col_space).
3251  while (c != ' ' && c != '\t' && col_space > indentation)
3252  {
3253  pos = m_edit_area->positionFromLineIndex (line, col_space--);
3254  c = m_edit_area->SendScintilla (QsciScintillaBase::SCI_GETCHARAT, pos);
3255  }
3256 
3257  // If a space or tab was found, break at this char,
3258  // otherwise break at cursor position
3259  int col_newline = col - 1;
3260  if (c == ' ' || c == '\t')
3261  col_newline = col_space + 1;
3262 
3263  // Insert a newline char for breaking the line possibly followed
3264  // by a line comment string
3265  QString newline = QString ("\n");
3266  style_comment = m_edit_area->is_style_comment ();
3267  if (style_comment == octave_qscintilla::ST_LINE_COMMENT)
3268  newline = newline + m_edit_area->comment_string ().at (0);
3269  m_edit_area->insertAt (newline, line, col_newline);
3270 
3271  // Automatically indent the new line to the indentation of previous line
3272  // and set the cursor position to the end of the indentation.
3273  m_edit_area->setIndentation (line + 1, indentation);
3274  m_edit_area->SendScintilla (QsciScintillaBase::SCI_LINEEND);
3275  }
3276 }
3277 
3278 // Slot handling a double click into the text area
3279 void file_editor_tab::handle_double_click (int, int, int modifier)
3280 {
3281  if (! modifier)
3282  {
3283  // double clicks without modifier
3284  // clear any existing indicators of this type
3286 
3288  {
3289  // Clear any previous selection.
3291 
3292  // highlighting of all occurrences of the clicked word is enabled
3293 
3294  // get the resulting cursor position
3295  // (required if click was beyond a line ending)
3296  int line, col;
3297  m_edit_area->getCursorPosition (&line, &col);
3298 
3299  // get the word at the cursor (if any)
3300  QString word = m_edit_area->wordAtLineIndex (line, col);
3301  word = word.trimmed ();
3302 
3303  if (! word.isEmpty ())
3304  {
3305  // word is not empty, so find all occurrences of the word
3306 
3307  // remember first visible line and x-offset for restoring the view afterwards
3308  int first_line = m_edit_area->firstVisibleLine ();
3309  int x_offset = m_edit_area->SendScintilla (QsciScintillaBase::SCI_GETXOFFSET);
3310 
3311  // search for first occurrence of the detected word
3312  bool find_result_available
3313  = m_edit_area->findFirst (word,
3314  false, // no regexp
3315  true, // case sensitive
3316  true, // whole words only
3317  false, // do not wrap
3318  true, // forward
3319  0, 0, // from the beginning
3320  false
3321 #if defined (HAVE_QSCI_VERSION_2_6_0)
3322  , true
3323 #endif
3324  );
3325 
3326  // loop over all occurrences and set the related indicator
3327  int oline, ocol;
3328  int wlen = word.length ();
3329 
3330  while (find_result_available)
3331  {
3332  // get cursor position after having found an occurrence
3333  m_edit_area->getCursorPosition (&oline, &ocol);
3334  // mark the selection
3335  m_edit_area->show_selection_markers (oline, ocol-wlen, oline, ocol);
3336 
3337  // find next occurrence
3338  find_result_available = m_edit_area->findNext ();
3339  }
3340 
3341  // restore the visible area of the file, the cursor position,
3342  // and the selection
3343  m_edit_area->setFirstVisibleLine (first_line);
3344  m_edit_area->SendScintilla (QsciScintillaBase::SCI_SETXOFFSET, x_offset);
3345  m_edit_area->setCursorPosition (line, col);
3346  m_edit_area->setSelection (line, col - wlen, line, col);
3348  }
3349  }
3350  }
3351 }
3352 
3354 {
3355  QRegExp rxfun1 ("^[\t ]*function[^=]+=([^\\(]+)\\([^\\)]*\\)[\t ]*$");
3356  QRegExp rxfun2 ("^[\t ]*function[\t ]+([^\\(]+)\\([^\\)]*\\)[\t ]*$");
3357  QRegExp rxfun3 ("^[\t ]*function[^=]+=[\t ]*([^\\s]+)[\t ]*$");
3358  QRegExp rxfun4 ("^[\t ]*function[\t ]+([^\\s]+)[\t ]*$");
3359  QRegExp rxfun5 ("^[\t ]*classdef[\t ]+([^\\s]+)[\t ]*$");
3360 
3361  QStringList lines = m_edit_area->text ().split ("\n");
3362 
3363  for (int i = 0; i < lines.count (); i++)
3364  {
3365  if (rxfun1.indexIn (lines.at (i)) != -1)
3366  return rxfun1.cap (1).remove (QRegExp ("[ \t]*"));
3367  else if (rxfun2.indexIn (lines.at (i)) != -1)
3368  return rxfun2.cap (1).remove (QRegExp ("[ \t]*"));
3369  else if (rxfun3.indexIn (lines.at (i)) != -1)
3370  return rxfun3.cap (1).remove (QRegExp ("[ \t]*"));
3371  else if (rxfun4.indexIn (lines.at (i)) != -1)
3372  return rxfun4.cap (1).remove (QRegExp ("[ \t]*"));
3373  else if (rxfun5.indexIn (lines.at (i)) != -1)
3374  return rxfun5.cap (1).remove (QRegExp ("[ \t]*"));
3375  }
3376 
3377  return QString ();
3378 }
3379 
3381 
3382 #endif
OCTAVE_END_NAMESPACE(octave)
OCTARRAY_OVERRIDABLE_FUNC_API octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:414
Definition: Cell.h:43
Base class for Octave interfaces that use Qt.
resource_manager & get_resource_manager(void)
int remove_breakpoint_from_file(const std::string &file="", int line=1)
Definition: bp-table.cc:901
int add_breakpoint_in_file(const std::string &file="", int line=1, const std::string &condition="")
Definition: bp-table.cc:730
bp_lines remove_all_breakpoints_from_file(const std::string &file, bool silent=false)
Definition: bp-table.cc:930
static bool interrupt(bool=true)
Definition: cmd-edit.cc:1625
void notice_settings(const gui_settings *settings, bool init=false)
void next_breakpoint(const QWidget *ID)
void handle_decode_warning_answer(QAbstractButton *btn)
void handle_add_octave_apis(const QStringList &api_entries)
void smart_indent_line_or_selected_text(const QWidget *ID)
void remove_all_breakpoints_signal(void)
void handle_lines_changed(void)
void handle_dbstop_if(const QString &prompt, int line, const QString &cond)
void report_marker_linenr(QIntList &lines, QStringList &conditions)
bool check_valid_identifier(QString file_name)
void do_comment_selected_text(bool comment, bool input_str=false)
base_qobject & m_octave_qobj
void run_file_signal(const QFileInfo &info)
void move_match_brace(const QWidget *ID, bool select)
void previous_breakpoint(const QWidget *ID)
void save_file_as(const QWidget *ID)
void do_indent_selected_text(bool indent)
void set_modified(bool modified=true)
void set_focus(const QWidget *ID)
bool valid_file_name(const QString &file=QString())
void set_focus_editor_signal(QWidget *)
void run_file(const QWidget *ID, bool step_into=false)
void add_breakpoint_event(int line, const QString &cond)
void unindent_selected_text(const QWidget *ID)
void remove_position_via_debugger_linenr(int debugger_linenr)
void save_apis_info(void)
void comment_selected_text(const QWidget *ID, bool input_str)
void handle_save_file_as_answer(const QString &fileName)
void confirm_dbquit_and_save(const QString &file_to_save, const QString &base_name, bool remove_on_success, bool restore_breakpoints)
void do_breakpoint_marker(bool insert, const QWidget *ID, int line=-1, const QString &cond="")
int check_file_modified(bool remove=false)
void request_remove_breakpoint_via_editor_linenr(int editor_linenr)
QString load_file(const QString &fileName)
QStringList m_bp_conditions
void update_rowcol_indicator(int line, int col)
void handle_current_enc_changed(const QString &enc)
QFileSystemWatcher m_file_system_watcher
void handle_request_remove_breakpoint(int line)
void confirm_dbquit_and_save_signal(const QString &file_to_save, const QString &base_name, bool remove_on_success, bool restore_breakpoints)
void indent_selected_text(const QWidget *ID)
void handle_save_file_as_answer_close(const QString &fileName)
void context_run(const QWidget *ID)
void next_bookmark(const QWidget *ID)
void check_restore_breakpoints(void)
void remove_all_positions(void)
QString m_prep_apis_file
void convert_eol(const QWidget *ID, QsciScintilla::EolMode)
void center_current_line(bool always=true)
void set_file_name(const QString &fileName)
void interpreter_event(const fcn_callback &fcn)
void handle_remove_next(int remove_line)
QString get_all_bookmarks(void)
void recover_from_exit(void)
void new_file(const QString &commands=QString())
void tab_remove_request(void)
void file_has_changed(const QString &path, bool do_close=false)
void handle_file_reload_answer(int decision)
void previous_bookmark(const QWidget *ID)
void remove_bookmark(const QWidget *ID)
void enable_file_watcher(bool do_enable)
void editor_check_conflict_save(const QString &saveFileName, bool remove_on_success)
void update_eol_indicator(void)
void do_save_file(const QString &file_to_save, bool remove_on_success, bool restore_breakpoints)
void request_add_breakpoint(int line, const QString &cond)
bool conditional_close(void)
QLabel * m_eol_indicator
void handle_margin_clicked(int line, int margin, Qt::KeyboardModifiers state)
void mru_add_file(const QString &file_name, const QString &encoding)
void remove_all_breakpoints(const QWidget *ID)
void save_file(const QWidget *ID)
QString get_function_name(void)
void handle_char_added(int character)
void context_help(const QWidget *ID, bool)
void find_translated_line_number(int original_linenr, int &translated_linenr, marker *&)
void update_breakpoints_handler(const octave_value_list &argout)
void delete_debugger_pointer(const QWidget *ID, int line=-1)
void update_lexer_settings(bool update_apis_only=false)
QStatusBar * m_status_bar
octave_qscintilla * m_edit_area
QTextCodec * check_valid_codec(void)
void handle_cursor_moved(int line, int col)
void auto_margin_width(void)
void editor_state_changed(bool copy_available, bool is_octave_file, bool is_modified)
bool m_always_reload_changed_files
void handle_context_menu_break_condition(int linenr)
void set_current_directory(const QString &dir)
QString encoding(void) const
void handle_api_entries_added(void)
void handle_request_add_breakpoint(int line, const QString &cond)
void set_encoding(const QString &new_encoding)
void zoom_in(const QWidget *ID)
void scintilla_command(const QWidget *, unsigned int)
void toggle_bookmark(const QWidget *ID)
void find_linenr_just_before(int linenr, int &original_linenr, int &editor_linenr)
void change_editor_state(const QWidget *ID)
void edit_mfile_request(const QString &, const QString &, const QString &, int)
void update_lexer(void)
QString file_name(void) const
void show_auto_completion(const QWidget *ID)
QString m_prep_apis_path
void insert_debugger_pointer(const QWidget *ID, int line=-1)
void autoc_closed(void)
void request_open_file(const QString &, const QString &=QString())
void show_dialog(QDialog *dlg, bool modal)
QsciAPIs * m_lexer_apis
void zoom_normal(const QWidget *ID)
void tab_ready_to_close(void)
QDateTime m_last_modified
void handle_save_as_filter_selected(const QString &filter)
void do_save_file_signal(const QString &file_to_save, bool remove_on_success, bool restore_breakpoints)
void uncomment_selected_text(const QWidget *ID)
QsciScintilla::EolMode m_save_as_desired_eol
void handle_context_menu_edit(const QString &)
void print_file(const QWidget *ID)
void dbstop_if(const QString &prompt, int line, const QString &cond)
void maybe_remove_next(int remove_line)
void file_name_changed(const QString &fileName, const QString &toolTip, bool modified)
bool m_highlight_all_occurrences
void update_breakpoints_signal(const octave_value_list &args)
bool unchanged_or_saved(void)
QLabel * m_col_indicator
void context_edit(const QWidget *ID)
void goto_line(const QWidget *ID, int line=-1)
void handle_file_resave_answer(int decision)
breakpoint_info m_breakpoint_info
void handle_copy_available(bool enableCopy)
void handle_double_click(int p, int l, int modifier)
void toggle_breakpoint(const QWidget *ID)
QLabel * m_enc_indicator
void update_window_title(bool modified)
QLabel * m_row_indicator
QsciScintilla::EolMode detect_eol_mode(void)
void remove_breakpoint_via_debugger_linenr(int debugger_linenr)
void edit_area_changed(octave_qscintilla *edit_area)
void request_add_octave_apis(const QStringList &)
void closeEvent(QCloseEvent *event)
void zoom_out(const QWidget *ID)
void api_entries_added(void)
void handle_save_file_as_answer_cancel(void)
void do_smart_indent_line_or_selected_text(void)
error_system & get_error_system(void)
Definition: interpreter.h:251
tree_evaluator & get_evaluator(void)
symbol_table & get_symbol_table(void)
Definition: interpreter.h:298
void recover_from_exception(void)
load_path & get_load_path(void)
Definition: interpreter.h:283
std::list< std::string > autoloaded_functions(void) const
Definition: lex.h:766
lexer(interpreter &interp)
Definition: lex.h:769
string_vector fcn_names(void) const
Definition: load-path.cc:950
Definition: marker.h:45
void handle_find_translation(int original_linenr, int &editor_linenr, marker *&bp)
Definition: marker.cc:90
void handle_remove_via_original_linenr(int original_linenr)
Definition: marker.cc:62
void handle_find_just_before(int linenr, int &original_linenr, int &editor_linenr)
Definition: marker.cc:100
void handle_request_remove_via_editor_linenr(int editor_linenr)
Definition: marker.cc:71
void request_remove(int original_linenr)
@ debugger_position
Definition: marker.h:59
@ breakpoint
Definition: marker.h:55
@ cond_break
Definition: marker.h:56
@ bookmark
Definition: marker.h:58
@ unsure_debugger_position
Definition: marker.h:60
void handle_remove(void)
Definition: marker.cc:84
void handle_report_editor_linenr(QIntList &lines, QStringList &conditions)
Definition: marker.cc:120
const QString & get_cond(void) const
Definition: marker.h:73
void set_cond(const QString &cond)
Definition: marker.h:75
virtual octave_user_code * user_code_value(bool silent=false)
Definition: ov-base.cc:967
octave_idx_type numel(void) const
Definition: oct-map.h:389
const Cell & contents(const_iterator p) const
Definition: oct-map.h:331
void get_current_position(int *pos, int *line, int *col)
void smart_indent(bool do_smart_indent, int do_auto_close, int line, int ind_char_width)
QString eol_string(void)
void set_word_selection(const QString &word=QString())
void show_selection_markers(int l1, int c1, int l2, int c2)
void context_menu_break_condition_signal(int)
void smart_indent_line_or_selected_text(int lineFrom, int lineTo)
virtual void setCursorPosition(int line, int col)
void context_menu_edit_signal(const QString &)
void set_selection_marker_color(const QColor &c)
void interpreter_event(const fcn_callback &fcn)
void clear_selection_markers(void)
QStringList comment_string(bool comment=true)
int is_style_comment(int pos=-1)
void replace_all(const QString &o_str, const QString &n_str, bool re, bool cs, bool wo)
void update_rowcol_indicator_signal(int line, int col)
gui_settings * get_settings(void) const
void read_lexer_settings(QsciLexer *lexer, gui_settings *settings, int mode=0, int def=0)
void combo_encoding(QComboBox *combo, const QString &current=QString())
octave_idx_type numel(void) const
Definition: str-vec.h:100
void clear_user_function(const std::string &name)
Definition: symtab.cc:469
std::list< std::string > built_in_function_names(void)
Definition: symtab.cc:597
void dbquit(bool all=false)
Definition: pt-eval.cc:5015
bp_table & get_bp_table(void)
Definition: pt-eval.h:429
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
OCTAVE_EXPORT octave_value_list Fdbstatus(octave::interpreter &interp, const octave_value_list &args, int nargout)
Definition: debug.cc:372
std::string canonicalize_file_name(const std::string &name)
Definition: file-ops.cc:744
MArray< T > filter(MArray< T > &b, MArray< T > &a, MArray< T > &x, MArray< T > &si, int dim=0)
Definition: filter.cc:48
const gui_pref cs_cursor_blinking("terminal/cursorBlinking", QVariant(true))
const gui_pref ed_always_reload_changed_files("editor/always_reload_changed_files", QVariant(false))
const gui_pref ed_indent_uses_tabs("editor/indent_uses_tabs", QVariant(false))
const gui_pref ed_code_completion("editor/codeCompletion", QVariant(true))
const gui_pref ed_show_hscroll_bar("editor/show_hscroll_bar", QVariant(true))
const gui_pref ed_code_completion_octave_functions("editor/codeCompletion_octave_functions", QVariant(true))
const gui_pref ed_auto_endif("editor/auto_endif", QVariant(1))
const gui_pref ed_show_white_space("editor/show_white_space", QVariant(false))
const gui_pref ed_color_mode("editor/color_mode", QVariant(0))
const gui_pref ed_line_numbers_size("editor/line_numbers_size", QVariant(0))
const gui_pref ed_wrap_lines("editor/wrap_lines", QVariant(false))
const gui_pref ed_highlight_current_line("editor/highlightCurrentLine", QVariant(true))
const gui_pref ed_break_lines("editor/break_lines", QVariant(false))
const gui_pref ed_break_lines_comments("editor/break_lines_comments", QVariant(false))
const QString ed_last_comment_str("editor/oct_last_comment_str")
const gui_pref ed_highlight_current_line_color("editor/highlightCurrentLineColor", QVariant(settings_color_no_change))
const gui_pref ed_long_line_marker("editor/long_line_marker", QVariant(true))
const gui_pref ed_tab_indents_line("editor/tab_indents_line", QVariant(false))
const gui_pref ed_code_completion_octave_builtins("editor/codeCompletion_octave_builtins", QVariant(true))
const gui_pref ed_show_edit_status_bar("editor/show_edit_status_bar", QVariant(true))
const gui_pref ed_code_completion_threshold("editor/codeCompletion_threshold", QVariant(3))
const gui_pref ed_code_completion_document("editor/codeCompletion_document", QVariant(true))
const gui_pref ed_backspace_unindents_line("editor/backspace_unindents_line", QVariant(false))
const gui_pref ed_code_completion_case("editor/codeCompletion_case", QVariant(true))
const gui_pref ed_rm_trailing_spaces("editor/rm_trailing_spaces", QVariant(true))
const gui_pref ed_default_enc("editor/default_encoding", QVariant("UTF-8"))
const gui_pref ed_long_line_marker_background("editor/long_line_marker_background", QVariant(false))
const gui_pref ed_show_white_space_indent("editor/show_white_space_indent", QVariant(false))
const gui_pref ed_show_line_numbers("editor/showLineNumbers", QVariant(true))
const gui_pref ed_highlight_all_occurrences("editor/highlight_all_occurrences", QVariant(true))
const gui_pref ed_show_eol_chars("editor/show_eol_chars", QVariant(false))
const gui_pref ed_show_indent_guides("editor/show_indent_guides", QVariant(false))
const gui_pref ed_code_folding("editor/code_folding", QVariant(true))
const gui_pref ed_force_newline("editor/force_newline", QVariant(true))
const gui_pref ed_indent_width("editor/indent_width", QVariant(2))
const gui_pref ed_tab_width("editor/tab_width", QVariant(2))
const gui_pref ed_long_line_column("editor/long_line_column", QVariant(80))
const gui_pref ed_default_eol_mode("editor/default_eol_mode", QVariant(os_eol_mode))
const gui_pref ed_code_completion_replace("editor/codeCompletion_replace", QVariant(false))
const gui_pref ed_code_completion_keywords("editor/codeCompletion_keywords", QVariant(true))
const gui_pref ed_auto_indent("editor/auto_indent", QVariant(true))
const gui_pref ed_long_line_marker_line("editor/long_line_marker_line", QVariant(true))
const gui_pref global_use_native_dialogs("use_native_file_dialogs", QVariant(true))
const gui_pref global_cursor_blinking("cursor_blinking", QVariant(true))
const QColor settings_color_no_change(255, 0, 255)
OCTGUI_API QColor interpolate_color(const QColor &col1, const QColor &col2, double fs, double fv)
Definition: gui-utils.cc:39
OCTAVE_EXPORT octave_value_list Fiskeyword(const octave_value_list &args, int)
Definition: lex.cc:5019
F77_RET_T const F77_DBLE const F77_DBLE * f
#define OCTAVE_VERSION
Definition: main.in.cc:63
for(octave_idx_type i=0;i< n;i++) ac+
Definition: mx-inlines.cc:756
QString fromStdString(const std::string &s)
#define lexer
Definition: oct-parse.cc:146
void free(void *)
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211
static OCTAVE_NORETURN void eval_error(const char *msg, const dim_vector &x, const dim_vector &y)
Definition: pt-tm-const.cc:56
static uint32_t state[624]
Definition: randmtzig.cc:193
const QString key
char * octave_u32_conv_to_encoding_strict(const char *tocode, const uint32_t *src, size_t srclen, size_t *lengthp)
bool valid_identifier(const char *s)
Definition: utils.cc:78
F77_RET_T len
Definition: xerbla.cc:61