GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
documentation.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2018-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <QAction>
31 #include <QApplication>
32 #include <QCompleter>
33 #include <QDesktopServices>
34 #include <QDir>
35 #include <QFile>
36 #include <QFileInfo>
37 #include <QHelpContentWidget>
38 #include <QHelpIndexWidget>
39 #if defined (HAVE_NEW_QHELPINDEXWIDGET_API) \
40  || defined (HAVE_QHELPENGINE_DOCUMENTSFORIDENTIFIER)
41 # include <QHelpLink>
42 #endif
43 #include <QHelpSearchEngine>
44 #include <QHelpSearchQueryWidget>
45 #include <QHelpSearchResultWidget>
46 #include <QLabel>
47 #include <QLineEdit>
48 #include <QMessageBox>
49 #include <QTabWidget>
50 #include <QVBoxLayout>
51 
52 #include "documentation.h"
53 #include "gui-preferences-global.h"
54 #include "gui-preferences-sc.h"
55 #include "octave-qobject.h"
56 #include "shortcut-manager.h"
57 
58 #include "defaults.h"
59 #include "file-ops.h"
60 #include "oct-env.h"
61 
62 namespace octave
63 {
64  // The documentation splitter, which is the main widget
65  // of the doc dock widget
67  : QSplitter (Qt::Horizontal, p),
68  m_octave_qobj (oct_qobj), m_doc_widget (p),
69  m_tool_bar (new QToolBar (p)),
70  m_query_string (QString ()),
71  m_prev_pages_menu (new QMenu (p)),
72  m_next_pages_menu (new QMenu (p)),
73  m_prev_pages_count (0),
74  m_next_pages_count (0),
75  m_findnext_shortcut (new QShortcut (p)),
76  m_findprev_shortcut (new QShortcut (p))
77  {
78  // Get original collection
79  QString collection = getenv ("OCTAVE_QTHELP_COLLECTION");
80  if (collection.isEmpty ())
83  + "octave_interpreter.qhc");
84 
85  // Setup the help engine with the original collection, use a writable copy
86  // of the original collection and load the help data
87  m_help_engine = new QHelpEngine (collection, this);
88 
89  // Mark help as readonly to avoid error if collection file is stored in a
90  // readonly location
91  m_help_engine->setProperty ("_q_readonly",
92  QVariant::fromValue<bool> (true));
93 
94  QString tmpdir = QDir::tempPath();
96  = QString::fromStdString (sys::tempnam (tmpdir.toStdString (),
97  "oct-qhelp-"));
98 
99  if (m_help_engine->copyCollectionFile (m_collection))
100  m_help_engine->setCollectionFile (m_collection);
101  else
102 #ifdef ENABLE_DOCS
103  // FIXME: Perhaps a better way to do this would be to keep a count
104  // in the GUI preferences file. After issuing this warning 3 times
105  // it would be disabled. The count would need to be reset when a new
106  // version of Octave is installed.
107  QMessageBox::warning (this, tr ("Octave Documentation"),
108  tr ("Could not copy help collection to temporary\n"
109  "file. Search capabilities may be affected.\n"
110  "%1").arg (m_help_engine->error ()));
111 #endif
112 
113  connect(m_help_engine, SIGNAL(setupFinished()),
114  m_help_engine->searchEngine(), SLOT(indexDocumentation()));
115 
116  if (! m_help_engine->setupData())
117  {
118 #ifdef ENABLE_DOCS
119  QMessageBox::warning (this, tr ("Octave Documentation"),
120  tr ("Could not setup the data required for the\n"
121  "documentation viewer. Only help texts in\n"
122  "the Command Window will be available."));
123 #endif
124 
125  disconnect (m_help_engine, 0, 0, 0);
126 
127  delete m_help_engine;
128  m_help_engine = nullptr;
129  }
130 
131  // The browser
132  QWidget *browser_find = new QWidget (this);
133  m_doc_browser = new documentation_browser (m_help_engine, browser_find);
134  connect (m_doc_browser, SIGNAL (cursorPositionChanged (void)),
135  this, SLOT(handle_cursor_position_change (void)));
136 
137  // Tool bar
139 
140  // Find bar
141  QWidget *find_footer = new QWidget (browser_find);
142  QLabel *find_label = new QLabel (tr ("Find:"), find_footer);
143  m_find_line_edit = new QLineEdit (find_footer);
144  connect (m_find_line_edit, SIGNAL (returnPressed (void)),
145  this, SLOT(find (void)));
146  connect (m_find_line_edit, SIGNAL (textEdited (const QString&)),
147  this, SLOT(find_forward_from_anchor (const QString&)));
148  QToolButton *forward_button = new QToolButton (find_footer);
149  forward_button->setText (tr ("Search forward"));
150  forward_button->setToolTip (tr ("Search forward"));
152  forward_button->setIcon (rmgr.icon ("go-down"));
153  connect (forward_button, SIGNAL (pressed (void)),
154  this, SLOT(find (void)));
155  QToolButton *backward_button = new QToolButton (find_footer);
156  backward_button->setText (tr ("Search backward"));
157  backward_button->setToolTip (tr ("Search backward"));
158  backward_button->setIcon (rmgr.icon ("go-up"));
159  connect (backward_button, SIGNAL (pressed (void)),
160  this, SLOT(find_backward (void)));
161  QHBoxLayout *h_box_find_footer = new QHBoxLayout (find_footer);
162  h_box_find_footer->addWidget (find_label);
163  h_box_find_footer->addWidget (m_find_line_edit);
164  h_box_find_footer->addWidget (forward_button);
165  h_box_find_footer->addWidget (backward_button);
166  h_box_find_footer->setMargin (2);
167  find_footer->setLayout (h_box_find_footer);
168 
169  QVBoxLayout *v_box_browser_find = new QVBoxLayout (browser_find);
170  v_box_browser_find->addWidget (m_tool_bar);
171  v_box_browser_find->addWidget (m_doc_browser);
172  v_box_browser_find->addWidget (find_footer);
173  browser_find->setLayout (v_box_browser_find);
174 
175  notice_settings (rmgr.get_settings ());
176 
177  m_findnext_shortcut->setContext (Qt::WidgetWithChildrenShortcut);
178  connect (m_findnext_shortcut, SIGNAL (activated (void)),
179  this, SLOT(find (void)));
180  m_findprev_shortcut->setContext (Qt::WidgetWithChildrenShortcut);
181  connect (m_findprev_shortcut, SIGNAL (activated (void)),
182  this, SLOT(find_backward (void)));
183 
184  find_footer->hide ();
186 
187  if (m_help_engine)
188  {
189 #if defined (HAVE_NEW_QHELPINDEXWIDGET_API)
190  // Starting in Qt 5.15, help engine uses filters instead of old API
191  m_help_engine->setUsesFilterEngine (true);
192 #endif
193  // Layout contents, index and search
194  QTabWidget *navi = new QTabWidget (this);
195  navi->setTabsClosable (false);
196  navi->setMovable (true);
197 
198  // Contents
199  QHelpContentWidget *content = m_help_engine->contentWidget ();
200  content->setObjectName ("documentation_tab_contents");
201  navi->addTab (content, tr ("Contents"));
202 
203  connect(m_help_engine->contentWidget (),
204  SIGNAL (linkActivated (const QUrl&)),
205  m_doc_browser, SLOT(handle_index_clicked (const QUrl&)));
206 
207  // Index
208  QHelpIndexWidget *index = m_help_engine->indexWidget ();
209 
210  m_filter = new QComboBox (this);
211  m_filter->setToolTip (tr ("Enter text to search the indices"));
212  m_filter->setEditable (true);
213  m_filter->setInsertPolicy (QComboBox::NoInsert);
214  m_filter->setMaxCount (10);
215  m_filter->setMaxVisibleItems (10);
216  m_filter->setSizeAdjustPolicy (QComboBox::AdjustToMinimumContentsLengthWithIcon);
217  QSizePolicy sizePol (QSizePolicy::Expanding, QSizePolicy::Preferred);
218  m_filter->setSizePolicy (sizePol);
219  m_filter->completer ()->setCaseSensitivity (Qt::CaseSensitive);
220  QLabel *filter_label = new QLabel (tr ("Search"));
221 
222  QWidget *filter_all = new QWidget (navi);
223  QHBoxLayout *h_box_index = new QHBoxLayout (filter_all);
224  h_box_index->addWidget (filter_label);
225  h_box_index->addWidget (m_filter);
226  h_box_index->setMargin (2);
227  filter_all->setLayout (h_box_index);
228 
229  QWidget *index_all = new QWidget (navi);
230  index_all->setObjectName ("documentation_tab_index");
231  QVBoxLayout *v_box_index = new QVBoxLayout (index_all);
232  v_box_index->addWidget (filter_all);
233  v_box_index->addWidget (index);
234  index_all->setLayout (v_box_index);
235 
236  navi->addTab (index_all, tr ("Function Index"));
237 
238 #if defined (HAVE_NEW_QHELPINDEXWIDGET_API)
239  connect (m_help_engine->indexWidget (),
240  &QHelpIndexWidget::documentActivated,
241  this, [this](const QHelpLink &link) {
242  m_doc_browser->handle_index_clicked (link.url);});
243 #else
244  connect (m_help_engine->indexWidget (),
245  SIGNAL (linkActivated (const QUrl&, const QString&)),
246  m_doc_browser, SLOT(handle_index_clicked (const QUrl&,
247  const QString&)));
248 #endif
249 
250  connect (m_filter, SIGNAL (editTextChanged (const QString&)),
251  this, SLOT(filter_update (const QString&)));
252 
253  connect (m_filter->lineEdit (), SIGNAL (editingFinished (void)),
254  this, SLOT(filter_update_history (void)));
255 
256  // Search
257  QHelpSearchEngine *search_engine = m_help_engine->searchEngine ();
258  QHelpSearchQueryWidget *search = search_engine->queryWidget ();
259  QHelpSearchResultWidget *result = search_engine->resultWidget ();
260  QWidget *search_all = new QWidget (navi);
261  QVBoxLayout *v_box_search = new QVBoxLayout (search_all);
262  v_box_search->addWidget (search);
263  v_box_search->addWidget (result);
264  search_all->setLayout (v_box_search);
265  search_all->setObjectName ("documentation_tab_search");
266  navi->addTab (search_all, tr ("Search"));
267 
268  connect (search, SIGNAL (search (void)),
269  this, SLOT(global_search (void)));
270 
271  connect (search_engine, SIGNAL (searchingStarted (void)),
272  this, SLOT(global_search_started (void)));
273  connect (search_engine, SIGNAL (searchingFinished (int)),
274  this, SLOT(global_search_finished (int)));
275 
276  connect (search_engine->resultWidget (),
277  SIGNAL (requestShowLink (const QUrl&)),
278  this,
279  SLOT(handle_search_result_clicked (const QUrl&)));
280 
281  // Fill the splitter
282  insertWidget (0, navi);
283  insertWidget (1, browser_find);
284  setStretchFactor (1, 1);
285  }
286 
287  // Initial view: Contents
288  m_doc_browser->setSource
289  (QUrl ("qthelp://org.octave.interpreter-1.0/doc/octave.html/index.html"));
290  }
291 
293  {
294  if (m_help_engine)
295  delete m_help_engine;
296 
297  // Cleanup temporary file and directory
298  QFile file (m_collection);
299  if (file.exists ())
300  {
301  QFileInfo finfo (file);
302  QString bname = finfo.fileName ();
303  QDir dir = finfo.absoluteDir ();
304  dir.setFilter (QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden);
305  QStringList namefilter;
306  namefilter.append ("*" + bname + "*");
307  for (const auto& fi : dir.entryInfoList (namefilter))
308  {
309  std::string file_name = fi.absoluteFilePath ().toStdString ();
310  sys::recursive_rmdir (file_name);
311  }
312 
313  file.remove();
314  }
315  }
316 
317  QAction * documentation::add_action (const QIcon& icon, const QString& text,
318  const char *member, QWidget *receiver,
319  QToolBar *tool_bar)
320  {
321  QAction *a;
322  QWidget *r = this;
323  if (receiver != nullptr)
324  r = receiver;
325 
326  a = new QAction (icon, text, this);
327  connect (a, SIGNAL (triggered ()), r, member);
328 
329  if (tool_bar)
330  tool_bar->addAction (a);
331 
332  m_doc_widget->addAction (a); // important for shortcut context
333  a->setShortcutContext (Qt::WidgetWithChildrenShortcut);
334 
335  return a;
336  }
337 
339  {
340  // Home, Previous, Next
343  = add_action (rmgr.icon ("go-home"), tr ("Go home"), SLOT (home (void)),
346  = add_action (rmgr.icon ("go-previous"), tr ("Go back"),
347  SLOT (backward (void)), m_doc_browser, m_tool_bar);
348  m_action_go_prev->setEnabled (false);
349 
350  // popdown menu with prev pages files
351  QToolButton *popdown_button_prev_pages = new QToolButton ();
352  popdown_button_prev_pages->setToolTip (tr ("Previous pages"));
353  popdown_button_prev_pages->setMenu (m_prev_pages_menu);
354  popdown_button_prev_pages->setPopupMode (QToolButton::InstantPopup);
355  popdown_button_prev_pages->setToolButtonStyle (Qt::ToolButtonTextOnly);
356  popdown_button_prev_pages->setCheckable (false);
357  popdown_button_prev_pages->setArrowType(Qt::DownArrow);
358  m_tool_bar->addWidget (popdown_button_prev_pages);
360  = add_action (rmgr.icon ("go-next"), tr ("Go forward"),
361  SLOT (forward (void)), m_doc_browser, m_tool_bar);
362  m_action_go_next->setEnabled (false);
363 
364  // popdown menu with prev pages files
365  QToolButton *popdown_button_next_pages = new QToolButton ();
366  popdown_button_next_pages->setToolTip (tr ("Next pages"));
367  popdown_button_next_pages->setMenu (m_next_pages_menu);
368  popdown_button_next_pages->setPopupMode (QToolButton::InstantPopup);
369  popdown_button_next_pages->setToolButtonStyle (Qt::ToolButtonTextOnly);
370  popdown_button_next_pages->setArrowType(Qt::DownArrow);
371  m_tool_bar->addWidget (popdown_button_next_pages);
372 
373  connect (m_doc_browser, SIGNAL (backwardAvailable (bool)),
374  m_action_go_prev, SLOT (setEnabled (bool)));
375  connect (m_doc_browser, SIGNAL (backwardAvailable (bool)),
376  popdown_button_prev_pages, SLOT (setEnabled (bool)));
377  connect (m_doc_browser, SIGNAL (forwardAvailable (bool)),
378  m_action_go_next, SLOT (setEnabled (bool)));
379  connect (m_doc_browser, SIGNAL (forwardAvailable (bool)),
380  popdown_button_next_pages, SLOT (setEnabled (bool)));
381  connect (m_doc_browser, SIGNAL (historyChanged (void)),
382  this, SLOT (update_history_menus (void)));
383 
384  // Init prev/next menus
385  for (int i = 0; i < max_history_entries; ++i)
386  {
387  m_prev_pages_actions[i] = new QAction (this);
388  m_prev_pages_actions[i]->setVisible (false);
389  m_next_pages_actions[i] = new QAction (this);
390  m_next_pages_actions[i]->setVisible (false);
391  m_prev_pages_menu->addAction (m_prev_pages_actions[i]);
392  m_next_pages_menu->addAction (m_next_pages_actions[i]);
393  }
394 
395  connect (m_prev_pages_menu, SIGNAL (triggered (QAction *)),
396  this, SLOT (open_hist_url (QAction *)));
397  connect (m_next_pages_menu, SIGNAL (triggered (QAction *)),
398  this, SLOT (open_hist_url (QAction *)));
399 
400  // Find
401  m_tool_bar->addSeparator ();
403  = add_action (rmgr.icon ("edit-find"), tr ("Find"),
404  SLOT (activate_find (void)), this, m_tool_bar);
405 
406  // Zoom
407  m_tool_bar->addSeparator ();
409  = add_action (rmgr.icon ("zoom-in"), tr ("Zoom in"),
410  SLOT (zoom_in (void)), m_doc_browser, m_tool_bar);
412  = add_action (rmgr.icon ("zoom-out"), tr ("Zoom out"),
413  SLOT (zoom_out (void)), m_doc_browser, m_tool_bar);
415  = add_action (rmgr.icon ("zoom-original"), tr ("Zoom original"),
416  SLOT (zoom_original (void)), m_doc_browser, m_tool_bar);
417  }
418 
420  {
421  if (! m_help_engine)
422  return;
423 
424  QString query_string;
425 #if defined (HAVE_QHELPSEARCHQUERYWIDGET_SEARCHINPUT)
426  QString queries
427  = m_help_engine->searchEngine ()->queryWidget ()->searchInput ();
428  query_string = queries;
429 #else
430  // FIXME: drop this part when support for Qt4 is dropped
432  = m_help_engine->searchEngine ()->queryWidget ()->query ();
433  if (queries.count ())
434  query_string = queries.first ().wordList.join (" ");
435  else
436  query_string = "";
437 #endif
438 
439  if (query_string.isEmpty ())
440  return;
441 
442  // Get quoted search strings first, then take first string as fall back
443  QRegExp rx ("\"([^\"]*)\"");
444  if (rx.indexIn (query_string, 0) != -1)
445  m_internal_search = rx.cap (1);
446  else
447 #if defined (HAVE_QT_SPLITBEHAVIOR_ENUM)
448  m_internal_search = query_string.split (" ", Qt::SkipEmptyParts).first ();
449 #else
450  m_internal_search = query_string.split (" ", QString::SkipEmptyParts).first ();
451 #endif
452 
453  m_help_engine->searchEngine ()->search (queries);
454  }
455 
457  {
458  qApp->setOverrideCursor(QCursor(Qt::WaitCursor));
459  }
460 
462  {
463  if (! m_help_engine)
464  return;
465 
466  if (! m_internal_search.isEmpty ())
467  {
469 
470  QHelpSearchEngine *search_engine = m_help_engine->searchEngine ();
471  if (search_engine)
472  {
473 #if defined (HAVE_QHELPSEARCHQUERYWIDGET_SEARCHINPUT)
474  QVector<QHelpSearchResult> res
475  = search_engine->searchResults (0, search_engine->searchResultCount ());
476 #else
478  = search_engine->hits (0, search_engine->hitCount ());
479 #endif
480 
481  if (res.count ())
482  {
483  QUrl url;
484 
485  if (res.count () == 1)
486 #if defined (HAVE_QHELPSEARCHQUERYWIDGET_SEARCHINPUT)
487  url = res.front ().url ();
488 #else
489  url = res.front ().first;
490 #endif
491  else
492  {
493  // Remove the quotes we added
494  QString search_string = m_internal_search;
495 
496  for (auto r = res.begin (); r != res.end (); r++)
497  {
498 #if defined (HAVE_QHELPSEARCHQUERYWIDGET_SEARCHINPUT)
499  QString title = r->title ().toLower ();
500  QUrl tmpurl = r->url ();
501 #else
502  QString title = r->second.toLower ();
503  QUrl tmpurl = r->first;
504 #endif
505  if (title.contains (search_string.toLower ()))
506  {
507  if (title.indexOf (search_string.toLower ()) == 0)
508  {
509  url = tmpurl;
510  break;
511  }
512  else if (url.isEmpty ())
513  url = tmpurl;
514  }
515  }
516  }
517 
518  if (! url.isEmpty ())
519  {
520  connect (this, SIGNAL (show_single_result (const QUrl&)),
521  this,
522  SLOT (handle_search_result_clicked (const QUrl&)));
523 
524  emit show_single_result (url);
525  }
526  }
527  }
528 
529  m_internal_search = QString ();
530  }
531 
532  qApp->restoreOverrideCursor();
533  }
534 
536  {
537  // Open url with matching text
539 
540  // Select all occurrences of matching text
542 
543  // Open search widget with matching text as search string
544  m_find_line_edit->setText (m_query_string);
545  m_find_line_edit->parentWidget ()->show ();
546 
547  // If no occurrence can be found go to the top of the page
548  if (! m_doc_browser->find (m_find_line_edit->text ()))
549  m_doc_browser->moveCursor (QTextCursor::Start);
550  else
551  {
552  // Go to to first occurrence of search text. Going to the end and then
553  // search backwards until the last occurrence ensures the search text
554  // is visible in the first line of the visible part of the text.
555  m_doc_browser->moveCursor (QTextCursor::End);
556  while (m_doc_browser->find (m_find_line_edit->text (),
557  QTextDocument::FindBackward));
558  }
559  }
560 
562  {
563  // Get highlight background and text color
564  QPalette pal = QApplication::palette ();
565  QTextCharFormat format;
566  QColor col = pal.color (QPalette::Highlight);
567  col.setAlphaF (0.25);
568  format.setBackground (QBrush (col));
569  format.setForeground (QBrush (pal.color (QPalette::Text)));
570 
571  // Create list for extra selected items
573  m_doc_browser->moveCursor (QTextCursor::Start);
574 
575  // Find all occurrences and add them to the selection
576  while ( m_doc_browser->find (text) )
577  {
578  QTextEdit::ExtraSelection selected_item;
579  selected_item.cursor = m_doc_browser->textCursor ();
580  selected_item.format = format;
581  selected.append (selected_item);
582  }
583 
584  // Apply selection and move back to the beginning
585  m_doc_browser->setExtraSelections (selected);
586  m_doc_browser->moveCursor (QTextCursor::Start);
587  }
588 
590  {
591  // If m_help_engine is not defined, the objects accessed by this method
592  // are not valid. Thus, just return in this case.
593  if (! m_help_engine)
594  return;
595 
596  // Icon size in the toolbar.
597  int size_idx = settings->value (global_icon_size).toInt ();
598  size_idx = (size_idx > 0) - (size_idx < 0) + 1; // Make valid index from 0 to 2
599 
600  QStyle *st = style ();
601  int icon_size = st->pixelMetric (global_icon_sizes[size_idx]);
602  m_tool_bar->setIconSize (QSize (icon_size, icon_size));
603 
604  // Shortcuts
606 
616  }
617 
619  {
620  if (m_doc_browser->hasFocus ())
621  {
622  m_doc_browser->copy();
623  }
624  }
625 
627 
628  void documentation::selectAll (void) { }
629 
630  void documentation::load_ref (const QString& ref_name)
631  {
632  if (! m_help_engine || ref_name.isEmpty ())
633  return;
634 
635 #if defined (HAVE_QHELPENGINE_DOCUMENTSFORIDENTIFIER)
636  QList<QHelpLink> found_links
637  = m_help_engine->documentsForIdentifier (ref_name);
638 #else
639  QMap<QString, QUrl> found_links
640  = m_help_engine->linksForIdentifier (ref_name);
641 #endif
642 
643  QTabWidget *navi = static_cast<QTabWidget*> (widget (0));
644 
645  if (found_links.count() > 0)
646  {
647  // First search in the function index
648 #if defined (HAVE_QHELPENGINE_DOCUMENTSFORIDENTIFIER)
649  QUrl first_url = found_links.constFirst().url;
650 #else
651  QUrl first_url = found_links.constBegin().value ();
652 #endif
653 
654  m_doc_browser->setSource (first_url);
655 
656  // Switch to function index tab
657  m_help_engine->indexWidget()->filterIndices (ref_name);
658  QWidget *index_tab
659  = navi->findChild<QWidget*> ("documentation_tab_index");
660  navi->setCurrentWidget (index_tab);
661  }
662  else
663  {
664  // Use full text search to provide the best match
665  QHelpSearchEngine *search_engine = m_help_engine->searchEngine ();
666  QHelpSearchQueryWidget *search_query = search_engine->queryWidget ();
667 
668 #if defined (HAVE_QHELPSEARCHQUERYWIDGET_SEARCHINPUT)
669  QString query = ref_name;
670  query.prepend ("\"").append ("\"");
671 #else
673  query << QHelpSearchQuery (QHelpSearchQuery::DEFAULT,
674  QStringList (QString("\"") + ref_name + QString("\"")));
675 #endif
676  m_internal_search = ref_name;
677  search_engine->search (query);
678 
679  // Switch to search tab
680 #if defined (HAVE_QHELPSEARCHQUERYWIDGET_SEARCHINPUT)
681  search_query->setSearchInput (query);
682 #else
683  search_query->setQuery (query);
684 #endif
685  QWidget *search_tab
686  = navi->findChild<QWidget*> ("documentation_tab_search");
687  navi->setCurrentWidget (search_tab);
688  }
689  }
690 
692  {
693  if (m_find_line_edit->parentWidget ()->isVisible ())
694  {
695  m_find_line_edit->parentWidget ()->hide ();
696  m_doc_browser->setFocus ();
697  }
698  else
699  {
700  m_find_line_edit->parentWidget ()->show ();
701  m_find_line_edit->selectAll ();
702  m_find_line_edit->setFocus ();
703  }
704  }
705 
706  void documentation::filter_update (const QString& expression)
707  {
708  if (! m_help_engine)
709  return;
710 
711  QString wildcard;
712  if (expression.contains (QLatin1Char('*')))
713  wildcard = expression;
714 
715  m_help_engine->indexWidget ()->filterIndices(expression, wildcard);
716  }
717 
719  {
720  QString text = m_filter->currentText (); // get current text
721  int index = m_filter->findText (text); // and its actual index
722 
723  if (index > -1)
724  m_filter->removeItem (index); // remove if already existing
725 
726  m_filter->insertItem (0, text); // (re)insert at beginning
727  m_filter->setCurrentIndex (0);
728  }
729 
731  {
732  find (true);
733  }
734 
735  void documentation::find (bool backward)
736  {
737  if (! m_help_engine)
738  return;
739 
740  QTextDocument::FindFlags find_flags;
741  if (backward)
742  find_flags = QTextDocument::FindBackward;
743 
744  if (! m_doc_browser->find (m_find_line_edit->text (), find_flags))
745  {
746  // Nothing was found, restart search from the begin or end of text
747  QTextCursor textcur = m_doc_browser->textCursor ();
748  if (backward)
749  textcur.movePosition (QTextCursor::End);
750  else
751  textcur.movePosition (QTextCursor::Start);
752  m_doc_browser->setTextCursor (textcur);
753  m_doc_browser->find (m_find_line_edit->text (), find_flags);
754  }
755 
757  }
758 
760  {
761  if (! m_help_engine)
762  return;
763 
764  // Search from the current position
765  QTextCursor textcur = m_doc_browser->textCursor ();
766  textcur.setPosition (m_search_anchor_position);
767  m_doc_browser->setTextCursor (textcur);
768 
769  if (! m_doc_browser->find (text))
770  {
771  // Nothing was found, restart search from the beginning
772  textcur.movePosition (QTextCursor::Start);
773  m_doc_browser->setTextCursor (textcur);
774  m_doc_browser->find (text);
775  }
776  }
777 
779  {
780  if (! m_help_engine)
781  return;
782 
783  m_search_anchor_position = m_doc_browser->textCursor ().position ();
784  }
785 
787  {
788  if (! m_help_engine)
789  return;
790 
791  if (m_doc_browser->hasFocus ())
793  }
794 
795  void documentation::registerDoc (const QString& qch)
796  {
797  if (m_help_engine)
798  {
799  QString ns = m_help_engine->namespaceName (qch);
800  bool do_setup = true;
801  if (m_help_engine->registeredDocumentations ().contains (ns))
802  {
803  if (m_help_engine->documentationFileName (ns) == qch)
804  do_setup = false;
805  else
806  {
807  m_help_engine->unregisterDocumentation (ns);
808  m_help_engine->registerDocumentation (qch);
809  }
810  }
811  else if (! m_help_engine->registerDocumentation (qch))
812  {
813  QMessageBox::warning (this, tr ("Octave Documentation"),
814  tr ("Unable to register help file %1.").
815  arg (qch));
816  return;
817  }
818 
819  if (do_setup)
820  m_help_engine->setupData();
821  }
822  }
823 
824  void documentation::unregisterDoc (const QString& qch)
825  {
826  if (! m_help_engine)
827  return;
828 
829  QString ns = m_help_engine->namespaceName (qch);
830  if (m_help_engine
831  && m_help_engine->registeredDocumentations ().contains (ns)
832  && m_help_engine->documentationFileName (ns) == qch)
833  {
834  m_help_engine->unregisterDocumentation (ns);
835  m_help_engine->setupData ();
836  }
837  }
838 
840  {
841  if (m_prev_pages_count != m_doc_browser->backwardHistoryCount ())
842  {
843  update_history (m_doc_browser->backwardHistoryCount (),
845  m_prev_pages_count = m_doc_browser->backwardHistoryCount ();
846  }
847 
848  if (m_next_pages_count != m_doc_browser->forwardHistoryCount ())
849  {
850  update_history (m_doc_browser->forwardHistoryCount (),
852  m_next_pages_count = m_doc_browser->forwardHistoryCount ();
853  }
854  }
855 
856  void documentation::update_history (int new_count, QAction **actions)
857  {
858  // Which menu has to be updated?
859  int prev_next = -1;
860  QAction *a = m_action_go_prev;
861  if (actions == m_next_pages_actions)
862  {
863  prev_next = 1;
864  a = m_action_go_next;
865  }
866 
867  // Get maximal count limited by array size
868  int count = qMin (new_count, int (max_history_entries));
869 
870  // Fill used menu entries
871  for (int i = 0; i < count; i++)
872  {
873  QString title = m_doc_browser->historyTitle (prev_next*(i+1));
874  title.remove (QRegExp ("\\s*\\(*GNU Octave \\(version [^\\)]*\\)[: \\)]*"));
875 
876  // Since the title only contains the section name and not the
877  // specific anchor, extract the latter from the url and append
878  // it to the title
879  QString url = m_doc_browser->historyUrl (prev_next*(i+1)).toString ();
880  if (url.contains ('#'))
881  {
882  // Get the anchor from the url
883  QString anchor = url.split ('#').last ();
884 
885  // Remove internal string parts
886  anchor.remove (QRegExp ("^index-"));
887  anchor.remove (QRegExp ("^SEC_"));
888  anchor.remove (QRegExp ("^XREF"));
889  anchor.remove ("Concept-Index_cp_letter-");
890  anchor.replace ("-"," ");
891 
892  // replace encoded special chars by their unencoded versions
893  QRegExp rx = QRegExp ("_00([0-7][0-9a-f])");
894  int pos = 0;
895  while ((pos = rx.indexIn(anchor, pos)) != -1)
896  {
897  anchor.replace ("_00"+rx.cap (1), QChar (rx.cap (1).toInt (nullptr,16)));
898  pos += rx.matchedLength();
899  }
900 
901  if (title != anchor)
902  title = title + ": " + anchor;
903  }
904 
905  if (i == 0)
906  a->setText (title); // set tool tip for prev/next buttons
907 
908  actions[i]->setText (title);
909  actions[i]->setData (m_doc_browser->historyUrl (prev_next*(i+1)));
910  actions[i]->setEnabled (true);
911  actions[i]->setVisible (true);
912  }
913 
914  // Hide unused menu entries
915  for (int j = count; j < max_history_entries; j++)
916  {
917  actions[j]->setEnabled (false);
918  actions[j]->setVisible (false);
919  }
920  }
921 
923  {
924  m_doc_browser->setSource (a->data ().toUrl ());
925  }
926 
927 
928  // The documentation browser
930  : QTextBrowser (p), m_help_engine (he), m_zoom_level (0)
931  {
932  setOpenLinks (false);
933  connect (this, SIGNAL (anchorClicked (QUrl)),
934  this, SLOT (handle_index_clicked (QUrl)));
935  }
936 
938  { }
939 
941  const QString&)
942  {
943  if (url.scheme () == "qthelp")
944  setSource (url);
945  else
946  QDesktopServices::openUrl (url);
947  }
948 
950  { }
951 
952  QVariant documentation_browser::loadResource (int type, const QUrl &url)
953  {
954  if (m_help_engine && url.scheme () == "qthelp")
955  return QVariant (m_help_engine->fileData(url));
956  else
957  return QTextBrowser::loadResource(type, url);
958  }
959 
961  {
963  {
964  zoomIn ();
965  m_zoom_level++;
966  }
967  }
968 
970  {
972  {
973  zoomOut ();
974  m_zoom_level--;
975  }
976  }
977 
979  {
980  zoomIn (- m_zoom_level);
981  m_zoom_level = 0;
982  }
983 
985  {
986  if (we->modifiers () == Qt::ControlModifier)
987  {
988 #if defined (HAVE_QWHEELEVENT_ANGLEDELTA)
989  if (we->angleDelta().y () > 0)
990 #else
991  if (we->delta() > 0)
992 #endif
993  zoom_in ();
994  else
995  zoom_out ();
996 
997  we->accept ();
998  }
999  else
1000  QTextEdit::wheelEvent (we);
1001  }
1002 
1003 }
Base class for Octave interfaces that use Qt.
resource_manager & get_resource_manager(void)
shortcut_manager & get_shortcut_manager(void)
Documentation browser derived from Textbrowser.
Definition: documentation.h:48
void zoom_original(void)
Zooming in and out while taking care of the zoom level.
void zoom_out(void)
Zooming in and out while taking care of the zoom level.
int m_zoom_level
Store the current zoom level.
Definition: documentation.h:80
void handle_index_clicked(const QUrl &url, const QString &keyword=QString())
void zoom_in(void)
Zooming in and out while taking care of the zoom level.
void notice_settings(const gui_settings *settings)
documentation_browser(QHelpEngine *help_engine, QWidget *parent=nullptr)
virtual QVariant loadResource(int type, const QUrl &url)
void wheelEvent(QWheelEvent *we)
void filter_update_history(void)
documentation(QWidget *parent, base_qobject &oct_qobj)
void filter_update(const QString &expression)
QShortcut * m_findprev_shortcut
void find_forward_from_anchor(const QString &text)
void global_search_started(void)
QHelpEngine * m_help_engine
QAction * m_prev_pages_actions[max_history_entries]
QAction * m_next_pages_actions[max_history_entries]
documentation_browser * m_doc_browser
void construct_tool_bar(void)
base_qobject & m_octave_qobj
void handle_search_result_clicked(const QUrl &url)
QShortcut * m_findnext_shortcut
void update_history_menus(void)
void handle_cursor_position_change(void)
void record_anchor_position(void)
QAction * m_action_zoom_original
void load_ref(const QString &name)
QAction * add_action(const QIcon &icon, const QString &text, const char *member, QWidget *receiver=nullptr, QToolBar *tool_bar=nullptr)
void open_hist_url(QAction *a)
void update_history(int new_count, QAction **actions)
void global_search_finished(int hits)
void select_all_occurrences(const QString &text)
Select all occurrences of a string in the doc browser.
void find(bool backward=false)
void show_single_result(const QUrl &)
void notice_settings(const gui_settings *settings)
QLineEdit * m_find_line_edit
void registerDoc(const QString &name)
void unregisterDoc(const QString &name)
gui_settings * get_settings(void) const
QIcon icon(const QString &icon_name, bool fallback=true)
void shortcut(QShortcut *sc, const sc_pref &scpref)
void set_shortcut(QAction *action, const sc_pref &scpref)
static octave_idx_type link(octave_idx_type s, octave_idx_type t, octave_idx_type *pp)
Definition: colamd.cc:97
void warning(const char *fmt,...)
Definition: error.cc:1050
const QStyle::PixelMetric global_icon_sizes[3]
const gui_pref global_icon_size("toolbar_icon_size", QVariant(0))
const sc_pref sc_doc_go_home(sc_doc+":go_home", Qt::AltModifier+Qt::Key_Home)
const sc_pref sc_doc_go_back(sc_doc+":go_back", QKeySequence::Back)
const sc_pref sc_edit_view_zoom_normal(sc_edit_view_zoom+"_normal", CTRL+Qt::Key_Period)
const sc_pref sc_edit_edit_find_replace(sc_edit_edit_find+"_replace", QKeySequence::Find)
const sc_pref sc_edit_edit_find_next(sc_edit_edit_find+"_next", QKeySequence::FindNext)
const sc_pref sc_edit_edit_find_previous(sc_edit_edit_find+"_previous", QKeySequence::FindPrevious)
const sc_pref sc_edit_view_zoom_in(sc_edit_view_zoom+"_in", QKeySequence::ZoomIn)
const sc_pref sc_doc_go_next(sc_doc+":go_next", QKeySequence::Forward)
const sc_pref sc_edit_view_zoom_out(sc_edit_view_zoom+"_out", QKeySequence::ZoomOut)
static std::list< std::string > search(const std::string &path, const std::string &original_name, bool all)
Definition: kpse.cc:486
T * r
Definition: mx-inlines.cc:773
QString fromStdString(const std::string &s)
std::string oct_doc_dir(void)
Definition: defaults.cc:331
std::string dir_sep_str(void)
Definition: file-ops.cc:243
std::string tempnam(const std::string &dir, const std::string &pfx)
Definition: file-ops.cc:646
int recursive_rmdir(const std::string &name)
Definition: file-ops.cc:557
static double we[256]
Definition: randmtzig.cc:449
size_t format(std::ostream &os, const char *fmt,...)
Definition: utils.cc:1329
static double fi[256]
Definition: randmtzig.cc:447