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