GNU Octave  4.4.1
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-2018 Jacob Dawid
4 
5 This file is part of Octave.
6 
7 Octave is free software: you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <https://www.gnu.org/licenses/>.
20 
21 */
22 
23 //! @file file-editor-tab.cc A single GUI file tab.
24 //!
25 //! This interfaces QsciScintilla with the rest of Octave.
26 
27 #if defined (HAVE_CONFIG_H)
28 # include "config.h"
29 #endif
30 
31 #if defined (HAVE_QSCINTILLA)
32 
33 #if defined (HAVE_QSCI_QSCILEXEROCTAVE_H)
34 # define HAVE_LEXER_OCTAVE 1
35 # include <Qsci/qscilexeroctave.h>
36 #elif defined (HAVE_QSCI_QSCILEXERMATLAB_H)
37 # define HAVE_LEXER_MATLAB 1
38 # include <Qsci/qscilexermatlab.h>
39 #endif
40 #include <Qsci/qscilexercpp.h>
41 #include <Qsci/qscilexerbash.h>
42 #include <Qsci/qscilexerperl.h>
43 #include <Qsci/qscilexerbatch.h>
44 #include <Qsci/qscilexerdiff.h>
45 #include <Qsci/qsciprinter.h>
46 #include <QApplication>
47 #include <QFileDialog>
48 #include <QMessageBox>
49 #include <QTextStream>
50 #include <QVBoxLayout>
51 #include <QInputDialog>
52 #include <QPrintDialog>
53 #include <QDateTime>
54 #include <QDesktopServices>
55 #include <QTextCodec>
56 #include <QStyle>
57 #include <QTextBlock>
58 #include <QLabel>
59 #include <QCheckBox>
60 #include <QDialogButtonBox>
61 #include <QPushButton>
62 
63 #include "resource-manager.h"
64 #include "file-editor-tab.h"
65 #include "file-editor.h"
66 #include "octave-txt-lexer.h"
67 #include "marker.h"
68 
69 #include "file-ops.h"
70 
71 #include "bp-table.h"
72 #include "call-stack.h"
73 #include "defaults.h"
74 #include "interpreter-private.h"
75 #include "interpreter.h"
76 #include "oct-map.h"
77 #include "octave-qt-link.h"
78 #include "ov-usr-fcn.h"
79 #include "symtab.h"
80 #include "unwind-prot.h"
81 #include "utils.h"
82 #include "version.h"
83 #include "octave-settings.h"
84 
85 namespace octave
86 {
87  bool file_editor_tab::_cancelled = false;
88 
89  //! A file_editor_tab object consists of a text area and three left margins.
90  //! The first holds breakpoints, bookmarks, and the debug program counter.
91  //! The second holds line numbers. The third holds "fold" marks, to hide
92  //! sections of text.
93 
94  // Make parent null for the file editor tab so that warning WindowModal
95  // messages don't affect grandparents.
96  file_editor_tab::file_editor_tab (const QString& directory_arg)
97  {
98  _lexer_apis = nullptr;
99  _is_octave_file = true;
100  _lines_changed = false;
101 
102  _ced = directory_arg;
103 
104  _file_name = "";
105  _file_system_watcher.setObjectName ("_qt_autotest_force_engine_poller");
106 
107  _edit_area = new octave_qscintilla (this);
108  _line = 0;
109  _col = 0;
110 
111  _bp_lines.clear (); // start with empty lists of breakpoints
112  _bp_conditions.clear ();
113  m_bp_restore_count = 0;
114 
115  connect (_edit_area, SIGNAL (cursorPositionChanged (int, int)),
116  this, SLOT (handle_cursor_moved (int,int)));
117 
118  connect (_edit_area, SIGNAL (SCN_CHARADDED (int)),
119  this, SLOT (handle_char_added (int)));
120 
121  connect (_edit_area, SIGNAL (SCN_DOUBLECLICK (int, int, int)),
122  this, SLOT (handle_double_click (int, int, int)));
123 
124  connect (_edit_area, SIGNAL (linesChanged ()),
125  this, SLOT (handle_lines_changed ()));
126 
127  connect (_edit_area, SIGNAL (context_menu_edit_signal (const QString&)),
128  this, SLOT (handle_context_menu_edit (const QString&)));
129 
130  // create statusbar for row/col indicator and eol mode
131  _status_bar = new QStatusBar (this);
132 
133  // row- and col-indicator
134  _row_indicator = new QLabel ("", this);
135  QFontMetrics fm = _row_indicator->fontMetrics ();
136  _row_indicator->setMinimumSize (4.5*fm.averageCharWidth (),0);
137  QLabel *row_label = new QLabel (tr ("line:"), this);
138  _col_indicator = new QLabel ("", this);
139  _col_indicator->setMinimumSize (4*fm.averageCharWidth (),0);
140  QLabel *col_label = new QLabel (tr ("col:"), this);
141  _status_bar->addWidget (row_label, 0);
142  _status_bar->addWidget (_row_indicator, 0);
143  _status_bar->addWidget (col_label, 0);
144  _status_bar->addWidget (_col_indicator, 0);
145 
146  // status bar: encoding
147  QLabel *enc_label = new QLabel (tr ("encoding:"), this);
148  _enc_indicator = new QLabel ("",this);
149  _status_bar->addWidget (enc_label, 0);
150  _status_bar->addWidget (_enc_indicator, 0);
151  _status_bar->addWidget (new QLabel (" ", this), 0);
152 
153  // status bar: eol mode
154  QLabel *eol_label = new QLabel (tr ("eol:"), this);
155  _eol_indicator = new QLabel ("",this);
156  _status_bar->addWidget (eol_label, 0);
157  _status_bar->addWidget (_eol_indicator, 0);
158  _status_bar->addWidget (new QLabel (" ", this), 0);
159 
160  // Leave the find dialog box out of memory until requested.
161  _find_dialog = nullptr;
162  _find_dialog_is_visible = false;
163 
164  // symbols
165  _edit_area->setMarginType (1, QsciScintilla::SymbolMargin);
166  _edit_area->setMarginSensitivity (1, true);
167  _edit_area->markerDefine (QsciScintilla::RightTriangle, marker::bookmark);
168  _edit_area->setMarkerBackgroundColor (QColor (0,0,232), marker::bookmark);
169  _edit_area->markerDefine (QsciScintilla::Circle, marker::breakpoint);
170  _edit_area->setMarkerBackgroundColor (QColor (192,0,0), marker::breakpoint);
171  _edit_area->markerDefine (QsciScintilla::Circle, marker::cond_break);
172  _edit_area->setMarkerBackgroundColor (QColor (255,127,0), marker::cond_break);
173  _edit_area->markerDefine (QsciScintilla::RightArrow, marker::debugger_position);
174  _edit_area->setMarkerBackgroundColor (QColor (255,255,0),
176  _edit_area->markerDefine (QsciScintilla::RightArrow,
178  _edit_area->setMarkerBackgroundColor (QColor (192,192,192),
180 
181  connect (_edit_area, SIGNAL (marginClicked (int, int,
182  Qt::KeyboardModifiers)),
183  this, SLOT (handle_margin_clicked (int, int,
184  Qt::KeyboardModifiers)));
185 
186  connect (_edit_area, SIGNAL (context_menu_break_condition_signal (int)),
187  this, SLOT (handle_context_menu_break_condition (int)));
188 
189  // line numbers
190  _edit_area->setMarginsForegroundColor (QColor (96, 96, 96));
191  _edit_area->setMarginsBackgroundColor (QColor (232, 232, 220));
192  _edit_area->setMarginType (2, QsciScintilla::TextMargin);
193 
194  // other features
195  _edit_area->setBraceMatching (QsciScintilla::StrictBraceMatch);
196  _edit_area->setAutoIndent (true);
197  _edit_area->setIndentationWidth (2);
198  _edit_area->setIndentationsUseTabs (false);
199 
200  _edit_area->setUtf8 (true);
201 
202  // auto completion
203  _edit_area->SendScintilla (QsciScintillaBase::SCI_AUTOCSETCANCELATSTART, false);
204 
205  QVBoxLayout *edit_area_layout = new QVBoxLayout ();
206  edit_area_layout->addWidget (_edit_area);
207  edit_area_layout->addWidget (_status_bar);
208  edit_area_layout->setMargin (0);
209  setLayout (edit_area_layout);
210 
211  // connect modified signal
212  connect (_edit_area, SIGNAL (modificationChanged (bool)),
213  this, SLOT (update_window_title (bool)));
214 
215  connect (_edit_area, SIGNAL (copyAvailable (bool)),
216  this, SLOT (handle_copy_available (bool)));
217 
218  connect (&_file_system_watcher, SIGNAL (fileChanged (const QString&)),
219  this, SLOT (file_has_changed (const QString&)));
220 
221  QSettings *settings = resource_manager::get_settings ();
222  if (settings)
223  notice_settings (settings, true);
224 
225  setFocusProxy (_edit_area);
226 
227  // encoding, not updated with the settings
228 #if defined (Q_OS_WIN32)
229  _encoding = settings->value ("editor/default_encoding", "SYSTEM")
230  .toString ();
231 #else
232  _encoding = settings->value ("editor/default_encoding", "UTF-8")
233  .toString ();
234 #endif
235  _enc_indicator->setText (_encoding);
236  // no changes in encoding yet
238  }
239 
241  {
242  // Tell all connected markers to self-destruct.
243  emit remove_all_breakpoints ();
244  emit remove_all_positions ();
245 
246  // Destroy items attached to _edit_area.
247  QsciLexer *lexer = _edit_area->lexer ();
248  if (lexer)
249  {
250  delete lexer;
251  _edit_area->setLexer (nullptr);
252  }
253  if (_find_dialog)
254  {
255  delete _find_dialog;
256  _find_dialog = nullptr;
257  }
258 
259  // Destroy _edit_area.
260  delete _edit_area;
261  }
262 
263  void file_editor_tab::set_encoding (const QString& new_encoding)
264  {
265  if (new_encoding.isEmpty ())
266  return;
267 
268  _encoding = new_encoding;
269  _enc_indicator->setText (_encoding);
270  if (! _edit_area->text ().isEmpty ())
271  set_modified (true);
272  }
273 
274  void file_editor_tab::closeEvent (QCloseEvent *e)
275  {
276  _cancelled = false; // prevent unwanted interaction of previous
277  // exits of octave which were canceled by the user
278 
279  if (check_file_modified () == QMessageBox::Cancel)
280  {
281  // ignore close event if file is not saved and user cancels
282  // closing this window
283  e->ignore ();
284  }
285  else
286  {
287  e->accept ();
288  emit tab_remove_request ();
289  }
290  }
291 
292  void file_editor_tab::set_current_directory (const QString& dir)
293  {
294  _ced = dir;
295  }
296 
297  void file_editor_tab::handle_context_menu_edit (const QString& word_at_cursor)
298  {
299  // search for a subfunction in actual file (this is done at first because
300  // octave finds this function before other with same name in the search path
301  QRegExp rxfun1 ("^[\t ]*function[^=]+=[\t ]*"
302  + word_at_cursor + "[\t ]*\\([^\\)]*\\)[\t ]*$");
303  QRegExp rxfun2 ("^[\t ]*function[\t ]+"
304  + word_at_cursor + "[\t ]*\\([^\\)]*\\)[\t ]*$");
305  QRegExp rxfun3 ("^[\t ]*function[\t ]+"
306  + word_at_cursor + "[\t ]*$");
307  QRegExp rxfun4 ("^[\t ]*function[^=]+=[\t ]*"
308  + word_at_cursor + "[\t ]*$");
309 
310  int pos_fct = -1;
311  QStringList lines = _edit_area->text ().split ("\n");
312 
313  int line;
314  for (line = 0; line < lines.count (); line++)
315  {
316  if ((pos_fct = rxfun1.indexIn (lines.at (line))) != -1)
317  break;
318  if ((pos_fct = rxfun2.indexIn (lines.at (line))) != -1)
319  break;
320  if ((pos_fct = rxfun3.indexIn (lines.at (line))) != -1)
321  break;
322  if ((pos_fct = rxfun4.indexIn (lines.at (line))) != -1)
323  break;
324  }
325 
326  if (pos_fct > -1)
327  {
328  // reg expr. found: it is an internal function
329  _edit_area->setCursorPosition (line, pos_fct);
330  _edit_area->SendScintilla (2232, line); // SCI_ENSUREVISIBLE
331  // SCI_VISIBLEFROMDOCLINE
332  int vis_line = _edit_area->SendScintilla (2220, line);
333  _edit_area->SendScintilla (2613, vis_line); // SCI_SETFIRSTVISIBLELINE
334  return;
335  }
336 
337  emit edit_mfile_request (word_at_cursor, _file_name, _ced, -1);
338  }
339 
340  // If "dbstop if ..." selected from context menu, create a conditional
341  // breakpoint. The default condition is (a) the existing condition if there
342  // is already a breakpoint (b) any selected text, or (c) empty
344  {
345  // Ensure editor line numbers match Octave core's line numbers.
346  // Give users the option to save modifications if necessary.
347  if (! unchanged_or_saved ())
348  return;
349 
350  QString cond;
351  bp_info info (_file_name, linenr+1); // Get function name & dir from filename.
352 
353  // Search for previous condition. FIXME: is there a more direct way?
354  if (_edit_area->markersAtLine (linenr) & (1 << marker::cond_break))
355  {
357  for (int i = 0; i < _bp_lines.length (); i++)
358  if (_bp_lines.value (i) == linenr)
359  {
360  cond = _bp_conditions.value (i);
361  break;
362  }
363  _bp_lines.clear ();
364  _bp_conditions.clear ();
365  }
366 
367  // If text selected by the mouse, default to that instead
368  // If both present, use the OR of them, to avoid accidental overwriting
369  // FIXME: If both are present, show old condition unselected and
370  // the selection (in edit area) selected (in the dialog).
371  if (_edit_area->hasSelectedText ())
372  {
373  if (cond == "")
374  cond = _edit_area->selectedText ();
375  else
376  cond = '(' + cond + ") || (" + _edit_area->selectedText () + ')';
377  }
378 
379  bool valid = false;
380  std::string prompt = "dbstop if";
381  bool ok;
382  while (! valid)
383  {
384  QString new_condition
385  = QInputDialog::getText (this, tr ("Breakpoint condition"),
386  tr (prompt.c_str ()), QLineEdit::Normal, cond,
387  &ok);
388  if (ok) // If cancel, don't change breakpoint condition.
389  {
390  try
391  {
392  // Suppress error messages on the console.
396 
397  octave::bp_table& bptab
398  = octave::__get_bp_table__ ("handle_context_menu_break_condition");
399 
400  bptab.condition_valid (new_condition.toStdString ());
401  valid = true;
402  }
403  catch (const index_exception& e) { }
404  catch (const execution_exception& e) { }
405  catch (const interrupt_exception&)
406  {
407  ok = false;
408  valid = true;
409  }
410 
411  // In case we repeat, set new prompt.
412  prompt = "ERROR: " + last_error_message () + "\n\ndbstop if";
413  cond = new_condition;
414  }
415  else
416  valid = true;
417  }
418 
419  if (ok) // If the user didn't cancel, actually set the breakpoint.
420  {
421  info.condition = cond.toStdString ();
422 
425  }
426  }
427 
428  void file_editor_tab::set_file_name (const QString& fileName)
429  {
430  // update tracked file if we really have a file on disk
431  QStringList trackedFiles = _file_system_watcher.files ();
432  if (! trackedFiles.isEmpty ())
433  _file_system_watcher.removePath (_file_name);
434  if (! fileName.isEmpty ())
435  _file_system_watcher.addPath (fileName);
436 
437  // update lexer and file name variable if file name changes
438  if (_file_name != fileName)
439  {
440  _file_name = fileName;
441  update_lexer ();
442  }
443 
444  // update the file editor with current editing directory
446 
447  // add the new file to the most-recently-used list
449  }
450 
451  // valid_file_name (file): checks whether "file" names a file.
452  // By default, "file" is empty; then _file_name is checked
454  {
455  if (file.isEmpty ())
456  {
457  if (_file_name.isEmpty ())
458  return false;
459  else
460  return true;
461  }
462 
463  return true;
464  }
465 
466  // We cannot create a breakpoint when the file is modified
467  // because the line number the editor is providing might
468  // not match what Octave core is interpreting in the
469  // file on disk. This function gives the user the option
470  // to save before creating the breakpoint.
472  {
473  bool retval = true;
474  if (_edit_area->isModified ())
475  {
476  int ans = QMessageBox::question (nullptr, tr ("Octave Editor"),
477  tr ("Cannot add breakpoint to modified file.\n"
478  "Save and add breakpoint, or cancel?"),
479  QMessageBox::Save | QMessageBox::Cancel, QMessageBox::Save);
480 
481  if (ans == QMessageBox::Save)
482  save_file (_file_name, false);
483  else
484  retval = false;
485  }
486 
487  return retval;
488  }
489 
490  // Toggle a breakpoint at the editor_linenr or, if this was called by
491  // a click with CTRL pressed, toggle a bookmark at that point.
492  void file_editor_tab::handle_margin_clicked (int margin, int editor_linenr,
493  Qt::KeyboardModifiers state)
494  {
495  if (margin == 1)
496  {
497  unsigned int markers_mask = _edit_area->markersAtLine (editor_linenr);
498 
499  if (state & Qt::ControlModifier)
500  {
501  if (markers_mask & (1 << marker::bookmark))
502  _edit_area->markerDelete (editor_linenr, marker::bookmark);
503  else
504  _edit_area->markerAdd (editor_linenr, marker::bookmark);
505  }
506  else
507  {
508  if (markers_mask & ((1 << marker::breakpoint)
509  | (1 << marker::cond_break)))
510  handle_request_remove_breakpoint (editor_linenr + 1);
511  else
512  {
513  if (unchanged_or_saved ())
514  handle_request_add_breakpoint (editor_linenr + 1, "");
515  }
516  }
517  }
518  }
519 
520 
522  {
523  // Create a new lexer
524  QsciLexer *lexer = nullptr;
525 
526  _is_octave_file = false;
527 
528  // Find the required lexer from file extensions
529  if (_file_name.endsWith (".m")
530  || _file_name.endsWith ("octaverc"))
531  {
532 #if defined (HAVE_LEXER_OCTAVE)
533  lexer = new QsciLexerOctave ();
534 #elif defined (HAVE_LEXER_MATLAB)
535  lexer = new QsciLexerMatlab ();
536 #else
537  lexer = new octave_txt_lexer ();
538 #endif
539  _is_octave_file = true;
540  }
541 
542  if (! lexer)
543  {
544  if (_file_name.endsWith (".c")
545  || _file_name.endsWith (".cc")
546  || _file_name.endsWith (".cpp")
547  || _file_name.endsWith (".cxx")
548  || _file_name.endsWith (".c++")
549  || _file_name.endsWith (".h")
550  || _file_name.endsWith (".hh")
551  || _file_name.endsWith (".hpp")
552  || _file_name.endsWith (".h++"))
553  {
554  lexer = new QsciLexerCPP ();
555  }
556  else if (_file_name.endsWith (".pl"))
557  {
558  lexer = new QsciLexerPerl ();
559  }
560  else if (_file_name.endsWith (".bat"))
561  {
562  lexer = new QsciLexerBatch ();
563  }
564  else if (_file_name.endsWith (".diff"))
565  {
566  lexer = new QsciLexerDiff ();
567  }
568  else if (_file_name.endsWith (".sh"))
569  {
570  lexer = new QsciLexerBash ();
571  }
572  else if (! valid_file_name ())
573  {
574  // new, no yet named file: let us assume it is octave
575 #if defined (HAVE_LEXER_OCTAVE)
576  lexer = new QsciLexerOctave ();
577  _is_octave_file = true;
578 #elif defined (HAVE_LEXER_MATLAB)
579  lexer = new QsciLexerMatlab ();
580  _is_octave_file = true;
581 #else
582  lexer = new octave_txt_lexer ();
583 #endif
584  }
585  else
586  {
587  // other or no extension
588  lexer = new octave_txt_lexer ();
589  }
590  }
591 
592  // Get any existing lexer
593  QsciLexer *old_lexer = _edit_area->lexer ();
594 
595  // If new file, no lexer, or lexer has changed,
596  // delete old one and set the newly created as current lexer
597  if (! old_lexer || ! valid_file_name ()
598  || QString(old_lexer->lexer ()) != QString(lexer->lexer ()))
599  {
600  // Delete and set new lexer
601  if (old_lexer)
602  delete old_lexer;
603  _edit_area->setLexer (lexer);
604 
605  // build information for auto completion (APIs)
606  _lexer_apis = new QsciAPIs (lexer);
607 
608  // Get the settings for this new lexer
610  }
611  else
612  {
613  // Otherwise, delete the newly created lexer and
614  // use the old, exisiting one
615  if (lexer)
616  delete lexer;
617  }
618  }
619 
620 
621  // Update settings, which are lexer related and have to be updated
622  // when a) the lexer changes or b) the settings have changed.
624  {
625  QsciLexer *lexer = _edit_area->lexer ();
626 
627  QSettings *settings = resource_manager::get_settings ();
628 
629  if (_lexer_apis)
630  {
631  _lexer_apis->cancelPreparation (); // stop preparing if apis exists
632 
633  bool update_apis = false; // flag, whether update of apis files
634 
635  // get path to prepared api info
636 #if defined (HAVE_QSTANDARDPATHS)
637  QString prep_apis_path
638  = QStandardPaths::writableLocation (QStandardPaths::HomeLocation)
639  + "/.config/octave/" + QString (OCTAVE_VERSION) + "/qsci/";
640 #else
641  QString prep_apis_path
642  = QDesktopServices::storageLocation (QDesktopServices::HomeLocation)
643  + "/.config/octave/" + QString (OCTAVE_VERSION) + "/qsci/";
644 #endif
645 
646  // get settings which infos are used for octave
647  bool octave_builtins = settings->value (
648  "editor/codeCompletion_octave_builtins", true).toBool ();
649  bool octave_functions = settings->value (
650  "editor/codeCompletion_octave_functions", true).toBool ();
651 
652  if (_is_octave_file)
653  {
654  // Keywords and Builtins do not change, these informations can be
655  // stored in prepared form in a file. Information on function are
656  // changing frequently, then if functions should also be auto-
657  // completed, the date of any existing file is checked.
658 
659  // Keywords are always used
660  _prep_apis_file = prep_apis_path + lexer->lexer () + "_k";
661 
662  // Buitlins are only used if the user settings say so
663  if (octave_builtins)
664  _prep_apis_file += 'b';
665 
666  if (octave_functions)
667  _prep_apis_file += 'f';
668 
669  _prep_apis_file += ".pap"; // final name of apis file
670 
671  // check whether the APIs info needs to be prepared and saved
672  QFileInfo apis_file = QFileInfo (_prep_apis_file);
673  // flag whether apis file needs update
674  update_apis = ! apis_file.exists ();
675 
676  if (octave_functions)
677  {
678  // Functions may change frequently. Update the apis data
679  // if the file is older than a few minutes preventing from
680  // re-preparing data when the user opens several files.
681  QDateTime apis_time = apis_file.lastModified ();
682  if (QDateTime::currentDateTime () > apis_time.addSecs (180))
683  update_apis = true;
684  }
685 
686  }
687  else // no octave file, just add extension
688  {
689 
690  _prep_apis_file = prep_apis_path + lexer->lexer () + ".pap";
691 
692  }
693 
694  if (update_apis || ! _lexer_apis->loadPrepared (_prep_apis_file))
695  {
696  // either we have decided to update the apis file or
697  // no prepared info was loaded, prepare and save if possible
698 
699  // create raw apis info
700  QString keyword;
701  QStringList keyword_list;
702  int i,j;
703 
704  if (_is_octave_file)
705  {
706  // octave: get keywords from internal informations depending on
707  // user preferences
708 
709  // keywords are always used
710  add_octave_apis (Fiskeyword ()); // add new entries
711 
712  interpreter& interp
714  "file_editor_tab::update_lexer_settings");
715 
716  if (octave_builtins)
717  add_octave_apis (F__builtins__ (interp)); // add new entries
718 
719  if (octave_functions)
720  add_octave_apis (F__list_functions__ (interp)); // add new entries
721 
722  }
723  else
724  {
725 
726  _prep_apis_file = prep_apis_path + lexer->lexer () + ".pap";
727 
728  for (i=1; i<=3; i++) // test the first 5 keyword sets
729  {
730  keyword = QString (lexer->keywords (i)); // get list
731  keyword_list = keyword.split (QRegExp (R"(\s+)")); // split
732  for (j = 0; j < keyword_list.size (); j++) // add to API
733  _lexer_apis->add (keyword_list.at (j));
734  }
735  }
736 
737  // disconnect slot for saving prepared info if already connected
738  disconnect (_lexer_apis, SIGNAL (apiPreparationFinished ()), nullptr, nullptr);
739  // check whether path for prepared info exists or can be created
740  if (QDir ("/").mkpath (prep_apis_path))
741  {
742  // path exists, apis info can be saved there
743  connect (_lexer_apis, SIGNAL (apiPreparationFinished ()),
744  this, SLOT (save_apis_info ()));
745  }
746 
747  _lexer_apis->prepare (); // prepare apis info
748 
749  }
750  }
751 
752  lexer->readSettings (*settings);
753 
754  _edit_area->setCaretForegroundColor (lexer->color (0));
755  _edit_area->setIndentationGuidesForegroundColor (lexer->color (0));
756 
757  // set some colors depending on selected background color of the lexer
758  QColor bg = lexer->paper (0);
759  QColor fg = lexer->color (0);
760 
761  int bh, bs, bv, fh, fs, fv, h, s, v;
762  bg.getHsv (&bh,&bs,&bv);
763  fg.getHsv (&fh,&fs,&fv);
764 
765  // margin colors
766  h = bh;
767  s = bs/2;
768  v = bv + (fv - bv)/5;
769 
770  bg.setHsv (h,s,v);
771  _edit_area->setEdgeColor (bg);
772 
773  v = bv + (fv - bv)/8;
774  bg.setHsv (h,s,v);
775  v = bv + (fv - bv)/4;
776  fg.setHsv (h,s,v);
777 
778  _edit_area->setMarkerForegroundColor (lexer->color (0));
779  _edit_area->setMarginsForegroundColor (lexer->color (0));
780  _edit_area->setMarginsBackgroundColor (bg);
781  _edit_area->setFoldMarginColors (bg,fg);
782 
783  // color indicator for highlighting all occurrences:
784  // applications highlight color with more transparency
785  QColor hg = QApplication::palette ().color (QPalette::Highlight);
787 
788  // fix line number width with respect to the font size of the lexer and
789  // set the line numbers font depending on the lexers font
790  if (settings->value ("editor/showLineNumbers", true).toBool ())
791  {
792  // Line numbers width
794 
795  // Line numbers font
796  QFont line_numbers_font = lexer->defaultFont ();
797  int font_size = line_numbers_font.pointSize ();
798  font_size = font_size
799  + settings->value ("editor/line_numbers_size", 0).toInt ();
800  if (font_size < 4)
801  font_size = 4;
802  line_numbers_font.setPointSize (font_size);
803 
804  _edit_area->setMarginsFont (line_numbers_font);
805  }
806  else
807  _edit_area->setMarginWidth (2,0);
808  }
809 
810 
811  // function for adding entries to the octave lexer's APIs
813  {
814  octave_value keys = key_ovl(0);
815  Cell key_list = keys.cell_value ();
816 
817  for (int idx = 0; idx < key_list.numel (); idx++)
818  _lexer_apis->add (QString (key_list.elem (idx).string_value ().data ()));
819  }
820 
822  {
823  _lexer_apis->savePrepared (_prep_apis_file);
824  }
825 
826  // slot for fetab_set_focus: sets the focus to the current edit area
828  {
829  if (ID != this)
830  return;
831  _edit_area->setFocus ();
832  }
833 
834  void file_editor_tab::context_help (const QWidget *ID, bool doc)
835  {
836  if (ID != this)
837  return;
838 
840  }
841 
843  {
844  if (ID != this)
845  return;
846 
848  }
849 
851  {
852  if (_cancelled)
853  return;
854 
855  if (check_file_modified () == QMessageBox::Cancel)
856  _cancelled = true;
857  }
858 
860  {
861  if (ID != this)
862  return;
863 
865  }
866 
867  void file_editor_tab::save_file (const QWidget *ID, const QString& fileName,
868  bool remove_on_success)
869  {
870  if (ID != this)
871  return;
872 
873  save_file (fileName, remove_on_success);
874  }
875 
877  {
878  if (ID != this)
879  return;
880 
881  save_file_as ();
882  }
883 
885  {
886  if (ID != this)
887  return;
888 
889  QsciPrinter *printer = new QsciPrinter (QPrinter::HighResolution);
890 
891  QPrintDialog printDlg (printer, this);
892 
893  if (printDlg.exec () == QDialog::Accepted)
894  printer->printRange (_edit_area);
895 
896  delete printer;
897  }
898 
900  {
901  if (ID != this)
902  return;
903 
904  if (_edit_area->isModified () | ! valid_file_name ())
905  {
906  save_file (_file_name); // save file dialog
907  if (! valid_file_name ())
908  return; // still invalid filename: "save as" was cancelled
909  }
910 
911  QFileInfo info (_file_name);
912  emit run_file_signal (info);
913  }
914 
916  {
917  if (ID != this)
918  return;
919 
921  }
922 
924  {
925  if (ID != this)
926  return;
927 
928  int line, cur;
929  _edit_area->getCursorPosition (&line, &cur);
930 
931  if (_edit_area->markersAtLine (line) & (1 << marker::bookmark))
932  _edit_area->markerDelete (line, marker::bookmark);
933  else
934  _edit_area->markerAdd (line, marker::bookmark);
935  }
936 
937  // Move the text cursor to the closest bookmark
938  // after the current line.
940  {
941  if (ID != this)
942  return;
943 
944  int line, cur;
945  _edit_area->getCursorPosition (&line, &cur);
946 
947  line++; // Find bookmark strictly after the current line.
948 
949  int nextline = _edit_area->markerFindNext (line, (1 << marker::bookmark));
950 
951  // Wrap.
952  if (nextline == -1)
953  nextline = _edit_area->markerFindNext (1, (1 << marker::bookmark));
954 
955  _edit_area->setCursorPosition (nextline, 0);
956  }
957 
958  // Move the text cursor to the closest bookmark
959  // before the current line.
961  {
962  if (ID != this)
963  return;
964 
965  int line, cur;
966  _edit_area->getCursorPosition (&line, &cur);
967 
968  line--; // Find bookmark strictly before the current line.
969 
970  int prevline = _edit_area->markerFindPrevious (line, (1 << marker::bookmark));
971 
972  // Wrap. Should use the last line of the file, not 1<<15
973  if (prevline == -1)
974  prevline = _edit_area->markerFindPrevious (_edit_area->lines (),
975  (1 << marker::bookmark));
976 
977  _edit_area->setCursorPosition (prevline, 0);
978  }
979 
981  {
982  if (ID != this)
983  return;
984 
985  _edit_area->markerDeleteAll (marker::bookmark);
986  }
987 
989  {
990  bp_table::intmap line_info;
991  line_info[0] = info.line;
992 
993  if (octave_qt_link::file_in_path (info.file, info.dir))
994  {
995  octave::bp_table& bptab
996  = octave::__get_bp_table__ ("octave_qt_link::file_in_path");
997 
998  bptab.add_breakpoint (info.function_name, line_info, info.condition);
999  }
1000  }
1001 
1003  {
1004  bp_table::intmap line_info;
1005  line_info[0] = info.line;
1006 
1007  if (octave_qt_link::file_in_path (info.file, info.dir))
1008  {
1009  octave::bp_table& bptab
1010  = octave::__get_bp_table__ ("remove_breakpoint_callback");
1011 
1012  bptab.remove_breakpoint (info.function_name, line_info);
1013  }
1014  }
1015 
1017  {
1018  if (octave_qt_link::file_in_path (info.file, info.dir))
1019  {
1020  octave::bp_table& bptab
1021  = octave::__get_bp_table__ ("remove_all_breakpoints_callback");
1022 
1023  bptab.remove_all_breakpoints_in_file (info.function_name, true);
1024  }
1025  }
1026 
1028  const QString& cond)
1029  : line (l), file (fname.toStdString ()), condition (cond.toStdString ())
1030  {
1031  QFileInfo file_info (fname);
1032 
1033  QString q_dir = file_info.absolutePath ();
1034  QString q_function_name = file_info.fileName ();
1035 
1036  // We have to cut off the suffix, because octave appends it.
1037  q_function_name.chop (file_info.suffix ().length () + 1);
1038 
1039  dir = q_dir.toStdString ();
1040  function_name = q_function_name.toStdString ();
1041 
1042  // Is the last component of DIR @foo? If so, strip it and prepend it
1043  // to the name of the function.
1044 
1045  size_t pos = dir.rfind (sys::file_ops::dir_sep_chars ());
1046 
1047  if (pos != std::string::npos && pos < dir.length () - 1)
1048  {
1049  if (dir[pos+1] == '@')
1050  {
1052 
1053  dir = dir.substr (0, pos);
1054  }
1055  }
1056  }
1057 
1059  const QString& condition)
1060  {
1061  bp_info info (_file_name, line, condition);
1062 
1065  }
1066 
1068  {
1069  bp_info info (_file_name, line);
1070 
1073  }
1074 
1076  {
1077  if (ID != this)
1078  return;
1079 
1080  int editor_linenr, cur;
1081  _edit_area->getCursorPosition (&editor_linenr, &cur);
1082 
1083  if (_edit_area->markersAtLine (editor_linenr) & (1 << marker::breakpoint))
1085  else
1086  {
1087  if (unchanged_or_saved ())
1088  handle_request_add_breakpoint (editor_linenr + 1, "");
1089  }
1090  }
1091 
1092  // Move the text cursor to the closest breakpoint (conditional or unconditional)
1093  // after the current line.
1095  {
1096  if (ID != this)
1097  return;
1098 
1099  int line, cur;
1100  _edit_area->getCursorPosition (&line, &cur);
1101 
1102  line++; // Find breakpoint strictly after the current line.
1103 
1104  int nextline = _edit_area->markerFindNext (line, (1 << marker::breakpoint));
1105  int nextcond = _edit_area->markerFindNext (line, (1 << marker::cond_break));
1106 
1107  // Check if the next conditional breakpoint is before next unconditional one.
1108  if (nextcond != -1 && (nextcond < nextline || nextline == -1))
1109  nextline = nextcond;
1110 
1111  _edit_area->setCursorPosition (nextline, 0);
1112  }
1113 
1114  // Move the text cursor to the closest breakpoint (conditional or unconditional)
1115  // before the current line.
1117  {
1118  if (ID != this)
1119  return;
1120 
1121  int line, cur, prevline, prevcond;
1122  _edit_area->getCursorPosition (&line, &cur);
1123 
1124  line--; // Find breakpoint strictly before the current line.
1125 
1126  prevline = _edit_area->markerFindPrevious (line, (1 << marker::breakpoint));
1127  prevcond = _edit_area->markerFindPrevious (line, (1 << marker::cond_break));
1128 
1129  // Check if the prev conditional breakpoint is closer than the unconditional.
1130  if (prevcond != -1 && prevcond > prevline)
1131  prevline = prevcond;
1132 
1133  _edit_area->setCursorPosition (prevline, 0);
1134  }
1135 
1137  {
1138  if (ID != this)
1139  return;
1140 
1141  bp_info info (_file_name);
1142 
1145  }
1146 
1147  void file_editor_tab::scintilla_command (const QWidget *ID, unsigned int sci_msg)
1148  {
1149  if (ID != this)
1150  return;
1151 
1152  _edit_area->SendScintilla (sci_msg);
1153  }
1154 
1155  void file_editor_tab::comment_selected_text (const QWidget *ID, bool input_str)
1156  {
1157  if (ID != this)
1158  return;
1159 
1160  do_comment_selected_text (true, input_str);
1161  }
1162 
1164  {
1165  if (ID != this)
1166  return;
1167 
1168  do_comment_selected_text (false);
1169  }
1170 
1172  {
1173  if (ID != this)
1174  return;
1175 
1176  do_indent_selected_text (true);
1177  }
1178 
1180  {
1181  if (ID != this)
1182  return;
1183 
1184  do_indent_selected_text (false);
1185  }
1186 
1188  {
1189  if (ID != this)
1190  return;
1191 
1193  }
1194 
1196  QsciScintilla::EolMode eol_mode)
1197  {
1198  if (ID != this)
1199  return;
1200 
1201  _edit_area->convertEols (eol_mode);
1202  _edit_area->setEolMode (eol_mode);
1204  }
1205 
1207  {
1208  if (ID != this)
1209  return;
1210 
1211  _edit_area->zoomIn (1);
1212  auto_margin_width ();
1213  }
1214 
1216  {
1217  if (ID != this)
1218  return;
1219 
1220  _edit_area->zoomOut (1);
1221  auto_margin_width ();
1222  }
1223 
1225  {
1226  if (ID != this)
1227  return;
1228 
1229  _edit_area->zoomTo (0);
1230  auto_margin_width ();
1231  }
1232 
1234  {
1235  // Find dialog is going to hide. Save location of window for
1236  // when it is reshown.
1237  _find_dialog_geometry = _find_dialog->geometry ();
1238  _find_dialog_is_visible = false;
1239  }
1240 
1241  void file_editor_tab::find (const QWidget *ID, QList<QAction *> fetab_actions)
1242  {
1243  if (ID != this)
1244  return;
1245 
1246  // The find_dialog feature doesn't need a slot for return info.
1247  // Rather than Qt::DeleteOnClose, let the find feature hang about
1248  // in case it contains useful information like previous searches
1249  // and so on. Perhaps one find dialog for the whole editor is
1250  // better, but individual find dialogs has the nice feature of
1251  // retaining position per file editor tabs, which can be undocked.
1252 
1253  if (! _find_dialog)
1254  {
1256  fetab_actions.mid (0,2),
1257  qobject_cast<QWidget *> (sender ()));
1258  connect (_find_dialog, SIGNAL (finished (int)),
1259  this, SLOT (handle_find_dialog_finished (int)));
1260 
1261  connect (this, SIGNAL (request_find_next ()),
1262  _find_dialog, SLOT (find_next ()));
1263 
1264  connect (this, SIGNAL (request_find_previous ()),
1265  _find_dialog, SLOT (find_prev ()));
1266 
1267  _find_dialog->setWindowModality (Qt::NonModal);
1268  _find_dialog_geometry = _find_dialog->geometry ();
1269  }
1270  else if (! _find_dialog->isVisible ())
1271  {
1272  _find_dialog->setGeometry (_find_dialog_geometry);
1273  QPoint p = _find_dialog->pos ();
1274  _find_dialog->move (p.x ()+10, p.y ()+10);
1275  }
1276 
1277  _find_dialog->show ();
1278  _find_dialog_is_visible = true;
1279  _find_dialog->activateWindow ();
1281 
1282  }
1283 
1285  {
1286  if (ID == this)
1287  emit request_find_next ();
1288  }
1289 
1291  {
1292  if (ID == this)
1293  emit request_find_previous ();
1294  }
1295 
1297  {
1298  if (ID != this)
1299  return;
1300 
1301  if (m_bp_restore_count > 0)
1302  {
1303  // This goto-line request is invoked by restoring a breakpoint during
1304  // saving the file, thus, do not go to the related line
1306  return;
1307  }
1308 
1309  if (line <= 0) // ask for desired line
1310  {
1311  bool ok = false;
1312  int index;
1313  _edit_area->getCursorPosition (&line, &index);
1314  line = QInputDialog::getInt (_edit_area, tr ("Goto line"),
1315  tr ("Line number"), line+1, 1,
1316  _edit_area->lines (), 1, &ok);
1317  if (ok)
1318  _edit_area->setCursorPosition (line-1, 0);
1319  }
1320  else // go to given line without dialog
1321  _edit_area->setCursorPosition (line-1, 0);
1322 
1323  center_current_line (false); // only center line if at top or bottom
1324  }
1325 
1326  void file_editor_tab::move_match_brace (const QWidget *ID, bool select)
1327  {
1328  if (ID != this)
1329  return;
1330 
1331  if (select)
1332  _edit_area->selectToMatchingBrace ();
1333  else
1334  _edit_area->moveToMatchingBrace ();
1335  }
1336 
1338  {
1339  if (ID != this)
1340  return;
1341 
1342  QsciScintilla::AutoCompletionSource s = _edit_area->autoCompletionSource ();
1343  switch (s)
1344  {
1345  case QsciScintilla::AcsAll:
1346  _edit_area->autoCompleteFromAll ();
1347  break;
1348 
1349  case QsciScintilla::AcsAPIs:
1350  _edit_area->autoCompleteFromAPIs ();
1351  break;
1352 
1353  case QsciScintilla::AcsDocument:
1354  _edit_area->autoCompleteFromDocument ();
1355  break;
1356 
1357  case QsciScintilla::AcsNone:
1358  break;
1359  }
1360  }
1361 
1363  {
1364  // FIXME:
1365  _edit_area->beginUndoAction ();
1366 
1367  if (_edit_area->hasSelectedText ())
1368  {
1369  int lineFrom, lineTo, colFrom, colTo;
1370  _edit_area->getSelection (&lineFrom, &colFrom, &lineTo, &colTo);
1371 
1372  if (colTo == 0) // the beginning of last line is not selected
1373  lineTo--; // stop at line above
1374 
1375  for (int i = lineFrom; i <= lineTo; i++)
1376  {
1377  if (indent)
1378  _edit_area->indent (i);
1379  else
1380  _edit_area->unindent (i);
1381  }
1382  //set selection on (un)indented section
1383  _edit_area->setSelection (lineFrom, 0, lineTo,
1384  _edit_area->text (lineTo).length ()-1);
1385  }
1386  else
1387  {
1388  int cpline, col;
1389  _edit_area->getCursorPosition (&cpline, &col);
1390  if (indent)
1391  _edit_area->indent (cpline);
1392  else
1393  _edit_area->unindent (cpline);
1394  }
1395 
1396  _edit_area->endUndoAction ();
1397  }
1398 
1400  {
1401  _edit_area->beginUndoAction ();
1402 
1403  int lineFrom, lineTo;
1404 
1405  if (_edit_area->hasSelectedText ())
1406  {
1407  int colFrom, colTo;
1408  _edit_area->getSelection (&lineFrom, &colFrom, &lineTo, &colTo);
1409 
1410  if (colTo == 0) // the beginning of last line is not selected
1411  lineTo--; // stop at line above
1412  }
1413  else
1414  {
1415  int col;
1416  _edit_area->getCursorPosition (&lineFrom, &col);
1417 
1418  lineTo = lineFrom;
1419  }
1420 
1421  _edit_area->smart_indent_line_or_selected_text (lineFrom, lineTo);
1422 
1423  _edit_area->endUndoAction ();
1424  }
1425 
1426  void file_editor_tab::do_comment_selected_text (bool comment, bool input_str)
1427  {
1428  QRegExp rxc;
1429  QString ws = "^([ \\t]*)";
1430  QStringList comment_str = _edit_area->comment_string (comment);
1431  QString used_comment_str = comment_str.at (0);
1432 
1433  if (comment)
1434  {
1435  if (input_str)
1436  {
1437  bool ok;
1438  QSettings *settings = resource_manager::get_settings ();
1439 
1440  used_comment_str = QInputDialog::getText (
1441  this, tr ("Comment selected text"),
1442  tr ("Comment string to use:\n"), QLineEdit::Normal,
1443  settings->value (oct_last_comment_str, comment_str.at (0)).toString (),
1444  &ok);
1445 
1446  if ((! ok) || used_comment_str.isEmpty ())
1447  return; // No input, do nothing
1448  else
1449  settings->setValue (oct_last_comment_str, used_comment_str); // Store last
1450  }
1451  }
1452  else
1453  {
1454  // Uncommenting (several strings possible)
1455 
1456  // Sort strings according their length
1457  QStringList comment_str_sorted (comment_str.at (0));
1458  bool inserted;
1459 
1460  for (int i = 1; i < comment_str.length (); i++)
1461  {
1462  inserted = false;
1463  for (int j = 0; j < comment_str_sorted.length (); j++)
1464  {
1465  if (comment_str.at (i).length () > comment_str_sorted.at (j).length ())
1466  {
1467  comment_str_sorted.insert (j, comment_str.at (i));
1468  inserted = true;
1469  break;
1470  }
1471  }
1472  if (! inserted)
1473  comment_str_sorted << comment_str.at (i);
1474  }
1475 
1476  // Create regular expression
1477  QString regexp;
1478  for (int i = 0; i < comment_str_sorted.length (); i++)
1479  {
1480  if (i > 0)
1481  regexp = regexp + QString ("|");
1482  regexp = regexp + comment_str_sorted.at (i);
1483  }
1484  rxc = QRegExp (ws + "(" + regexp + ")");
1485  }
1486 
1487  // Do the commenting/uncommenting
1488  int len = 0, lenc = 0;
1489  _edit_area->beginUndoAction ();
1490 
1491  if (_edit_area->hasSelectedText ())
1492  {
1493  int lineFrom, lineTo, colFrom, colTo;
1494  int change_col_from = 1;
1495  int change_col_to = 1;
1496  bool removed;
1497 
1498  _edit_area->getSelection (&lineFrom, &colFrom, &lineTo, &colTo);
1499 
1500  if (colTo == 0) // the beginning of last line is not selected
1501  lineTo--; // stop at line above
1502 
1503  for (int i = lineFrom; i <= lineTo; i++)
1504  {
1505  if (comment)
1506  {
1507  _edit_area->insertAt (used_comment_str, i, 0);
1508  }
1509  else
1510  {
1511  QString line (_edit_area->text (i));
1512  if ((removed = line.contains (rxc)))
1513  {
1514  len = rxc.matchedLength (); // complete length
1515  QString matched_text = rxc.capturedTexts ().at (0);
1516  lenc = matched_text.remove (QRegExp (ws)).length (); // only comment string
1517  _edit_area->setSelection (i, len-lenc, i, len);
1518  _edit_area->removeSelectedText ();
1519  }
1520 
1521  // handle case, where the selection remains unchanged
1522  if (i == lineFrom && (colFrom < len-lenc || ! removed))
1523  change_col_from = 0; // do not change start of selection
1524  if (i == lineTo && (colTo < len-lenc || ! removed))
1525  change_col_to = 0; // do not change end of selection
1526  }
1527  }
1528 
1529  // update the selection area
1530  if (comment)
1531  {
1532  colFrom = colFrom + lenc; // shift start position by comment length
1533  if (colTo > 0)
1534  colTo = colTo + lenc; // shift end position by comment length
1535  else
1536  lineTo++; // colTo == 0 , fully select previous line
1537  }
1538  else
1539  {
1540  if (colTo == 0)
1541  lineTo++; // colTo == 0 , fully select previous line
1542  colFrom = colFrom - change_col_from*lenc;
1543  colTo = colTo - change_col_to*lenc;
1544  }
1545 
1546  // set updated selection area
1547  _edit_area->setSelection (lineFrom, colFrom, lineTo, colTo);
1548  }
1549  else
1550  {
1551  int cpline, col;
1552  _edit_area->getCursorPosition (&cpline, &col);
1553  if (comment)
1554  _edit_area->insertAt (used_comment_str, cpline, 0);
1555  else
1556  {
1557  QString line (_edit_area->text (cpline));
1558  if (line.contains (rxc))
1559  {
1560  len = rxc.matchedLength (); // complete length
1561  QString matched_text = rxc.capturedTexts ().at (0);
1562  lenc = matched_text.remove (QRegExp (ws)).length (); // only comment string
1563  _edit_area->setSelection (cpline, len-lenc, cpline, len);
1564  _edit_area->removeSelectedText ();
1565  }
1566  }
1567  }
1568  _edit_area->endUndoAction ();
1569  }
1570 
1572  {
1573  QString title ("");
1574  QString tooltip ("");
1575 
1576  if (! valid_file_name ())
1577  title = tr ("<unnamed>");
1578  else
1579  {
1580  if (_long_title)
1581  title = _file_name;
1582  else
1583  {
1584  QFileInfo file (_file_name);
1585  title = file.fileName ();
1586  tooltip = _file_name;
1587  }
1588  }
1589 
1590  if (modified)
1591  emit file_name_changed (title.prepend ("* "), tooltip);
1592  else
1593  emit file_name_changed (title, tooltip);
1594  }
1595 
1597  {
1598  _copy_available = enableCopy;
1600  }
1601 
1602  // show_dialog: shows a modal or non modal dialog depending on input arg
1603  void file_editor_tab::show_dialog (QDialog *dlg, bool modal)
1604  {
1605  dlg->setAttribute (Qt::WA_DeleteOnClose);
1606  if (modal)
1607  dlg->exec ();
1608  else
1609  {
1610  dlg->setWindowModality (Qt::NonModal);
1611  dlg->show ();
1612  }
1613  }
1614 
1616  {
1617  int decision = QMessageBox::Yes;
1618  if (_edit_area->isModified ())
1619  {
1620  // File is modified but not saved, ask user what to do. The file
1621  // editor tab can't be made parent because it may be deleted depending
1622  // upon the response. Instead, change the _edit_area to read only.
1623  QMessageBox::StandardButtons buttons = QMessageBox::Save |
1624  QMessageBox::Discard |
1625  QMessageBox::Cancel;
1626 
1627  // For now, just a warning message about closing a tab that has been
1628  // modified seems sufficient. Exit-condition-specific messages could
1629  // be achieved by making 'available_actions' a function input string.
1630  QString available_actions =
1631  tr ("Do you want to cancel closing, save or discard the changes?");
1632 
1633  QString file;
1634  if (valid_file_name ())
1635  file = _file_name;
1636  else
1637  file = tr ("<unnamed>");
1638 
1639  QMessageBox *msgBox
1640  = new QMessageBox (QMessageBox::Warning, tr ("Octave Editor"),
1641  tr ("The file\n\n"
1642  " %1\n\n"
1643  "is about to be closed but has been modified. "
1644  "%2").
1645  arg (file). arg (available_actions),
1646  buttons, qobject_cast<QWidget *> (parent ()));
1647 
1648  msgBox->setDefaultButton (QMessageBox::Save);
1649  _edit_area->setReadOnly (true);
1650  connect (msgBox, SIGNAL (finished (int)),
1651  this, SLOT (handle_file_modified_answer (int)));
1652 
1653  show_dialog (msgBox, true);
1654 
1655  if (_cancelled)
1656  return QMessageBox::Cancel;
1657  else
1658  return decision;
1659  }
1660  else
1661  {
1662  // Nothing was modified. Leave tab present in case user
1663  // decides to cancel some point further along.
1664  }
1665 
1666  return decision;
1667  }
1668 
1670  {
1671  if (decision == QMessageBox::Save)
1672  {
1673  // Save file, but do not remove from editor.
1674  save_file (_file_name, false, false);
1675  }
1676  else if (decision == QMessageBox::Discard)
1677  {
1678  // User doesn't want to save, leave tab and remove subsequently.
1679  }
1680  else
1681  {
1682  // User canceled, allow editing again.
1683  _edit_area->setReadOnly (false);
1684  _cancelled = true;
1685  }
1686  }
1687 
1688  void file_editor_tab::set_modified (bool modified)
1689  {
1690  _edit_area->setModified (modified);
1691  }
1692 
1694  {
1695  // reset the possibly still existing read only state
1696  _edit_area->setReadOnly (false);
1697 
1698  // if we are in this slot and the list of breakpoint is not empty,
1699  // then this tab was saved during an exit of the applications (not
1700  // restoring the breakpoints and not emptying the list) and the user
1701  // canceled this closing late on.
1703  }
1704 
1706  {
1707  if (! _bp_lines.isEmpty ())
1708  {
1709  // At least one breakpoint is present.
1710  // Get rid of breakpoints at old (now possibly invalid) linenumbers
1711  remove_all_breakpoints (this);
1712 
1713  // and set breakpoints at the new linenumbers
1714  m_bp_restore_count = _bp_lines.length ();
1715  for (int i = 0; i < _bp_lines.length (); i++)
1717  _bp_conditions.value (i));
1718 
1719  // Keep the list of breakpoints empty, except after explicit requests.
1720  _bp_lines.clear ();
1721  _bp_conditions.clear ();
1722  }
1723  }
1724 
1725  QString file_editor_tab::load_file (const QString& fileName)
1726  {
1727  // get the absolute path
1728  QFileInfo file_info = QFileInfo (fileName);
1729  QString file_to_load;
1730  if (file_info.exists ())
1731  file_to_load = file_info.canonicalFilePath ();
1732  else
1733  file_to_load = fileName;
1734  QFile file (file_to_load);
1735  if (! file.open (QFile::ReadOnly))
1736  return file.errorString ();
1737 
1738  // read the file
1739  QTextStream in (&file);
1740  // set the desired codec
1741  QTextCodec *codec = QTextCodec::codecForName (_encoding.toLatin1 ());
1742  in.setCodec (codec);
1743 
1744  QApplication::setOverrideCursor (Qt::WaitCursor);
1745  _edit_area->setText (in.readAll ());
1746  _edit_area->setEolMode (detect_eol_mode ());
1747  QApplication::restoreOverrideCursor ();
1748 
1749  _copy_available = false; // no selection yet available
1750  set_file_name (file_to_load);
1751  update_window_title (false); // window title (no modification)
1752  _edit_area->setModified (false); // loaded file is not modified yet
1753 
1755 
1756  // FIXME: (BREAKPOINTS) At this point it would be nice to put any set
1757  // breakpoints on the margin. In order to do this, somehow the
1758  // "dbstatus" command needs to be accessed. All it would require is a
1759  // routine that does "res = feval ("dbstatus") and signals that result
1760  // to some slot.
1761  //
1762  // See patch #8016 for a general way to get Octave results from
1763  // commands processed in the background.
1764 
1765  /*
1766  connect (octave_link, SIGNAL (fileSelected (QObject *, const QString&, const octave_value_list&)),
1767  this, SLOT (handle_feval_result (QObject *, const QString&, const octave_value_list&)));
1768  connect (this, SIGNAL (evaluate_octave_command (const QString&)),
1769  octave_link, SLOT (queue_octave_command (const QString&)));
1770 
1771  emit evaluate_octave_command ("dbstatus");
1772  */
1773 
1774  return QString ();
1775  }
1776 
1777  QsciScintilla::EolMode file_editor_tab::detect_eol_mode (void)
1778  {
1779  QByteArray text = _edit_area->text ().toLatin1 ();
1780 
1781  QByteArray eol_lf = QByteArray (1,0x0a);
1782  QByteArray eol_cr = QByteArray (1,0x0d);
1783  QByteArray eol_crlf = eol_cr;
1784  eol_crlf.append (eol_lf);
1785 
1786  int count_crlf = text.count (eol_crlf);
1787  int count_lf = text.count (eol_lf) - count_crlf; // isolated lf
1788  int count_cr = text.count (eol_cr) - count_crlf; // isolated cr;
1789 
1790  // get default from OS or from settings
1791 #if defined (Q_OS_WIN32)
1792  int os_eol_mode = QsciScintilla::EolWindows;
1793 #elif defined (Q_OS_MAC)
1794  int os_eol_mode = QsciScintilla::EolMac;
1795 #else
1796  int os_eol_mode = QsciScintilla::EolUnix;
1797 #endif
1798  QSettings *settings = resource_manager::get_settings ();
1799  QsciScintilla::EolMode eol_mode = static_cast<QsciScintilla::EolMode> (
1800  settings->value ("editor/default_eol_mode",os_eol_mode).toInt ());
1801 
1802  int count_max = 0;
1803 
1804  if (count_crlf > count_max)
1805  {
1806  eol_mode = QsciScintilla::EolWindows;
1807  count_max = count_crlf;
1808  }
1809  if (count_lf > count_max)
1810  {
1811  eol_mode = QsciScintilla::EolUnix;
1812  count_max = count_lf;
1813  }
1814  if (count_cr > count_max)
1815  {
1816  eol_mode = QsciScintilla::EolMac;
1817  count_max = count_cr;
1818  }
1819 
1820  return eol_mode;
1821  }
1822 
1824  {
1825  switch (_edit_area->eolMode ())
1826  {
1827  case QsciScintilla::EolWindows:
1828  _eol_indicator->setText ("CRLF");
1829  break;
1830  case QsciScintilla::EolMac:
1831  _eol_indicator->setText ("CR");
1832  break;
1833  case QsciScintilla::EolUnix:
1834  _eol_indicator->setText ("LF");
1835  break;
1836  }
1837  }
1838 
1839  // FIXME: See patch #8016 for a general way to get Octave results from
1840  // commands processed in the background, e.g., dbstatus.
1842  QString& command,
1844  {
1845  // Check if this object initiated the command.
1846  if (requester == this)
1847  {
1848  if (command == "dbstatus")
1849  {
1850  // Should be installing breakpoints in this file
1851  /*
1852  octave:1> result = dbstatus
1853  result =
1854 
1855  0x1 struct array containing the fields:
1856 
1857  name
1858  file
1859  line
1860  */
1861  // Check for results that match "file".
1862  }
1863  }
1864  }
1865 
1866  void file_editor_tab::new_file (const QString& commands)
1867  {
1868  update_window_title (false); // window title (no modification)
1869 
1870  QSettings *settings = resource_manager::get_settings ();
1871 
1872  // set the eol mode from the settings or depending on the OS if the entry is
1873  // missing in the settings
1874 #if defined (Q_OS_WIN32)
1875  int eol_mode = QsciScintilla::EolWindows;
1876 #elif defined (Q_OS_MAC)
1877  int eol_mode = QsciScintilla::EolMac;
1878 #else
1879  int eol_mode = QsciScintilla::EolUnix;
1880 #endif
1881  _edit_area->setEolMode (
1882  static_cast<QsciScintilla::EolMode> (
1883  settings->value ("editor/default_eol_mode",eol_mode).toInt ()));
1884 
1886 
1887  update_lexer ();
1888 
1889  _edit_area->setText (commands);
1890  _edit_area->setModified (false); // new file is not modified yet
1891  }
1892 
1893  // Force reloading of a file after it is saved.
1894  // This is needed to get the right line numbers for breakpoints (bug #46632).
1895  bool file_editor_tab::exit_debug_and_clear (const QString& full_name_q,
1896  const QString& base_name_q)
1897  {
1898  symbol_table& symtab
1899  = __get_symbol_table__ ("file_editor_tab::exit_debug_and_clear");
1900 
1901  std::string base_name = base_name_q.toStdString ();
1902  octave_value sym;
1903  try
1904  {
1905  sym = symtab.find (base_name);
1906  }
1907  catch (const execution_exception& e)
1908  {
1909  // Ignore syntax error.
1910  // It was in the old file on disk; the user may have fixed it already.
1911  }
1912 
1913  // Return early if this file is not loaded in the symbol table
1914  if (! sym.is_defined () || ! sym.is_user_code ())
1915  return true;
1916 
1918 
1919  std::string full_name = full_name_q.toStdString ();
1920  if (sys::canonicalize_file_name (full_name.c_str ())
1921  != sys::canonicalize_file_name (fcn->fcn_file_name ().c_str ()))
1922  return true;
1923 
1924  // If this file is loaded, check that we aren't currently running it
1925  bool retval = true;
1926  octave_idx_type curr_frame = -1;
1927  size_t nskip = 0;
1928  call_stack& cs
1929  = __get_call_stack__ ("file_editor_tab::exit_debug_and_clear");
1930  octave_map stk = cs.backtrace (nskip, curr_frame, false);
1931  Cell names = stk.contents ("name");
1932  for (octave_idx_type i = names.numel () - 1; i >= 0; i--)
1933  {
1934  if (names(i).string_value () == base_name)
1935  {
1936  int ans = QMessageBox::question (nullptr, tr ("Debug or Save"),
1937  tr ("This file is currently being executed.\n"
1938  "Quit debugging and save?"),
1939  QMessageBox::Save | QMessageBox::Cancel);
1940 
1941  if (ans == QMessageBox::Save)
1942  {
1943  emit execute_command_in_terminal_signal ("dbquit");
1944  // Wait until dbquit has actually occurred
1945  while (names.numel () > i)
1946  {
1947  octave_sleep (0.01);
1948  stk = cs.backtrace (nskip, curr_frame, false);
1949  names = stk.contents ("name");
1950  }
1951  }
1952  else
1953  retval = false;
1954  break;
1955  }
1956  }
1957 
1958  // If we aren't currently running it, or have quit above, force a reload.
1959  if (retval == true)
1960  symtab.clear_user_function (base_name);
1961 
1962  return retval;
1963  }
1964 
1965  void file_editor_tab::save_file (const QString& saveFileName,
1966  bool remove_on_success,
1967  bool restore_breakpoints)
1968  {
1969  // If it is a new file with no name, signal that saveFileAs
1970  // should be performed.
1971  if (! valid_file_name (saveFileName))
1972  {
1973  save_file_as (remove_on_success);
1974  return;
1975  }
1976 
1977  // Get a list of breakpoint line numbers, before exit_debug_and_clear().
1979 
1980  // get the absolute path (if existing)
1981  QFileInfo file_info = QFileInfo (saveFileName);
1982  QString file_to_save;
1983  if (file_info.exists ())
1984  {
1985  file_to_save = file_info.canonicalFilePath ();
1986  // Force reparse of this function next time it is used (bug #46632)
1987  if ((Fisdebugmode ())(0).is_true ()
1988  && ! exit_debug_and_clear (file_to_save, file_info.baseName ()))
1989  return;
1990  }
1991  else
1992  file_to_save = saveFileName;
1993  QFile file (file_to_save);
1994 
1995  // stop watching file
1996  QStringList trackedFiles = _file_system_watcher.files ();
1997  if (trackedFiles.contains (file_to_save))
1998  _file_system_watcher.removePath (file_to_save);
1999 
2000  // open the file for writing
2001  if (! file.open (QIODevice::WriteOnly))
2002  {
2003  // Unsuccessful, begin watching file again if it was being
2004  // watched previously.
2005  if (trackedFiles.contains (file_to_save))
2006  _file_system_watcher.addPath (file_to_save);
2007 
2008  // Create a NonModal message about error.
2009  QMessageBox *msgBox
2010  = new QMessageBox (QMessageBox::Critical,
2011  tr ("Octave Editor"),
2012  tr ("Could not open file %1 for write:\n%2.").
2013  arg (file_to_save).arg (file.errorString ()),
2014  QMessageBox::Ok, nullptr);
2015  show_dialog (msgBox, false);
2016 
2017  return;
2018  }
2019 
2020  // save the contents into the file
2021 
2022  _encoding = _new_encoding; // consider a possible new encoding
2023 
2024  // set the desired codec (if suitable for contents)
2025  QTextCodec *codec = QTextCodec::codecForName (_encoding.toLatin1 ());
2026 
2027  if (check_valid_codec (codec))
2028  {
2029  save_file_as (remove_on_success);
2030  return;
2031  }
2032 
2033  // write the file
2034  QTextStream out (&file);
2035  out.setCodec (codec);
2036 
2037  QApplication::setOverrideCursor (Qt::WaitCursor);
2038  out << _edit_area->text ();
2039  out.flush ();
2040  QApplication::restoreOverrideCursor ();
2041  file.flush ();
2042  file.close ();
2043 
2044  // file exists now
2045  file_info = QFileInfo (file);
2046  file_to_save = file_info.canonicalFilePath ();
2047 
2048  // save filename after closing file as set_file_name starts watching again
2049  set_file_name (file_to_save); // make absolute
2050 
2051  // set the window title to actual filename (not modified)
2052  update_window_title (false);
2053 
2054  // files is save -> not modified, update encoding in statusbar
2055  _edit_area->setModified (false);
2056  _enc_indicator->setText (_encoding);
2057 
2058  if (remove_on_success)
2059  {
2060  emit tab_remove_request ();
2061  return; // Don't touch member variables after removal
2062  }
2063 
2064  // Attempt to restore the breakpoints if that is desired.
2065  // This is only allowed if the tab is not closing since changing
2066  // breakpoints would reopen the tab in this case.
2067  if (restore_breakpoints)
2069  }
2070 
2071  void file_editor_tab::save_file_as (bool remove_on_success)
2072  {
2073  // Simply put up the file chooser dialog box with a slot connection
2074  // then return control to the system waiting for a file selection.
2075 
2076  // reset _new_encoding
2078 
2079  // If the tab is removed in response to a QFileDialog signal, the tab
2080  // can't be a parent.
2081  QFileDialog *fileDialog;
2082  if (remove_on_success)
2083  {
2084  // If tab is closed, "this" cannot be parent in which case modality
2085  // has no effect. Disable editing instead.
2086  _edit_area->setReadOnly (true);
2087  fileDialog = new QFileDialog ();
2088  }
2089  else
2090  fileDialog = new QFileDialog (this);
2091 
2092  // Giving trouble under KDE (problem is related to Qt signal handling on unix,
2093  // see https://bugs.kde.org/show_bug.cgi?id=260719 ,
2094  // it had/has no effect on Windows, though)
2095  fileDialog->setOption (QFileDialog::DontUseNativeDialog, true);
2096 
2097  // define a new grid layout with the extra elements
2098  QGridLayout *extra = new QGridLayout (fileDialog);
2099  QFrame *separator = new QFrame (fileDialog);
2100  separator->setFrameShape (QFrame::HLine); // horizontal line as separator
2101  separator->setFrameStyle (QFrame::Sunken);
2102 
2103  // combo box for choosing new line ending chars
2104  QLabel *label_eol = new QLabel (tr ("Line Endings:"));
2105  QComboBox *combo_eol = new QComboBox ();
2106  combo_eol->addItem ("Windows (CRLF)"); // ensure the same order as in
2107  combo_eol->addItem ("Mac (CR)"); // the settings dialog
2108  combo_eol->addItem ("Unix (LF)");
2109  _save_as_desired_eol = _edit_area->eolMode (); // init with current eol
2110  combo_eol->setCurrentIndex (_save_as_desired_eol);
2111 
2112  // combo box for encoding
2113  QLabel *label_enc = new QLabel (tr ("File Encoding:"));
2114  QComboBox *combo_enc = new QComboBox ();
2116 
2117  // track changes in the combo boxes
2118  connect (combo_eol, SIGNAL (currentIndexChanged (int)),
2119  this, SLOT (handle_combo_eol_current_index (int)));
2120  connect (combo_enc, SIGNAL (currentIndexChanged (QString)),
2121  this, SLOT (handle_combo_enc_current_index (QString)));
2122 
2123  // build the extra grid layout
2124  extra->addWidget (separator,0,0,1,6);
2125  extra->addWidget (label_eol,1,0);
2126  extra->addWidget (combo_eol,1,1);
2127  extra->addItem (new QSpacerItem (1,20,QSizePolicy::Fixed,
2128  QSizePolicy::Fixed), 1,2);
2129  extra->addWidget (label_enc,1,3);
2130  extra->addWidget (combo_enc,1,4);
2131  extra->addItem (new QSpacerItem (1,20,QSizePolicy::Expanding,
2132  QSizePolicy::Fixed), 1,5);
2133 
2134  // and add the extra grid layout to the dialog's layout
2135  QGridLayout *dialog_layout = dynamic_cast<QGridLayout *> (fileDialog->layout ());
2136  dialog_layout->addLayout (extra,dialog_layout->rowCount (),0,
2137  1,dialog_layout->columnCount ());
2138 
2139  // add the possible filters and the default suffix
2140  QStringList filters;
2141  filters << tr ("Octave Files (*.m)")
2142  << tr ("All Files (*)");
2143  fileDialog->setNameFilters (filters);
2144  fileDialog->setDefaultSuffix ("m");
2145 
2146  if (valid_file_name ())
2147  {
2148  fileDialog->selectFile (_file_name);
2149  QFileInfo file_info (_file_name);
2150  if (file_info.suffix () != "m")
2151  {
2152  // it is not an octave file
2153  fileDialog->selectNameFilter (filters.at (1)); // "All Files"
2154  fileDialog->setDefaultSuffix (""); // no default suffix
2155  }
2156  }
2157  else
2158  {
2159  fileDialog->selectFile ("");
2160  fileDialog->setDirectory (_ced);
2161 
2162  // propose a name corresponding to the function name
2163  QString fname = get_function_name ();
2164  if (! fname.isEmpty ())
2165  fileDialog->selectFile (fname + ".m");
2166  }
2167 
2168  fileDialog->setAcceptMode (QFileDialog::AcceptSave);
2169  fileDialog->setViewMode (QFileDialog::Detail);
2170 
2171  connect (fileDialog, SIGNAL (filterSelected (const QString&)),
2172  this, SLOT (handle_save_as_filter_selected (const QString&)));
2173 
2174  if (remove_on_success)
2175  {
2176  connect (fileDialog, SIGNAL (fileSelected (const QString&)),
2177  this, SLOT (handle_save_file_as_answer_close (const QString&)));
2178 
2179  connect (fileDialog, SIGNAL (rejected ()),
2180  this, SLOT (handle_save_file_as_answer_cancel ()));
2181  }
2182  else
2183  {
2184  connect (fileDialog, SIGNAL (fileSelected (const QString&)),
2185  this, SLOT (handle_save_file_as_answer (const QString&)));
2186  }
2187 
2188  show_dialog (fileDialog, ! valid_file_name ());
2189  }
2190 
2192  {
2193  _save_as_desired_eol = static_cast<QsciScintilla::EolMode> (index);
2194  }
2195 
2197  {
2198  _new_encoding = text;
2199  }
2200 
2202  {
2203  QFileDialog *file_dialog = qobject_cast<QFileDialog *> (sender ());
2204 
2205  QRegExp rx ("\\*\\.([^ ^\\)]*)[ \\)]"); // regexp for suffix in filter
2206  int index = rx.indexIn (filter,0); // get first suffix in filter
2207 
2208  if (index > -1)
2209  file_dialog->setDefaultSuffix (rx.cap (1)); // found a suffix, set default
2210  else
2211  file_dialog->setDefaultSuffix (""); // not found, clear default
2212  }
2213 
2215  {
2216  QFileInfo file = QFileInfo (file_name);
2217  QString base_name = file.baseName ();
2218 
2219  if ((file.suffix () == "m")
2220  && (! valid_identifier (base_name.toStdString ())))
2221  {
2222  int ans = QMessageBox::question (nullptr, tr ("Octave Editor"),
2223  tr ("\"%1\"\n"
2224  "is not a valid identifier.\n\n"
2225  "If you keep this filename, you will not be able to\n"
2226  "call your script using its name as an Octave command.\n\n"
2227  "Do you want to choose another name?").arg (base_name),
2228  QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
2229 
2230  if (ans == QMessageBox::Yes)
2231  return true;
2232  }
2233 
2234  return false;
2235  }
2236 
2237  bool file_editor_tab::check_valid_codec (QTextCodec *codec)
2238  {
2239  if (! codec)
2240  {
2241  QMessageBox::critical (nullptr,
2242  tr ("Octave Editor"),
2243  tr ("The current encoding %1\n"
2244  "can not be applied.\n\n"
2245  "Please select another one or cancel saving!").arg (_encoding));
2246 
2247  return true;
2248  }
2249 
2250  if (! codec->canEncode (_edit_area->text ()))
2251  {
2252  int ans = QMessageBox::warning (nullptr,
2253  tr ("Octave Editor"),
2254  tr ("The current editor contents can not be encoded\n"
2255  "with the selected encoding %1.\n"
2256  "Using it will result in data loss!\n\n"
2257  "Do you want to choose another encoding?").arg (_encoding),
2258  QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
2259 
2260  if (ans == QMessageBox::Yes)
2261  return true;
2262  }
2263 
2264  return false;
2265  }
2266 
2267  void file_editor_tab::handle_save_file_as_answer (const QString& saveFileName)
2268  {
2269  if (_save_as_desired_eol != _edit_area->eolMode ())
2271 
2272  if (saveFileName == _file_name)
2273  {
2274  save_file (saveFileName);
2275  }
2276  else
2277  {
2278  // Have editor check for conflict, do not delete tab after save.
2279  if (check_valid_identifier (saveFileName))
2280  save_file_as (false);
2281  else
2282  emit editor_check_conflict_save (saveFileName, false);
2283  }
2284  }
2285 
2286  void file_editor_tab::handle_save_file_as_answer_close (const QString& saveFileName)
2287  {
2288  if (_save_as_desired_eol != _edit_area->eolMode ())
2289  {
2290  _edit_area->setReadOnly (false); // was set to read-only in save_file_as
2292  _edit_area->setReadOnly (true); // restore read-only mode
2293  }
2294 
2295  // saveFileName == _file_name can not happen, because we only can get here
2296  // when we close a tab and _file_name is not a valid filename yet
2297 
2298  // Have editor check for conflict, delete tab after save.
2299  if (check_valid_identifier (saveFileName))
2300  save_file_as (true);
2301  else
2302  emit editor_check_conflict_save (saveFileName, true);
2303  }
2304 
2306  {
2307  // User canceled, allow editing again.
2308  _edit_area->setReadOnly (false);
2309  }
2310 
2311  void file_editor_tab::file_has_changed (const QString&, bool do_close)
2312  {
2313  // Prevent popping up multiple message boxes when the file has
2314  // been changed multiple times by temporarily removing from the
2315  // file watcher.
2316  QStringList trackedFiles = _file_system_watcher.files ();
2317  if (! trackedFiles.isEmpty ())
2318  _file_system_watcher.removePath (_file_name);
2319 
2320  if (QFile::exists (_file_name) && ! do_close)
2321  {
2322  // The file is modified
2324 
2326 
2327  else
2328  {
2329  // give editor and this tab the focus,
2330  // possibly making the editor visible if it is hidden
2331  emit set_focus_editor_signal (this);
2332  _edit_area->setFocus ();
2333 
2334  // Create a WindowModal message that blocks the edit area
2335  // by making _edit_area parent.
2336  QMessageBox *msgBox
2337  = new QMessageBox (QMessageBox::Warning,
2338  tr ("Octave Editor"),
2339  tr ("It seems that \'%1\' has been modified by another application. Do you want to reload it?").
2340  arg (_file_name),
2341  QMessageBox::Yes | QMessageBox::No, this);
2342 
2343  connect (msgBox, SIGNAL (finished (int)),
2344  this, SLOT (handle_file_reload_answer (int)));
2345 
2346  msgBox->setWindowModality (Qt::WindowModal);
2347  msgBox->setAttribute (Qt::WA_DeleteOnClose);
2348  msgBox->show ();
2349  }
2350  }
2351  else
2352  {
2353  // If desired and if file is not modified,
2354  // close the file without any user interaction
2355  if (do_close && ! _edit_area->isModified ())
2356  {
2357  handle_file_resave_answer (QMessageBox::Cancel);
2358  return;
2359  }
2360 
2361  // give editor and this tab the focus,
2362  // possibly making the editor visible if it is hidden
2363  emit set_focus_editor_signal (this);
2364  _edit_area->setFocus ();
2365 
2366  QString modified = "";
2367  if (_edit_area->isModified ())
2368  modified = tr ("\n\nWarning: The contents in the editor is modified!");
2369 
2370  // Create a WindowModal message. The file editor tab can't be made
2371  // parent because it may be deleted depending upon the response.
2372  // Instead, change the _edit_area to read only.
2373  QMessageBox *msgBox
2374  = new QMessageBox (QMessageBox::Warning, tr ("Octave Editor"),
2375  tr ("It seems that the file\n"
2376  "%1\n"
2377  "has been deleted or renamed. Do you want to save it now?%2").
2378  arg (_file_name).arg (modified),
2379  QMessageBox::Save | QMessageBox::Close, nullptr);
2380 
2381  _edit_area->setReadOnly (true);
2382 
2383  connect (msgBox, SIGNAL (finished (int)),
2384  this, SLOT (handle_file_resave_answer (int)));
2385 
2386  msgBox->setWindowModality (Qt::WindowModal);
2387  msgBox->setAttribute (Qt::WA_DeleteOnClose);
2388  msgBox->show ();
2389  }
2390  }
2391 
2392  void file_editor_tab::notice_settings (const QSettings *settings, bool init)
2393  {
2394  // QSettings pointer is checked before emitting.
2395 
2396  if (! init)
2398 
2399  // code folding
2400  if (settings->value ("editor/code_folding",true).toBool ())
2401  {
2402  _edit_area->setMarginType (3, QsciScintilla::SymbolMargin);
2403  _edit_area->setFolding (QsciScintilla::BoxedTreeFoldStyle , 3);
2404  }
2405  else
2406  {
2407  _edit_area->setFolding (QsciScintilla::NoFoldStyle, 3);
2408  }
2409 
2410  // status bar
2411  if (settings->value ("editor/show_edit_status_bar",true).toBool ())
2412  _status_bar->show ();
2413  else
2414  _status_bar->hide ();
2415 
2416  //highlight current line color
2417  QVariant default_var = QColor (240, 240, 240);
2418  QColor setting_color = settings->value ("editor/highlight_current_line_color",
2419  default_var).value<QColor> ();
2420  _edit_area->setCaretLineBackgroundColor (setting_color);
2421  _edit_area->setCaretLineVisible
2422  (settings->value ("editor/highlightCurrentLine", true).toBool ());
2423 
2424  bool match_keywords = settings->value
2425  ("editor/codeCompletion_keywords",true).toBool ();
2426  bool match_document = settings->value
2427  ("editor/codeCompletion_document",true).toBool ();
2428 
2429  QsciScintilla::AutoCompletionSource source = QsciScintilla::AcsNone;
2430  if (match_keywords)
2431  if (match_document)
2432  source = QsciScintilla::AcsAll;
2433  else
2434  source = QsciScintilla::AcsAPIs;
2435  else if (match_document)
2436  source = QsciScintilla::AcsDocument;
2437  _edit_area->setAutoCompletionSource (source);
2438 
2439  _edit_area->setAutoCompletionReplaceWord
2440  (settings->value ("editor/codeCompletion_replace",false).toBool ());
2441  _edit_area->setAutoCompletionCaseSensitivity
2442  (settings->value ("editor/codeCompletion_case",true).toBool ());
2443 
2444  if (settings->value ("editor/codeCompletion", true).toBool ())
2445  _edit_area->setAutoCompletionThreshold
2446  (settings->value ("editor/codeCompletion_threshold",2).toInt ());
2447  else
2448  _edit_area->setAutoCompletionThreshold (-1);
2449 
2450  if (settings->value ("editor/show_white_space",false).toBool ())
2451  if (settings->value ("editor/show_white_space_indent",false).toBool ())
2452  _edit_area->setWhitespaceVisibility (QsciScintilla::WsVisibleAfterIndent);
2453  else
2454  _edit_area->setWhitespaceVisibility (QsciScintilla::WsVisible);
2455  else
2456  _edit_area->setWhitespaceVisibility (QsciScintilla::WsInvisible);
2457 
2458  _edit_area->setEolVisibility (
2459  settings->value ("editor/show_eol_chars",false).toBool ());
2460 
2461  if (settings->value ("editor/showLineNumbers", true).toBool ())
2462  {
2463  _edit_area->setMarginLineNumbers (2, true);
2464  auto_margin_width ();
2465  connect (_edit_area, SIGNAL (linesChanged ()),
2466  this, SLOT (auto_margin_width ()));
2467  }
2468  else
2469  {
2470  _edit_area->setMarginLineNumbers (2, false);
2471  disconnect (_edit_area, SIGNAL (linesChanged ()), nullptr, nullptr);
2472  }
2473 
2474  _smart_indent = settings->value ("editor/auto_indent",true).toBool ();
2475  _edit_area->setAutoIndent (_smart_indent);
2476  _edit_area->setTabIndents
2477  (settings->value ("editor/tab_indents_line",false).toBool ());
2478  _edit_area->setBackspaceUnindents
2479  (settings->value ("editor/backspace_unindents_line",false).toBool ());
2480  _edit_area->setIndentationGuides
2481  (settings->value ("editor/show_indent_guides",false).toBool ());
2482  _edit_area->setIndentationsUseTabs
2483  (settings->value ("editor/indent_uses_tabs",false).toBool ());
2484  _edit_area->setIndentationWidth
2485  (settings->value ("editor/indent_width",2).toInt ());
2486 
2487  _edit_area->setTabWidth
2488  (settings->value ("editor/tab_width",2).toInt ());
2489 
2490  _edit_area->SendScintilla (QsciScintillaBase::SCI_SETHSCROLLBAR,
2491  settings->value ("editor/show_hscroll_bar",true).toBool ());
2492  _edit_area->SendScintilla (QsciScintillaBase::SCI_SETSCROLLWIDTH,-1);
2493  _edit_area->SendScintilla (QsciScintillaBase::SCI_SETSCROLLWIDTHTRACKING,true);
2494 
2495  _long_title = settings->value ("editor/longWindowTitle", false).toBool ();
2496  update_window_title (_edit_area->isModified ());
2497 
2498  _auto_endif = settings->value ("editor/auto_endif",1).toInt ();
2499 
2500  // long line marker
2501  int line_length = settings->value ("editor/long_line_column",80).toInt ();
2502  _edit_area->setEdgeColumn (line_length);
2503 
2504  if (settings->value ("editor/long_line_marker",true).toBool ())
2505  {
2506  if (settings->value ("editor/long_line_marker_line",true).toBool ())
2507  _edit_area->setEdgeMode (QsciScintilla::EdgeLine);
2508  else
2509  {
2510  if (settings->value ("editor/long_line_marker_background",false)
2511  .toBool ())
2512  _edit_area->setEdgeMode (QsciScintilla::EdgeBackground);
2513  else
2514  _edit_area->setEdgeMode (QsciScintilla::EdgeLine);
2515  }
2516  }
2517  else
2518  _edit_area->setEdgeMode (QsciScintilla::EdgeNone);
2519 
2520  // line wrapping and breaking
2521  _edit_area->setWrapVisualFlags (QsciScintilla::WrapFlagByBorder);
2522  _edit_area->setWrapIndentMode (QsciScintilla::WrapIndentSame);
2523 
2524  if (settings->value ("editor/wrap_lines",false).toBool ())
2525  _edit_area->setWrapMode (QsciScintilla::WrapWord);
2526  else
2527  _edit_area->setWrapMode (QsciScintilla::WrapNone);
2528 
2529  if (settings->value ("editor/break_lines",false).toBool ())
2530  _line_break = line_length;
2531  else
2532  _line_break = 0;
2533 
2535  settings->value ("editor/break_lines_comments",false).toBool ();
2536 
2537  // highlight all occurrences of a word selected by a double click
2539  settings->value ("editor/highlight_all_occurrences", true).toBool ();
2540 
2541  // reload changed files
2543  settings->value ("editor/always_reload_changed_files",false).toBool ();
2544 
2545  // Set cursor blinking depending on the settings.
2546  // QScintilla ignores the application global settings, so some special
2547  // handling is required
2548  bool cursor_blinking;
2549 
2550  if (settings->contains ("cursor_blinking"))
2551  cursor_blinking = settings->value ("cursor_blinking",true).toBool ();
2552  else
2553  cursor_blinking = settings->value ("terminal/cursorBlinking",true).toBool ();
2554 
2555  if (cursor_blinking)
2556  _edit_area->SendScintilla (QsciScintillaBase::SCI_SETCARETPERIOD,500);
2557  else
2558  _edit_area->SendScintilla (QsciScintillaBase::SCI_SETCARETPERIOD,0);
2559 
2560  }
2561 
2562 
2564  {
2565  _edit_area->setMarginWidth (2, "1" + QString::number (_edit_area->lines ()));
2566  }
2567 
2568  // the following close request was changed from a signal slot into a
2569  // normal function because we need the return value from close whether
2570  // the tab really was closed (for canceling exiting octave).
2571  // When emitting a signal, only the return value from the last slot
2572  // goes back to the sender
2574  {
2575  return close ();
2576  }
2577 
2579  {
2580  if (ID != this)
2581  {
2582  // Widget may be going out of focus. If so, record location.
2583  if (_find_dialog)
2584  {
2585  if (_find_dialog->isVisible ())
2586  {
2587  _find_dialog_geometry = _find_dialog->geometry ();
2588  _find_dialog->hide ();
2589  }
2590  }
2591  return;
2592  }
2593 
2595  {
2596  _find_dialog->setGeometry (_find_dialog_geometry);
2597  QPoint p = _find_dialog->pos ();
2598  _find_dialog->move (p.x ()+10, p.y ()+10);
2599  _find_dialog->show ();
2600  }
2601 
2603  }
2604 
2606  {
2607  // A zero (null pointer) means that all file editor tabs
2608  // should respond, otherwise just the desired file editor tab.
2609  if (ID != this && ID != nullptr)
2610  return;
2611 
2612  // This list also includes windows with name ""
2614  }
2615 
2617  {
2618  if (decision == QMessageBox::Yes)
2619  {
2620  // reload: file is readded to the file watcher in set_file_name ()
2622  }
2623  else
2624  {
2625  // do not reload: readd to the file watche
2626  _file_system_watcher.addPath (_file_name);
2627  }
2628  }
2629 
2631  {
2632  // check decision of user in dialog
2633  if (decision == QMessageBox::Save)
2634  {
2635  save_file (_file_name); // readds file to watcher in set_file_name ()
2636  _edit_area->setReadOnly (false); // delete read only flag
2637  }
2638  else
2639  {
2640  // Definitely close the file.
2641  // Set modified to false to prevent the dialog box when the close event
2642  // is posted. If the user cancels the close in this dialog the tab is
2643  // left open with a non-existing file.
2644  _edit_area->setModified (false);
2645  close ();
2646  }
2647  }
2648 
2650  {
2651  if (ID != this || ID == nullptr)
2652  return;
2653 
2654  emit remove_all_positions (); // debugger_position, unsure_debugger_position
2655 
2656  if (line > 0)
2657  {
2658  marker *dp;
2659 
2660  if (_edit_area->isModified ())
2661  {
2662  // The best that can be done if the editor contents has been
2663  // modified is to see if there is a match with the original
2664  // line number of any existing breakpoints. We can put a normal
2665  // debugger pointer at that breakpoint position. Otherwise, it
2666  // isn't certain whether the original line number and current line
2667  // number match.
2668  int editor_linenr = -1;
2669  marker *dummy;
2670  emit find_translated_line_number (line, editor_linenr, dummy);
2671  if (editor_linenr != -1)
2672  {
2673  // Match with an existing breakpoint.
2674  dp = new marker (_edit_area, line,
2675  marker::debugger_position, editor_linenr);
2676  }
2677  else
2678  {
2679  int original_linenr = -1;
2680  editor_linenr = -1;
2681  emit find_linenr_just_before (line, original_linenr, editor_linenr);
2682  if (original_linenr >= 0)
2683  {
2684  // Make a guess by using an offset from the breakpoint.
2685  int linenr_guess = editor_linenr + line - original_linenr;
2686  dp = new marker (_edit_area, line,
2688  linenr_guess);
2689  }
2690  else
2691  {
2692  // Can't make a very good guess, so just use the debugger
2693  // line number.
2694  dp = new marker (_edit_area, line,
2696  }
2697  }
2698  }
2699  else
2701 
2702  connect (this, SIGNAL (remove_position_via_debugger_linenr (int)),
2703  dp, SLOT (handle_remove_via_original_linenr (int)));
2704  connect (this, SIGNAL (remove_all_positions (void)),
2705  dp, SLOT (handle_remove (void)));
2706 
2707  center_current_line (false);
2708  }
2709  }
2710 
2712  {
2713  if (ID != this || ID == nullptr)
2714  return;
2715 
2716  if (line > 0)
2718  }
2719 
2721  const QWidget *ID, int line,
2722  const QString& cond)
2723  {
2724  if (ID != this || ID == nullptr)
2725  return;
2726 
2727  if (line > 0)
2728  {
2729  if (insert)
2730  {
2731  int editor_linenr = -1;
2732  marker *bp = nullptr;
2733 
2734  // If comes back indicating a non-zero breakpoint marker,
2735  // reuse it if possible
2736  emit find_translated_line_number (line, editor_linenr, bp);
2737  if (bp != nullptr)
2738  {
2739  if ((cond == "") != (bp->get_cond () == ""))
2740  {
2741  // can only reuse conditional bp as conditional
2743  bp = nullptr;
2744  }
2745  else
2746  bp->set_cond (cond);
2747  }
2748 
2749  if (bp == nullptr)
2750  {
2751  bp = new marker (_edit_area, line,
2752  cond == "" ? marker::breakpoint
2753  : marker::cond_break, cond);
2754 
2755  connect (this, SIGNAL (remove_breakpoint_via_debugger_linenr
2756  (int)),
2757  bp, SLOT (handle_remove_via_original_linenr (int)));
2758  connect (this, SIGNAL (request_remove_breakpoint_via_editor_linenr
2759  (int)),
2760  bp, SLOT (handle_request_remove_via_editor_linenr
2761  (int)));
2762  connect (this, SIGNAL (remove_all_breakpoints (void)),
2763  bp, SLOT (handle_remove (void)));
2764  connect (this, SIGNAL (find_translated_line_number (int, int&,
2765  marker*&)),
2766  bp, SLOT (handle_find_translation (int, int&,
2767  marker*&)));
2768  connect (this, SIGNAL (find_linenr_just_before (int, int&, int&)),
2769  bp, SLOT (handle_find_just_before (int, int&, int&)));
2770  connect (this, SIGNAL (report_marker_linenr (QIntList&,
2771  QStringList&)),
2772  bp, SLOT (handle_report_editor_linenr (QIntList&,
2773  QStringList&)));
2774  connect (bp, SIGNAL (request_remove (int)),
2775  this, SLOT (handle_request_remove_breakpoint (int)));
2776  }
2777  }
2778  else
2780  }
2781  }
2782 
2784  {
2785  long int visible_lines
2786  = _edit_area->SendScintilla (QsciScintillaBase::SCI_LINESONSCREEN);
2787 
2788  if (visible_lines > 2)
2789  {
2790  int line, index;
2791  _edit_area->getCursorPosition (&line, &index);
2792  // compensate for "folding":
2793  // step 1: expand the current line, if it was folded
2794  _edit_area->SendScintilla (2232, line); // SCI_ENSUREVISIBLE
2795 
2796  // step 2: map file line num to "visible" one // SCI_VISIBLEFROMDOCLINE
2797  int vis_line = _edit_area->SendScintilla (2220, line);
2798 
2799  int first_line = _edit_area->firstVisibleLine ();
2800 
2801  if (always || vis_line == first_line
2802  || vis_line > first_line + visible_lines - 2)
2803  {
2804  first_line += (vis_line - first_line - (visible_lines - 1) / 2);
2805  _edit_area->SendScintilla (2613, first_line); // SCI_SETFIRSTVISIBLELINE
2806  }
2807  }
2808  }
2809 
2811  {
2812  // the related signal is emitted before cursor-move-signal!
2813  _lines_changed = true;
2814  }
2815 
2817  {
2818  if (_edit_area->SendScintilla (QsciScintillaBase::SCI_AUTOCACTIVE))
2819  show_auto_completion (this);
2820 
2821  if (_lines_changed) // cursor moved and lines have changed
2822  {
2823  _lines_changed = false;
2824  if (_is_octave_file && line == _line+1 && col < _col)
2825  {
2826  // Obviously, we have a newline here
2827  if (_smart_indent || _auto_endif)
2829  }
2830  }
2831 
2832  _line = line;
2833  _col = col;
2834 
2835  _row_indicator->setNum (line+1);
2836  _col_indicator->setNum (col+1);
2837  }
2838 
2839  // Slot that is entered each time a new character was typed.
2840  // It is used for handling line breaking if this is desired.
2841  // The related signal is emitted after the signal for a moved cursor
2842  // such that _col and _line can not be used for current position.
2844  {
2845  if (_line_break)
2846  {
2847  // If line breaking is desired, get the current line and column.
2848  // For taking the tab width into consideration, use own function
2849  int line, col, pos;
2850  _edit_area->get_current_position (&pos, &line, &col);
2851 
2852  // immediately return if line has not reached the max. line length
2853  if (col <= _line_break)
2854  return;
2855 
2856  // If line breaking is only desired in comments,
2857  // return if not in a comment
2858  int style_comment = octave_qscintilla::ST_NONE;
2860  {
2861  // line breaking only in comments, check for comment style
2862  style_comment = _edit_area->is_style_comment ();
2863  if (! style_comment)
2864  return; // no comment, return
2865  }
2866 
2867  // Here we go for breaking the current line by inserting a newline.
2868  // For determining the position of a specific column, we have to get
2869  // the column from the QScintila function without taking tab lengths
2870  // into account, since the calculation from line/col to position ignores
2871  // this, too
2872  _edit_area->getCursorPosition (&line, &col);
2873  int c = 0;
2874  int col_space = col;
2875  int indentation = _edit_area->indentation (line);
2876 
2877  // Search the first occurence of space or tab backwards starting from
2878  // the current column (col_space).
2879  while (c != ' ' && c != '\t' && col_space > indentation)
2880  {
2881  pos = _edit_area->positionFromLineIndex (line, col_space--);
2882  c = _edit_area->SendScintilla (QsciScintillaBase::SCI_GETCHARAT, pos);
2883  }
2884 
2885  // If a space or tab was found, break at this char,
2886  // otherwise break at cursor position
2887  int col_newline = col - 1;
2888  if (c == ' ' || c == '\t')
2889  col_newline = col_space + 1;
2890 
2891  // Insert a newline char for breaking the line possibly followed
2892  // by a line comment string
2893  QString newline = QString ("\n");
2894  style_comment = _edit_area->is_style_comment ();
2895  if (style_comment == octave_qscintilla::ST_LINE_COMMENT)
2896  newline = newline + _edit_area->comment_string ().at (0);
2897  _edit_area->insertAt (newline, line, col_newline);
2898 
2899  // Automatically indent the new line to the indentation of previous line
2900  // and set the cursor position to the end of the indentation.
2901  _edit_area->setIndentation (line + 1, indentation);
2902  _edit_area->SendScintilla (QsciScintillaBase::SCI_LINEEND);
2903  }
2904  }
2905 
2906  // Slot handling a double click into the text area
2907  void file_editor_tab::handle_double_click (int, int, int modifier)
2908  {
2909  if (! modifier)
2910  {
2911  // double clicks without modifier
2912  // clear any existing indicators of this type
2914 
2916  {
2917  // Clear any previous selection.
2919 
2920  // highlighting of all occurrences of the clicked word is enabled
2921 
2922  // get the resulting cursor position
2923  // (required if click was beyond a line ending)
2924  int line, col;
2925  _edit_area->getCursorPosition (&line, &col);
2926 
2927  // get the word at the cursor (if any)
2928  QString word = _edit_area->wordAtLineIndex (line, col);
2929  word = word.trimmed ();
2930 
2931  if (! word.isEmpty ())
2932  {
2933  // word is not empty, so find all occurrences of the word
2934 
2935  // remember first visible line for restoring the view afterwards
2936  int first_line = _edit_area->firstVisibleLine ();
2937 
2938  // search for first occurrence of the detected word
2939  bool find_result_available
2940  = _edit_area->findFirst (word,
2941  false, // no regexp
2942  true, // case sensitive
2943  true, // whole words only
2944  false, // do not wrap
2945  true, // forward
2946  0,0, // from the beginning
2947  false
2948 #if defined (HAVE_QSCI_VERSION_2_6_0)
2949  , true
2950 #endif
2951  );
2952 
2953  // loop over all occurrences and set the related indicator
2954  int oline, ocol;
2955  int wlen = word.length ();
2956 
2957  while (find_result_available)
2958  {
2959  // get cursor position after having found an occurrence
2960  _edit_area->getCursorPosition (&oline, &ocol);
2961  // mark the selection
2962  _edit_area->show_selection_markers (oline, ocol, wlen);
2963 
2964  // find next occurrence
2965  find_result_available = _edit_area->findNext ();
2966  }
2967 
2968  // restore the visible area of the file, the cursor position,
2969  // and the selection
2970  _edit_area->setFirstVisibleLine (first_line);
2971  _edit_area->setCursorPosition (line, col);
2972  _edit_area->setSelection (line, col - wlen, line, col);
2974  }
2975  }
2976  }
2977  }
2978 
2980  {
2981  QRegExp rxfun1 ("^[\t ]*function[^=]+=([^\\(]+)\\([^\\)]*\\)[\t ]*$");
2982  QRegExp rxfun2 ("^[\t ]*function[\t ]+([^\\(]+)\\([^\\)]*\\)[\t ]*$");
2983  QRegExp rxfun3 ("^[\t ]*function[^=]+=[\t ]*([^\\s]+)[\t ]*$");
2984  QRegExp rxfun4 ("^[\t ]*function[\t ]+([^\\s]+)[\t ]*$");
2985 
2986  QStringList lines = _edit_area->text ().split ("\n");
2987 
2988  for (int i = 0; i < lines.count (); i++)
2989  {
2990  if (rxfun1.indexIn (lines.at (i)) != -1)
2991  return rxfun1.cap (1).remove (QRegExp ("[ \t]*"));
2992  else if (rxfun2.indexIn (lines.at (i)) != -1)
2993  return rxfun2.cap (1).remove (QRegExp ("[ \t]*"));
2994  else if (rxfun3.indexIn (lines.at (i)) != -1)
2995  return rxfun3.cap (1).remove (QRegExp ("[ \t]*"));
2996  else if (rxfun4.indexIn (lines.at (i)) != -1)
2997  return rxfun4.cap (1).remove (QRegExp ("[ \t]*"));
2998  }
2999 
3000  return QString ();
3001  }
3002 }
3003 
3004 #endif
void handle_find_dialog_finished(int decision)
void handle_file_resave_answer(int decision)
void comment_selected_text(const QWidget *ID, bool input_str)
QString load_file(const QString &fileName)
void print_file(const QWidget *ID)
void handle_char_added(int character)
void previous_bookmark(const QWidget *ID)
void convert_eol(const QWidget *ID, QsciScintilla::EolMode)
void set_focus(const QWidget *ID)
OCTINTERP_API void octave_sleep(double seconds)
void handle_copy_available(bool enableCopy)
Definition: Cell.h:37
For example cd octave end example noindent changes the current working directory to file
Definition: dirfns.cc:124
static void combo_encoding(QComboBox *combo, QString current=QString())
void request_remove_breakpoint_via_editor_linenr(int editor_linenr)
bool is_user_code(void) const
Definition: ov.h:767
void remove_all_breakpoints(void)
std::string canonicalize_file_name(const std::string &name)
Definition: file-ops.cc:685
interpreter & __get_interpreter__(const std::string &who)
fname
Definition: load-save.cc:767
bool valid_file_name(const QString &file=QString())
std::string string_value(bool force=false) const
Definition: ov.h:955
void set_modified(bool modified=true)
bool check_valid_identifier(QString file_name)
void init_search_text(void)
Definition: find-dialog.cc:246
void run_file(const QWidget *ID)
void edit_mfile_request(const QString &, const QString &, const QString &, int)
QsciScintilla::EolMode _save_as_desired_eol
bp_info(const QString &fname, int l=0, const QString &cond="")
void next_breakpoint(const QWidget *ID)
std::string dir_sep_chars(void)
Definition: file-ops.cc:242
void set_focus_editor_signal(QWidget *)
void next_bookmark(const QWidget *ID)
void editor_state_changed(bool copy_available, bool is_octave_file)
for fields that display a single number
Definition: time.cc:441
void handle_request_remove_breakpoint(int line)
QFileSystemWatcher _file_system_watcher
void file_has_changed(const QString &path, bool do_close=false)
octave_map backtrace(size_t nskip, octave_idx_type &curr_user_frame, bool print_subfn=true) const
Definition: call-stack.cc:573
void add_octave_apis(octave_value_list key_ovl)
void file_name_query(const QWidget *ID)
void set_word_selection(const QString &word=QString())
void handle_file_modified_answer(int decision)
void find_next(const QWidget *ID)
void scintilla_command(const QWidget *, unsigned int)
void zoom_normal(const QWidget *ID)
bool is_defined(void) const
Definition: ov.h:523
void handle_save_as_filter_selected(const QString &filter)
T & elem(octave_idx_type n)
Definition: Array.h:488
void remove_position_via_debugger_linenr(int debugger_linenr)
bool condition_valid(const std::string &cond)
Definition: bp-table.cc:211
to define functions rather than attempting to enter them directly on the command line The block of commands is executed as soon as you exit the editor To avoid executing any simply delete all the lines from the buffer before leaving the editor When invoked with no edit the previously executed command
Definition: oct-hist.cc:587
nd example oindent opens the file binary numeric values will be read assuming they are stored in IEEE format with the least significant bit and then converted to the native representation Opening a file that is already open simply opens it again and returns a separate file id It is not an error to open a file several though writing to the same file through several different file ids may produce unexpected results The possible values of text mode reading and writing automatically converts linefeeds to the appropriate line end character for the you may append a you must also open the file in binary mode The parameter conversions are currently only supported for and permissions will be set to and then everything is written in a single operation This is very efficient and improves performance c
Definition: file-io.cc:587
void update_window_title(bool modified)
s
Definition: file-io.cc:2729
intmap add_breakpoint(const std::string &fname="", const intmap &lines=intmap(), const std::string &condition="")
Definition: bp-table.cc:554
void zoom_out(const QWidget *ID)
void context_edit(const QWidget *ID)
i e
Definition: data.cc:2591
int remove_breakpoint(const std::string &fname="", const intmap &lines=intmap())
Definition: bp-table.cc:643
virtual std::string fcn_file_name(void) const
Definition: ov-fcn.h:74
void closeEvent(QCloseEvent *event)
void do_breakpoint_marker(bool insert, const QWidget *ID, int line=-1, const QString &cond="")
octave_value arg
Definition: pr-output.cc:3244
octave_function * fcn
Definition: ov-class.cc:1754
#define OCTAVE_VERSION
Definition: main.in.cc:48
void handle_combo_enc_current_index(QString text)
octave::call_stack & cs
Definition: ov-class.cc:1752
void execute_command_in_terminal_signal(const QString &)
void zoom_in(const QWidget *ID)
std::string concat(const std::string &dir, const std::string &file)
Definition: file-ops.cc:344
void unindent_selected_text(const QWidget *ID)
void toggle_bookmark(const QWidget *ID)
void handle_request_add_breakpoint(int line, const QString &cond)
double h
Definition: graphics.cc:11808
symbol_table & __get_symbol_table__(const std::string &who)
void do_smart_indent_line_or_selected_text(void)
int buffer_error_messages
Definition: error.cc:112
void insert_debugger_pointer(const QWidget *ID, int line=-1)
file_editor_tab(const QString &directory="")
A file_editor_tab object consists of a text area and three left margins.
void mru_add_file(const QString &file_name, const QString &encoding)
void get_current_position(int *pos, int *line, int *col)
bool valid_identifier(const char *s)
Definition: utils.cc:74
void notice_settings(const QSettings *settings, bool init=false)
QStringList comment_string(bool comment=true)
void move_match_brace(const QWidget *ID, bool select)
void find_previous(const QWidget *ID)
intmap remove_all_breakpoints_in_file(const std::string &fname, bool silent=false)
Definition: bp-table.cc:695
void file_name_changed(const QString &fileName, const QString &toolTip)
void handle_save_file_as_answer(const QString &fileName)
void previous_breakpoint(const QWidget *ID)
void do_indent_selected_text(bool indent)
void add_filename_to_list(const QString &, const QString &, QWidget *)
void request_find_previous(void)
void handle_double_click(int p, int l, int modifier)
void run_file_signal(const QFileInfo &info)
void smart_indent_line_or_selected_text(const QWidget *ID)
void toggle_breakpoint(const QWidget *ID)
void handle_context_menu_edit(const QString &)
octave_value retval
Definition: data.cc:6246
void indent_selected_text(const QWidget *ID)
bool is_true(const std::string &s)
void set_current_directory(const QString &dir)
const Cell & contents(const_iterator p) const
Definition: oct-map.h:317
void context_help(const QWidget *ID, bool)
std::map< int, int > intmap
Definition: bp-table.h:60
MArray< T > filter(MArray< T > &b, MArray< T > &a, MArray< T > &x, MArray< T > &si, int dim=0)
Definition: filter.cc:43
const QString & get_cond(void) const
Definition: marker.h:69
octave_value find(const std::string &name, const octave_value_list &args=octave_value_list(), bool skip_variables=false, bool local_funcs=true)
Definition: symtab.cc:358
void set_file_name(const QString &fileName)
void report_marker_linenr(QIntList &lines, QStringList &conditions)
void save_file_as(const QWidget *ID)
void do_comment_selected_text(bool comment, bool input_str=false)
void save_file(const QWidget *ID)
void editor_check_conflict_save(const QString &saveFileName, bool remove_on_success)
void goto_line(const QWidget *ID, int line=-1)
void delete_debugger_pointer(const QWidget *ID, int line=-1)
const QString oct_last_comment_str("editor/oct_last_comment_str")
QsciScintilla::EolMode detect_eol_mode(void)
void context_run(const QWidget *ID)
static uint32_t state[624]
Definition: randmtzig.cc:183
void uncomment_selected_text(const QWidget *ID)
void warning(const char *fmt,...)
Definition: error.cc:801
octave::unwind_protect frame
Definition: graphics.cc:12190
OCTINTERP_API std::string last_error_message(void)
octave_qscintilla * _edit_area
OCTAVE_EXPORT octave_value_list the first data row corresponds to an index of zero The a spreadsheet style form such as the file is read until end of file is reached The such as text
Definition: dlmread.cc:194
defaults to zero A value of zero computes the digamma a value the trigamma and so on The digamma function is defined
Definition: psi.cc:68
octave_user_code * user_code_value(bool silent=false) const
void remove_breakpoint_callback(const bp_info &info)
bp_table & __get_bp_table__(const std::string &who)
p
Definition: lu.cc:138
void handle_context_menu_break_condition(int linenr)
void center_current_line(bool always=true)
void remove_all_breakpoints_callback(const bp_info &info)
void remove_all_positions(void)
void handle_file_reload_answer(int decision)
call_stack & __get_call_stack__(const std::string &who)
void handle_cursor_moved(int line, int col)
void handle_save_file_as_answer_close(const QString &fileName)
void handle_combo_eol_current_index(int index)
void remove_breakpoint_via_debugger_linenr(int debugger_linenr)
static QSettings * get_settings(void)
octave::sys::file_stat fs(filename)
void smart_indent(bool do_smart_indent, int do_auto_close, int line)
for i
Definition: data.cc:5264
void remove_bookmark(const QWidget *ID)
void set_encoding(const QString &new_encoding)
QList< int > QIntList
Definition: dialog.h:38
lexer(interpreter *interp=nullptr)
Definition: lex.h:758
bool exit_debug_and_clear(const QString &full_name, const QString &base_name)
void handle_octave_result(QObject *requester, QString &command, octave_value_list &result)
void show_dialog(QDialog *dlg, bool modal)
void show_auto_completion(const QWidget *ID)
void clear_user_function(const std::string &name)
Definition: symtab.h:432
void new_file(const QString &commands=QString())
nd example The result of the integration is returned in the value of it is recommended to verify this value for difficult integrands and then a warning is issued and as may be less efficient for a smooth or otherwise well behaved integrand than other methods such as Article No
Definition: quadcc.cc:1567
void handle_save_file_as_answer_cancel(void)
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:366
void smart_indent_line_or_selected_text(int lineFrom, int lineTo)
void change_editor_state(const QWidget *ID)
std::string toStdString(const QString &s)
bool check_valid_codec(QTextCodec *codec)
void tab_remove_request(void)
void set_selection_marker_color(const QColor &c)
If this string is the system will ring the terminal sometimes it is useful to be able to print the original representation of the string
Definition: utils.cc:888
void handle_margin_clicked(int line, int margin, Qt::KeyboardModifiers state)
void request_find_next(void)
void set_cond(const QString &cond)
Definition: marker.h:71
void find_linenr_just_before(int linenr, int &original_linenr, int &editor_linenr)
void add_breakpoint_callback(const bp_info &info)
void show_selection_markers(int line, int col, int len)
Cell cell_value(void) const
void find_translated_line_number(int original_linenr, int &translated_linenr, marker *&)
to define functions rather than attempting to enter them directly on the command line The block of commands is executed as soon as you exit the editor To avoid executing any commands
Definition: oct-hist.cc:587
void find(const QWidget *ID, QList< QAction *>)