GNU Octave  4.4.1
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
octave-qscintilla.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2013-2018 Torsten
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 // Author: Torsten <ttl@justmail.de>
24 
25 #if defined (HAVE_CONFIG_H)
26 # include "config.h"
27 #endif
28 
29 #if defined (HAVE_QSCINTILLA)
30 
31 #include <Qsci/qscilexer.h>
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 
46 #include <Qsci/qscicommandset.h>
47 
48 #include <QKeySequence>
49 #include <QShortcut>
50 #include <QToolTip>
51 #include <QVBoxLayout>
52 
53 #include <QMimeData>
54 
55 // FIXME: hardwired marker numbers?
56 #include "marker.h"
57 
58 #include "octave-qscintilla.h"
59 #include "file-editor-tab.h"
60 #include "shortcut-manager.h"
61 #include "resource-manager.h"
62 #include "octave-settings.h"
63 
64 // Return true if CANDIDATE is a "closing" that matches OPENING,
65 // such as "end" or "endif" for "if", or "catch" for "try".
66 // Used for testing the last word of an "if" etc. line,
67 // or the first word of the following line.
68 
69 static bool
70 is_end (const QString& candidate, const QString& opening)
71 {
72  bool retval = false;
73 
74  if (opening == "do") // The only one that can't be ended by "end"
75  {
76  if (candidate == "until")
77  retval = true;
78  }
79  else
80  {
81  if (candidate == "end")
82  retval = true;
83  else
84  {
85  if (opening == "try")
86  {
87  if (candidate == "catch" || candidate == "end_try_catch")
88  retval = true;
89  }
90  else if (opening == "unwind_protect")
91  {
92  if (candidate == "unwind_protect_cleanup"
93  || candidate == "end_unwind_protect")
94  retval = true;
95  }
96  else if (candidate == "end" + opening)
97  retval = true;
98  else if (opening == "if" && candidate == "else")
99  retval = true;
100  }
101  }
102 
103  return retval;
104 }
105 
106 namespace octave
107 {
109  : QsciScintilla (p), m_word_at_cursor (), m_selection (),
110  m_selection_replacement (), m_selection_line (-1),
111  m_selection_col (-1), m_indicator_id (1)
112  {
113  connect (this, SIGNAL (textChanged (void)),
114  this, SLOT (text_changed (void)));
115 
116  connect (this, SIGNAL (cursorPositionChanged (int, int)),
117  this, SLOT (cursor_position_changed (int, int)));
118 
119  // clear scintilla edit shortcuts that are handled by the editor
120  QsciCommandSet *cmd_set = standardCommands ();
121 
122  // Disable buffered drawing on all systems
123  SendScintilla (SCI_SETBUFFEREDDRAW, false);
124 
125 #if defined (HAVE_QSCI_VERSION_2_6_0)
126  // find () was added in QScintilla 2.6
127  cmd_set->find (QsciCommand::SelectionCopy)->setKey (0);
128  cmd_set->find (QsciCommand::SelectionCut)->setKey (0);
129  cmd_set->find (QsciCommand::Paste)->setKey (0);
130  cmd_set->find (QsciCommand::SelectAll)->setKey (0);
131  cmd_set->find (QsciCommand::SelectionDuplicate)->setKey (0);
132  cmd_set->find (QsciCommand::LineTranspose)->setKey (0);
133  cmd_set->find (QsciCommand::Undo)->setKey (0);
134  cmd_set->find (QsciCommand::Redo)->setKey (0);
135  cmd_set->find (QsciCommand::SelectionUpperCase)->setKey (0);
136  cmd_set->find (QsciCommand::SelectionLowerCase)->setKey (0);
137  cmd_set->find (QsciCommand::ZoomIn)->setKey (0);
138  cmd_set->find (QsciCommand::ZoomOut)->setKey (0);
139  cmd_set->find (QsciCommand::DeleteWordLeft)->setKey (0);
140  cmd_set->find (QsciCommand::DeleteWordRight)->setKey (0);
141  cmd_set->find (QsciCommand::DeleteLineLeft)->setKey (0);
142  cmd_set->find (QsciCommand::DeleteLineRight)->setKey (0);
143  cmd_set->find (QsciCommand::LineDelete)->setKey (0);
144  cmd_set->find (QsciCommand::LineCut)->setKey (0);
145  cmd_set->find (QsciCommand::LineCopy)->setKey (0);
146 #else
147  // find commands via its default key (tricky way without find ())
148  QList< QsciCommand * > cmd_list = cmd_set->commands ();
149  for (int i = 0; i < cmd_list.length (); i++)
150  {
151  int cmd_key = cmd_list.at (i)->key ();
152  switch (cmd_key)
153  {
154  case Qt::Key_C | Qt::CTRL : // SelectionCopy
155  case Qt::Key_X | Qt::CTRL : // SelectionCut
156  case Qt::Key_V | Qt::CTRL : // Paste
157  case Qt::Key_A | Qt::CTRL : // SelectAll
158  case Qt::Key_D | Qt::CTRL : // SelectionDuplicate
159  case Qt::Key_T | Qt::CTRL : // LineTranspose
160  case Qt::Key_Z | Qt::CTRL : // Undo
161  case Qt::Key_Y | Qt::CTRL : // Redo
162  case Qt::Key_Z | Qt::CTRL | Qt::SHIFT : // Redo
163  case Qt::Key_U | Qt::CTRL : // SelectionLowerCase
164  case Qt::Key_U | Qt::CTRL | Qt::SHIFT : // SelectionUpperCase
165  case Qt::Key_Plus | Qt::CTRL : // ZoomIn
166  case Qt::Key_Minus | Qt::CTRL : // ZoomOut
167  case Qt::Key_Backspace | Qt::CTRL | Qt::SHIFT : // DeleteLineLeft
168  case Qt::Key_Delete | Qt::CTRL | Qt::SHIFT : // DeleteLineRight
169  case Qt::Key_K | Qt::META : // DeleteLineRight
170  case Qt::Key_Backspace | Qt::CTRL : // DeleteWordLeft
171  case Qt::Key_Delete | Qt::CTRL : // DeleteWordRight
172  case Qt::Key_L | Qt::CTRL | Qt::SHIFT : // LineDelete
173  case Qt::Key_L | Qt::CTRL : // LineCut
174  case Qt::Key_T | Qt::CTRL | Qt::SHIFT : // LineCopy
175  cmd_list.at (i)->setKey (0);
176  }
177  }
178 #endif
179 
180 #if defined (Q_OS_MAC)
181  // Octave interprets Cmd key as Meta whereas Qscintilla interprets it
182  // as Ctrl. We thus invert Meta/Ctrl in Qscintilla's shortcuts list.
183  QList< QsciCommand * > cmd_list_mac = cmd_set->commands ();
184  for (int i = 0; i < cmd_list_mac.length (); i++)
185  {
186  // Primary key
187  int key = cmd_list_mac.at (i)->key ();
188 
189  if (static_cast<int> (key | Qt::META) == key &&
190  static_cast<int> (key | Qt::CTRL) != key)
191  key = (key ^ Qt::META) | Qt::CTRL;
192  else if (static_cast<int> (key | Qt::CTRL) == key)
193  key = (key ^ Qt::CTRL) | Qt::META;
194 
195  cmd_list_mac.at (i)->setKey (key);
196 
197  // Alternate key
198  key = cmd_list_mac.at (i)->alternateKey ();
199 
200  if (static_cast<int> (key | Qt::META) == key &&
201  static_cast<int> (key | Qt::CTRL) != key)
202  key = (key ^ Qt::META) | Qt::CTRL;
203  else if (static_cast<int> (key | Qt::CTRL) == key)
204  key = (key ^ Qt::CTRL) | Qt::META;
205 
206  cmd_list_mac.at (i)->setAlternateKey (key);
207  }
208 #endif
209 
210  // selection markers
211 
212  m_indicator_id = indicatorDefine (QsciScintilla::StraightBoxIndicator);
213  if (m_indicator_id == -1)
214  m_indicator_id = 1;
215 
216  setIndicatorDrawUnder (true, m_indicator_id);
217 
218  markerDefine (QsciScintilla::Minus, marker::selection);
219 
220  // init state of undo/redo action for this tab
221  emit status_update (isUndoAvailable (), isRedoAvailable ());
222  }
223 
225  {
226  QColor ic = c;
227  ic.setAlphaF (0.25);
228  setIndicatorForegroundColor (ic, m_indicator_id);
229  setIndicatorOutlineColor (ic, m_indicator_id);
230 
231  setMarkerForegroundColor (c, marker::selection);
232  setMarkerBackgroundColor (c, marker::selection);
233  }
234 
235  // context menu requested
236  void octave_qscintilla::contextMenuEvent (QContextMenuEvent *e)
237  {
238 #if defined (HAVE_QSCI_VERSION_2_6_0)
239  QPoint global_pos, local_pos; // the menu's position
240  QMenu *context_menu = createStandardContextMenu (); // standard menu
241 
242  bool in_left_margin = false;
243 
244  // determine position depending on mouse or keyboard event
245  if (e->reason () == QContextMenuEvent::Mouse)
246  {
247  // context menu by mouse
248  global_pos = e->globalPos (); // global mouse position
249  local_pos = e->pos (); // local mouse position
250  if (e->x () < marginWidth (1) + marginWidth (2))
251  in_left_margin = true;
252  }
253  else
254  {
255  // context menu by keyboard or other: get point of text cursor
256  get_global_textcursor_pos (&global_pos, &local_pos);
257  QRect editor_rect = geometry (); // editor rect mapped to global
258  editor_rect.moveTopLeft
259  (parentWidget ()->mapToGlobal (editor_rect.topLeft ()));
260  if (! editor_rect.contains (global_pos)) // is cursor outside editor?
261  global_pos = editor_rect.topLeft (); // yes, take top left corner
262  }
263 
264 #if defined (HAVE_QSCI_VERSION_2_6_0)
265  if (! in_left_margin)
266 #endif
267  {
268  // fill context menu with editor's standard actions
269  emit create_context_menu_signal (context_menu);
270 
271  // additional custom entries of the context menu
272  context_menu->addSeparator (); // separator before custom entries
273 
274  // help menu: get the position of the mouse or the text cursor
275  // (only for octave files)
276  QString lexer_name = lexer ()->lexer ();
277  if (lexer_name == "octave" || lexer_name == "matlab")
278  {
279  m_word_at_cursor = wordAtPoint (local_pos);
280  if (! m_word_at_cursor.isEmpty ())
281  {
282  context_menu->addAction (tr ("Help on") + ' ' + m_word_at_cursor,
283  this, SLOT (contextmenu_help (bool)));
284  context_menu->addAction (tr ("Documentation on")
285  + ' ' + m_word_at_cursor,
286  this, SLOT (contextmenu_doc (bool)));
287  context_menu->addAction (tr ("Edit") + ' ' + m_word_at_cursor,
288  this, SLOT (contextmenu_edit (bool)));
289  }
290  }
291  }
292 #if defined (HAVE_QSCI_VERSION_2_6_0)
293  else
294  {
295  // remove all standard actions from scintilla
296  QList<QAction *> all_actions = context_menu->actions ();
297  QAction *a;
298 
299  foreach (a, all_actions)
300  context_menu->removeAction (a);
301 
302  a = context_menu->addAction (tr ("dbstop if ..."), this,
303  SLOT (contextmenu_break_condition (bool)));
304  a->setData (local_pos);
305  }
306 #endif
307 
308  // finaly show the menu
309  context_menu->exec (global_pos);
310 #endif
311  }
312 
313  // common function with flag for documentation
315  {
316  if (documentation)
318  else
320  }
321 
322  // call edit the function related to the current word
324  {
325  if (get_actual_word ())
326  contextmenu_edit (true);
327  }
328 
329  // call edit the function related to the current word
331  {
332  if (hasSelectedText ())
333  contextmenu_run (true);
334  }
335 
337  QPoint *local_pos)
338  {
339  long position = SendScintilla (SCI_GETCURRENTPOS);
340  long point_x = SendScintilla (SCI_POINTXFROMPOSITION,0,position);
341  long point_y = SendScintilla (SCI_POINTYFROMPOSITION,0,position);
342  *local_pos = QPoint (point_x,point_y); // local cursor position
343  *global_pos = mapToGlobal (*local_pos); // global position of cursor
344  }
345 
346  // determine the actual word and whether we are in an octave or matlab script
348  {
349  QPoint global_pos, local_pos;
350  get_global_textcursor_pos (&global_pos, &local_pos);
351  m_word_at_cursor = wordAtPoint (local_pos);
352  QString lexer_name = lexer ()->lexer ();
353  return ((lexer_name == "octave" || lexer_name == "matlab")
354  && ! m_word_at_cursor.isEmpty ());
355  }
356 
357  // helper function for clearing all indicators of a specific style
359  {
360  int end_pos = text ().length ();
361  int end_line, end_col;
362  lineIndexFromPosition (end_pos, &end_line, &end_col);
363  clearIndicatorRange (0, 0, end_line, end_col, m_indicator_id);
364 
365  markerDeleteAll (marker::selection);
366  }
367 
368  // Function returning the true cursor position where the tab length
369  // is taken into account.
370  void octave_qscintilla::get_current_position (int *pos, int *line, int *col)
371  {
372  *pos = SendScintilla (QsciScintillaBase::SCI_GETCURRENTPOS);
373  *line = SendScintilla (QsciScintillaBase::SCI_LINEFROMPOSITION, *pos);
374  *col = SendScintilla (QsciScintillaBase::SCI_GETCOLUMN, *pos);
375  }
376 
377  // Function returning the comment string of the current lexer
378  QStringList octave_qscintilla::comment_string (bool comment)
379  {
380  int lexer = SendScintilla (SCI_GETLEXER);
381 
382  switch (lexer)
383  {
384 #if defined (HAVE_LEXER_OCTAVE) || defined (HAVE_LEXER_MATLAB)
385 #if defined (HAVE_LEXER_OCTAVE)
386  case SCLEX_OCTAVE:
387 #else
388  case SCLEX_MATLAB:
389 #endif
390  {
391  QSettings *settings = resource_manager::get_settings ();
392  int comment_string;
393 
394  if (comment)
395  {
396  // The commenting string is requested
397  if (settings->contains (oct_comment_str))
398  // new version (radio buttons)
399  comment_string = settings->value (oct_comment_str,
400  oct_comment_str_d).toInt ();
401  else
402  // old version (combo box)
403  comment_string = settings->value (oct_comment_str_old,
404  oct_comment_str_d).toInt ();
405 
406  return (QStringList (oct_comment_strings.at (comment_string)));
407  }
408  else
409  {
410  QStringList c_str;
411 
412  // The possible uncommenting string(s) are requested
413  comment_string = settings->value (oct_uncomment_str,
414  oct_uncomment_str_d).toInt ();
415 
416  for (int i = 0; i < oct_comment_strings_count; i++)
417  {
418  if (1 << i & comment_string)
419  c_str.append (oct_comment_strings.at (i));
420  }
421 
422  return c_str;
423  }
424 
425  }
426 #endif
427 
428  case SCLEX_PERL:
429  case SCLEX_BASH:
430  case SCLEX_DIFF:
431  return QStringList ("#");
432 
433  case SCLEX_CPP:
434  return QStringList ("//");
435 
436  case SCLEX_BATCH:
437  return QStringList ("REM ");
438  }
439 
440  return QStringList ("%"); // should never happen
441  }
442 
443 
444  // provide the style at a specific position
446  {
447  int position;
448  if (pos < 0)
449  // The positition has to be reduced by 2 for getting the real style (?)
450  position = SendScintilla (QsciScintillaBase::SCI_GETCURRENTPOS) - 2;
451  else
452  position = pos;
453 
454  return SendScintilla (QsciScintillaBase::SCI_GETSTYLEAT, position);
455  }
456 
457  // Is a specific cursor position in a line or block comment?
459  {
460  int lexer = SendScintilla (QsciScintillaBase::SCI_GETLEXER);
461  int style = get_style (pos);
462 
463  switch (lexer)
464  {
465  case SCLEX_CPP:
466  return (ST_LINE_COMMENT * (
467  style == QsciLexerCPP::CommentLine
468  || style == QsciLexerCPP::CommentLineDoc)
469  + ST_BLOCK_COMMENT * (
470  style == QsciLexerCPP::Comment
471  || style == QsciLexerCPP::CommentDoc
472  || style == QsciLexerCPP::CommentDocKeyword
473  || style == QsciLexerCPP::CommentDocKeywordError)
474  );
475 
476 #if defined (HAVE_LEXER_MATLAB)
477  case SCLEX_MATLAB:
478  return (ST_LINE_COMMENT * (style == QsciLexerMatlab::Comment));
479 #endif
480 #if defined (HAVE_LEXER_OCTAVE)
481  case SCLEX_OCTAVE:
482  return (ST_LINE_COMMENT * (style == QsciLexerOctave::Comment));
483 #endif
484 
485  case SCLEX_PERL:
486  return (ST_LINE_COMMENT * (style == QsciLexerPerl::Comment));
487 
488  case SCLEX_BATCH:
489  return (ST_LINE_COMMENT * (style == QsciLexerBatch::Comment));
490 
491  case SCLEX_DIFF:
492  return (ST_LINE_COMMENT * (style == QsciLexerDiff::Comment));
493 
494  case SCLEX_BASH:
495  return (ST_LINE_COMMENT * (style == QsciLexerBash::Comment));
496 
497  }
498 
499  return ST_NONE;
500  }
501 
502  // Do smart indendation after if, for, ...
503  void octave_qscintilla::smart_indent (bool do_smart_indent,
504  int do_auto_close, int line)
505  {
506  QString prevline = text (line);
507 
508  QRegExp bkey = QRegExp ("^[\t ]*(if|for|while|switch|case|otherwise"
509  "|do|function|properties|events|classdef"
510  "|unwind_protect|unwind_protect_cleanup|try"
511  "|parfor|methods)"
512  "[\r]?[\n\t #%]");
513  // last word except for comments, assuming no ' or " in comment.
514  // rx_end = QRegExp ("(\\w+)[ \t;\r\n]*([%#][^\"']*)?$");
515 
516  // last word except for comments,
517  // allowing % and # in single or double quoted strings
518  // FIXME This will get confused by transpose.
519  QRegExp ekey = QRegExp ("(?:(?:['\"][^'\"]*['\"])?[^%#]*)*"
520  "(\\w+)[ \t;\r\n]*([%#].*)?$");
521 
522  int bpos = bkey.indexIn (prevline, 0);
523  int epos;
524 
525  if (bpos > -1)
526  {
527  // Found keyword after that indentation should be added
528 
529  // Check for existing end statement in the same line
530  epos = ekey.indexIn (prevline, bpos);
531  QString first_word = bkey.cap(1);
532  bool inline_end = (epos > -1) && is_end (ekey.cap(1), first_word);
533 
534  if (do_smart_indent && ! inline_end)
535  {
536  // Do smart indent in the current line (line+1)
537  indent (line+1);
538  setCursorPosition (line+1, indentation (line) + indentationWidth ());
539  }
540 
541  if (do_auto_close
542  && ! inline_end
543  && ! first_word.contains (
544  QRegExp ("(case|otherwise|unwind_protect_cleanup)")))
545  {
546  // Do auto close
547  auto_close (do_auto_close, line, prevline, first_word);
548  }
549 
550  return;
551  }
552 
553  QRegExp mkey = QRegExp ("^[\t ]*(else|elseif|catch)[\r]?[\t #%\n]");
554  if (prevline.contains (mkey))
555  {
556  int prev_ind = indentation (line-1);
557  int act_ind = indentation (line);
558 
559  if (prev_ind == act_ind)
560  unindent (line);
561  else if (prev_ind > act_ind)
562  {
563  setIndentation (line+1, prev_ind);
564  setCursorPosition (line+1, prev_ind);
565  }
566  return;
567  }
568 
569  ekey = QRegExp ("^[\t ]*(end|endif|endfor|endwhile|until|endfunction"
570  "|end_try_catch|end_unwind_protext)[\r]?[\t #%\n(;]");
571  if (prevline.contains (ekey))
572  {
573  if (indentation (line-1) <= indentation (line))
574  {
575  unindent (line+1);
576  unindent (line);
577  setCursorPosition (line+1,
578  indentation (line));
579  }
580  return;
581  }
582  }
583 
584  // Do smart indendation of current selection or line.
586  int lineTo)
587  {
588  QRegExp blank_line_regexp = QRegExp ("^[\t ]*$");
589 
590  QRegExp begin_block_regexp
591  = QRegExp ("^([\t ]*)(if|elseif|else"
592  "|for|while|do|parfor"
593  "|switch|case|otherwise"
594  "|function"
595  "|classdef|properties|events|enumeration|methods"
596  "|unwind_protect|unwind_protect_cleanup|try|catch)"
597  "[\r\n\t #%]");
598 
599  QRegExp end_block_regexp
600  = QRegExp ("^([\t ]*)(end"
601  "|end(for|function|if|parfor|switch|while"
602  "|classdef|enumeration|events|methods|properties)"
603  "|end_(try_catch|unwind_protect)"
604  "|until)"
605  "[\r\n\t #%]");
606 
607  int indent_column = -1;
608  int indent_increment = indentationWidth ();
609 
610  for (int line = lineFrom-1; line >= 0; line--)
611  {
612  QString line_text = text (line);
613 
614  if (blank_line_regexp.indexIn (line_text) < 0)
615  {
616  // Found first non-blank line above beginning of region or
617  // current line. Base indentation from this line, increasing
618  // indentation by indentationWidth if it looks like the
619  // beginning of a code block.
620 
621  indent_column = indentation (line);
622 
623  if (begin_block_regexp.indexIn (line_text) > -1)
624  indent_column += indent_increment;
625 
626  break;
627  }
628  }
629 
630  if (indent_column < 0)
631  indent_column = indentation (lineFrom);
632 
633  for (int line = lineFrom; line <= lineTo; line++)
634  {
635  QString line_text = text (line);
636 
637  if (end_block_regexp.indexIn (line_text) > -1)
638  indent_column -= indent_increment;
639 
640  setIndentation (line, indent_column);
641 
642  if (begin_block_regexp.indexIn (line_text) > -1)
643  indent_column += indent_increment;
644  }
645  }
646 
647  void octave_qscintilla::set_word_selection (const QString& word)
648  {
649  m_selection = word;
650 
651  if (word.isEmpty ())
652  {
653  m_selection_line = -1;
654  m_selection_col = -1;
655 
657 
659 
660  QToolTip::hideText ();
661  }
662  else
663  {
664  int pos;
666  }
667  }
668 
669  void octave_qscintilla::show_selection_markers (int line, int col, int len)
670  {
671  fillIndicatorRange (line, col - len, line, col, m_indicator_id);
672 
673  markerAdd (line, marker::selection);
674  }
675 
677  {
678  contextmenu_help_doc (false);
679  }
680 
682  {
683  contextmenu_help_doc (true);
684  }
685 
687  {
688  if (get_actual_word ())
690  }
691 
693  {
695  }
696 
698  {
699  QStringList commands = selectedText ().split (QRegExp ("[\r\n]"),
700  QString::SkipEmptyParts);
701  for (int i = 0; i < commands.size (); i++)
703  }
704 
705  // wrappers for dbstop related context menu items
706 
707  // FIXME: Why can't the data be sent as the argument to the function???
709  {
710 #if defined (HAVE_QSCI_VERSION_2_6_0)
711  QAction *action = qobject_cast<QAction *>(sender ());
712  QPoint local_pos = action->data ().value<QPoint> ();
713 
714  // pick point just right of margins, so lineAt doesn't give -1
715  int margins = marginWidth (1) + marginWidth (2) + marginWidth (3);
716  local_pos = QPoint (margins + 1, local_pos.y ());
717 
718  emit context_menu_break_condition_signal (lineAt (local_pos));
719 #endif
720  }
721 
722  void octave_qscintilla::contextmenu_break_once (const QPoint& local_pos)
723  {
724 #if defined (HAVE_QSCI_VERSION_2_6_0)
725  emit context_menu_break_once (lineAt (local_pos));
726 #endif
727  }
728 
730  {
731  emit status_update (isUndoAvailable (), isRedoAvailable ());
732  }
733 
735  {
736  // Clear the selection if we move away from it. We have to check the
737  // position, because we allow entering text at the point of the
738  // selection to trigger a search and replace that does not clear the
739  // selection until it is complete.
740 
741  if (! m_selection.isEmpty ()
742  && (line != m_selection_line || col != m_selection_col))
744  }
745 
746  // when edit area gets focus update information on undo/redo actions
747  void octave_qscintilla::focusInEvent (QFocusEvent *focusEvent)
748  {
749  emit status_update (isUndoAvailable (), isRedoAvailable ());
750 
751  QsciScintilla::focusInEvent (focusEvent);
752  }
753 
755  {
756  int pos;
758 
759  // Offer to replace other instances.
760 
761  QKeySequence keyseq = Qt::SHIFT + Qt::Key_Return;
762 
763  QString msg = (tr ("Press '%1' to replace all occurrences of '%2' with '%3'.")
764  . arg (keyseq.toString ())
765  . arg (m_selection)
767 
768  QPoint global_pos;
769  QPoint local_pos;
770 
771  get_global_textcursor_pos (&global_pos, &local_pos);
772 
773  QFontMetrics ttfm (QToolTip::font ());
774 
775  // Try to avoid overlapping with the text completion dialog
776  // and the text that is currently being edited.
777 
778  global_pos += QPoint (2*ttfm.maxWidth (), -3*ttfm.height ());
779 
780  QToolTip::showText (global_pos, msg);
781  }
782 
783  void octave_qscintilla::keyPressEvent (QKeyEvent *key_event)
784  {
785  if (m_selection.isEmpty ())
786  QsciScintilla::keyPressEvent (key_event);
787  else
788  {
789  int key = key_event->key ();
790  Qt::KeyboardModifiers modifiers = key_event->modifiers ();
791 
792  if (key == Qt::Key_Return && modifiers == Qt::ShiftModifier)
793  {
794  // get the resulting cursor position
795  // (required if click was beyond a line ending)
796  int pos, line, col;
797  get_current_position (&pos, &line, &col);
798 
799  // remember first visible line for restoring the view afterwards
800  int first_line = firstVisibleLine ();
801 
802  // search for first occurrence of the detected word
803  bool find_result_available
804  = findFirst (m_selection,
805  false, // no regexp
806  true, // case sensitive
807  true, // whole words only
808  false, // do not wrap
809  true, // forward
810  0, 0, // from the beginning
811  false
812 #if defined (HAVE_QSCI_VERSION_2_6_0)
813  , true
814 #endif
815  );
816 
817  while (find_result_available)
818  {
819  replace (m_selection_replacement);
820 
821  // FIXME: is this the right thing to do? findNext doesn't
822  // work properly if the length of the replacement text is
823  // different from the original.
824 
825  int new_line, new_col;
826  get_current_position (&pos, &new_line, &new_col);
827 
828  find_result_available
829  = findFirst (m_selection,
830  false, // no regexp
831  true, // case sensitive
832  true, // whole words only
833  false, // do not wrap
834  true, // forward
835  new_line, new_col, // from new pos
836  false
837 #if defined (HAVE_QSCI_VERSION_2_6_0)
838  , true
839 #endif
840  );
841  }
842 
843  // restore the visible area of the file, the cursor position,
844  // and the selection
845  setFirstVisibleLine (first_line);
846  setCursorPosition (line, col);
847 
848  // Clear the selection.
850  }
851  else
852  {
853  // The idea here is to allow backspace to remove the last
854  // character of the replacement text to allow minimal editing
855  // and to also end the selection replacement action if text is
856  // not valid as a word constituent (control characters,
857  // etc.). Is there a better way than having special cases for
858  // DEL and ESC here?
859 
860  QString text = key_event->text ();
861 
862  bool cancel_replacement = false;
863 
864  if (key == Qt::Key_Backspace)
865  {
866  if (m_selection_replacement.isEmpty ())
867  cancel_replacement = true;
868  else
869  m_selection_replacement.chop (1);
870  }
871  else if (key == Qt::Key_Delete || key == Qt::Key_Escape)
872  cancel_replacement = true;
873  else if (! text.isEmpty ())
875  else if (modifiers != Qt::ShiftModifier)
876  cancel_replacement = true;
877 
878  // Perform default action.
879 
880  QsciScintilla::keyPressEvent (key_event);
881 
882  if (cancel_replacement)
884 
885  if (! m_selection_replacement.isEmpty ())
887  }
888  }
889  }
890 
891  void octave_qscintilla::auto_close (int auto_endif, int linenr,
892  const QString& line, QString& first_word)
893  {
894  // Insert and "end" for an "if" etc., if needed.
895  // (Use of "while" allows "return" to skip the rest.
896  // It may be clearer to use "if" and "goto",
897  // but that violates the coding standards.)
898 
899  bool autofill_simple_end = (auto_endif == 2);
900 
901  size_t start = line.toStdString ().find_first_not_of (" \t");
902 
903  // Check if following line has the same or less indentation
904  // Check if the following line does not start with
905  // end* (until) (catch)
906  if (linenr < lines () - 1)
907  {
908  int offset = 1;
909  size_t next_start;
910  QString next_line;
911  do // find next non-blank line
912  {
913  next_line = text (linenr + offset++);
914  next_start = next_line.toStdString ().find_first_not_of (" \t\n");
915  }
916  while (linenr + offset < lines ()
917  && next_start == std::string::npos);
918  if (next_start == std::string::npos)
919  next_start = 0;
920  if (next_start > start) // more indented => don't add "end"
921  return;
922  if (next_start == start) // same => check if already is "end"
923  {
924  QRegExp rx_start = QRegExp (R"((\w+))");
925  int tmp = rx_start.indexIn (next_line, start);
926  if (tmp != -1 && is_end (rx_start.cap(1), first_word))
927  return;
928  }
929  }
930 
931  // If all of the above, insert a new line, with matching indent
932  // and either 'end' or 'end...', depending on a flag.
933 
934  // If we insert directly after the last line, the "end" is autoindented,
935  // so add a dummy line.
936  if (linenr + 2 == lines ())
937  insertAt (QString ("\n"), linenr + 2, 0);
938 
939  // For try/catch/end, fill "end" first, so "catch" is top of undo stack
940  if (first_word == "try")
941  insertAt (QString (start, ' ')
942  + (autofill_simple_end ? "end\n" : "end_try_catch\n"),
943  linenr + 2, 0);
944  else if (first_word == "unwind_protect")
945  insertAt (QString (start, ' ')
946  + (autofill_simple_end ? "end\n" : "end_unwind_protect\n"),
947  linenr + 2, 0);
948 
949  QString next_line;
950  if (first_word == "do")
951  next_line = "until\n";
952  else if (first_word == "try")
953  next_line = "catch\n";
954  else if (first_word == "unwind_protect")
955  next_line = "unwind_protect_cleanup\n";
956  else if (autofill_simple_end)
957  next_line = "end\n";
958  else
959  {
960  if (first_word == "unwind_protect")
961  first_word = '_' + first_word;
962  next_line = "end" + first_word + "\n";
963  }
964 
965  insertAt (QString (start, ' ') + next_line, linenr + 2, 0);
966  }
967 
968  void octave_qscintilla::dragEnterEvent (QDragEnterEvent *e)
969  {
970  // if is not dragging a url, pass to qscintilla to handle,
971  // otherwise ignore it so that it will be handled by
972  // the parent
973  if (!e->mimeData ()->hasUrls ())
974  {
975  QsciScintilla::dragEnterEvent (e);
976  }
977  else
978  {
979  e->ignore();
980  }
981  }
982 }
983 
984 #endif
void execute_command_in_terminal_signal(const QString &)
const QStringList oct_comment_strings(QStringList()<< "##"<< "#"<< "%"<< "%%"<< "%!")
void status_update(bool, bool)
#define CTRL(x)
Definition: kpty.cpp:143
void set_word_selection(const QString &word=QString())
const int oct_comment_str_d
void get_global_textcursor_pos(QPoint *global_pos, QPoint *local_pos)
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
i e
Definition: data.cc:2591
octave_value arg
Definition: pr-output.cc:3244
calling an anonymous function involves an overhead quite comparable to the overhead of an m file function Passing a handle to a built in function is because the interpreter is not involved in the internal loop For a
Definition: cellfun.cc:400
void dragEnterEvent(QDragEnterEvent *e)
const QString oct_comment_str("editor/oct_comment_str")
void focusInEvent(QFocusEvent *focusEvent)
void get_current_position(int *pos, int *line, int *col)
QStringList comment_string(bool comment=true)
const int oct_uncomment_str_d
text(const graphics_handle &mh, const graphics_handle &p)
Definition: graphics.in.h:4541
double tmp
Definition: data.cc:6252
octave_value retval
Definition: data.cc:6246
const QString oct_comment_str_old("editor/octave_comment_string")
void create_context_menu_signal(QMenu *)
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
static bool is_end(const QString &candidate, const QString &opening)
octave::sys::time start
Definition: graphics.cc:12337
void cursor_position_changed(int, int)
p
Definition: lu.cc:138
const QString oct_uncomment_str("editor/oct_uncomment_str")
octave_idx_type length(void) const
Definition: ovl.h:96
void contextmenu_break_once(const QPoint &)
void context_menu_break_condition_signal(int)
void show_doc_signal(const QString &)
static QSettings * get_settings(void)
void smart_indent(bool do_smart_indent, int do_auto_close, int line)
for i
Definition: data.cc:5264
void auto_close(int auto_endif, int l, const QString &line, QString &first_word)
lexer(interpreter *interp=nullptr)
Definition: lex.h:758
void keyPressEvent(QKeyEvent *e)
const int oct_comment_strings_count
void smart_indent_line_or_selected_text(int lineFrom, int lineTo)
void set_selection_marker_color(const QColor &c)
void show_selection_markers(int line, int col, int len)
virtual void contextMenuEvent(QContextMenuEvent *e)
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 context_menu_edit_signal(const QString &)