GNU Octave  8.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
documentation-bookmarks.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2018-2023 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <QCompleter>
31 #include <QMenu>
32 #include <QShortcut>
33 #include <QVBoxLayout>
34 #include <QWidget>
35 
36 #include "documentation.h"
38 
39 #include "gui-settings.h"
40 #include "gui-preferences-global.h"
41 #include "gui-preferences-dc.h"
42 #include "gui-preferences-sc.h"
43 #include "octave-qtutils.h"
44 #include "shortcut-manager.h"
45 
46 #include "defaults.h"
47 #include "file-ops.h"
48 #include "oct-env.h"
49 
51 
53  documentation *doc, documentation_browser *browser,
54  base_qobject& oct_qobj, QWidget *p)
55 : QWidget (p),
56  m_doc (doc), m_browser (browser), m_octave_qobj (oct_qobj),
57  m_ctx_menu_item (nullptr)
58 {
59  setObjectName ("documentation_tab_bookmarks");
60 
61  resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
63 
64  // Setup the tree view with the bookmarks
65  m_tree = new QTreeWidget (p);
66 
67  m_tree->setContextMenuPolicy (Qt::CustomContextMenu);
68  m_tree->setSelectionMode (QAbstractItemView::ExtendedSelection);
69  m_tree->setSortingEnabled (false);
70  m_tree->setDragEnabled(true);
71  m_tree->viewport()->setAcceptDrops(true);
72  m_tree->setDropIndicatorShown(true);
73  m_tree->setDragDropMode(QAbstractItemView::InternalMove);
74  m_tree->setColumnCount (1);
75  m_tree->setHeaderHidden (true);
76  m_tree->setEditTriggers (QAbstractItemView::EditKeyPressed
77  | QAbstractItemView::SelectedClicked);
78 
79  connect (m_tree, &QTreeWidget::customContextMenuRequested,
81  connect (m_tree, &QTreeWidget::itemDoubleClicked,
83 
84  // Define the icons for the tree view
85  icon_folder.addPixmap (style ()->standardPixmap(QStyle::SP_DirClosedIcon),
86  QIcon::Normal, QIcon::Off);
87  icon_folder.addPixmap (style ()->standardPixmap(QStyle::SP_DirOpenIcon),
88  QIcon::Normal, QIcon::On);
89  icon_bookmark.addPixmap (style ()->standardPixmap(QStyle::SP_FileIcon));
90 
91  // Setup and read the bookmarkfile
92  QFileInfo f (settings->fileName ());
93  QString f_path = f.absolutePath ();
94  f.setFile (QDir (f_path), dc_bookmark_file);
95  m_xbel_file.setFileName (f.absoluteFilePath ());
96 
97  if (m_xbel_file.exists ())
98  {
99  QString err = read_bookmarks ();
100  if ( !err.isEmpty ())
101  {
102  err.append (tr ("\nNo documentation bookmarks loaded!"));
103  QMessageBox::warning (this,
104  tr ("Octave: Loading Documentation Bookmarks"),
105  err);
106  m_xbel_file.close ();
107  }
108  }
109 
110  // Setup the filter widget
111  m_filter_widget = new QWidget (p);
112  m_filter = new QComboBox (m_filter_widget);
113 
114  m_filter->setToolTip (tr ("Enter text to search the bookmarks"));
115  m_filter->setEditable (true);
116  m_filter->setInsertPolicy (QComboBox::NoInsert);
117  m_filter->setMaxCount (10);
118  m_filter->setMaxVisibleItems (10);
119  m_filter->setSizeAdjustPolicy (QComboBox::AdjustToMinimumContentsLengthWithIcon);
120  QSizePolicy size_pol (QSizePolicy::Expanding, QSizePolicy::Preferred);
121  m_filter->setSizePolicy (size_pol);
122  m_filter->completer ()->setCaseSensitivity (Qt::CaseSensitive);
123 
124  m_filter->addItems (settings->value (dc_bookmark_filter_mru).toStringList ());
125 
126  connect (m_filter, &QComboBox::editTextChanged,
128  connect (m_filter->lineEdit (), &QLineEdit::editingFinished,
130 
131  m_filter_checkbox = new QCheckBox (m_filter_widget);
132  bool filter_state = settings->value (dc_bookmark_filter_active).toBool ();
133  m_filter_checkbox->setChecked (filter_state);
134  filter_activate (filter_state);
135 
136  connect (m_filter_checkbox, &QCheckBox::toggled,
138 
139  QLabel *filter_label = new QLabel (tr ("Filter"), m_filter_widget);
140  QHBoxLayout *h_box_bm = new QHBoxLayout (m_filter_widget);
141  h_box_bm->addWidget (filter_label);
142  h_box_bm->addWidget (m_filter_checkbox);
143  h_box_bm->addWidget (m_filter);
144  h_box_bm->setMargin (2);
145  m_filter_widget->setLayout (h_box_bm);
146 
147  m_filter_shown = settings->value (dc_bookmark_filter_shown).toBool ();
148  m_filter_widget->setVisible (m_filter_shown);
149 
150  // Resulting Layout of this widget
151  QVBoxLayout *v_box_bm = new QVBoxLayout (this);
152  v_box_bm->addWidget (m_filter_widget);
153  v_box_bm->addWidget (m_tree);
154  setLayout (v_box_bm);
155 }
156 
157 // Slot for adding the current page as a bookmark
159 {
160  QUrl url = m_browser->historyUrl (0);
161 
162  // Check if bookmark already exists and select if yes
163  QTreeWidgetItemIterator it (m_tree);
164  while (*it)
165  {
166  QUrl url_i = (*it)->data (0, url_role).toUrl ();
167  if (url == url_i)
168  {
169  m_tree->setCurrentItem (*it);
170  (*it)->setExpanded (true);
171  return;
172  }
173  it++;
174  }
175 
176  // Add the anchor name to the title of the page and add the bookmark
177  // as top-level-item
178  QString title = m_doc->title_and_anchor (m_browser->historyTitle (0), url);
179  add_bookmark (title, url.toString ());
180 }
181 
182 // Function for actually adding a bookmark to the tree
183 void documentation_bookmarks::add_bookmark (const QString& title,
184  const QString& url,
185  QTreeWidgetItem* item)
186 {
187  // Create new bookmark
188  QTreeWidgetItem *new_item = new QTreeWidgetItem (QStringList (title));
189  new_item->setData (0, tag_role, QVariant (bookmark_tag));
190  new_item->setData (0, url_role, QVariant (url));
191  new_item->setFlags ((new_item->flags () & (~Qt::ItemIsDropEnabled))
192  | Qt::ItemIsEditable
193  | Qt::ItemIsDragEnabled);
194  new_item->setIcon (0, icon_bookmark);
195 
196  // Insert as top level or child item
197  // TODO: Open dialog allowing to select a target folder if this
198  // bookmark is added manually and not by reading a bookmark file
199  if (item)
200  item->addChild (new_item);
201  else
202  m_tree->addTopLevelItem (new_item);
203 }
204 
205 // Slot for adding a folder from the context menu
207 {
208  QTreeWidgetItem *parent_item = nullptr;
209 
210  if (m_ctx_menu_item)
211  {
212  if (m_ctx_menu_item->data (0, tag_role).toInt () == folder_tag)
213  parent_item = m_ctx_menu_item;
214  else
215  {
216  QTreeWidgetItem *p = m_ctx_menu_item->parent ();
217  if (p)
218  parent_item = p;
219  }
220  }
221 
222  QTreeWidgetItem *new_folder = add_folder (tr ("New Folder"), parent_item);
223 
224  m_tree->setCurrentItem (new_folder);
225  m_tree->editItem (new_folder);
226 }
227 
228 // Function for actually adding a folder to the tree
229 QTreeWidgetItem* documentation_bookmarks::add_folder (const QString& folder,
230  QTreeWidgetItem *item, bool expanded)
231 {
232  QTreeWidgetItem *new_folder = new QTreeWidgetItem (QStringList (folder));
233  new_folder->setData (0, tag_role, QVariant (folder_tag));
234  new_folder->setFlags (new_folder->flags() | Qt::ItemIsEditable
235  | Qt::ItemIsDragEnabled
236  | Qt::ItemIsDropEnabled);
237  new_folder->setChildIndicatorPolicy (QTreeWidgetItem::DontShowIndicatorWhenChildless);
238  new_folder->setIcon (0, icon_folder);
239  new_folder->setExpanded (expanded);
240 
241  // Insert as top level or child item
242  if (item)
243  item->addChild (new_folder);
244  else
245  m_tree->addTopLevelItem (new_folder);
246 
247  return new_folder;
248 }
249 
250 void documentation_bookmarks::filter_bookmarks (const QString& pattern)
251 {
252  QTreeWidgetItemIterator it (m_tree);
253 
254  while (*it)
255  {
256  if ((*it)->text (0).contains (pattern, Qt::CaseInsensitive))
257  {
258  (*it)->setHidden (false);
259  (*it)->setExpanded (true);
260  QTreeWidgetItem *p = (*it)->parent ();
261  while (p)
262  {
263  p->setHidden (false);
264  p->setExpanded (true);
265  p = p->parent ();
266  }
267  }
268  else
269  (*it)->setHidden (true);
270 
271  it++;
272  }
273 }
274 
276 {
277  m_filter->setEnabled (state);
278 
279  QString pattern;
280  if (state)
281  pattern = m_filter->currentText ();
282 
283  filter_bookmarks (pattern);
284 }
285 
287 {
288  QString text = m_filter->currentText (); // get current text
289  int index = m_filter->findText (text); // and its actual index
290 
291  if (index > -1)
292  m_filter->removeItem (index); // remove if already existing
293 
294  m_filter->insertItem (0, text); // (re)insert at beginning
295  m_filter->setCurrentIndex (0);
296 }
297 
298 void documentation_bookmarks::handle_double_click (QTreeWidgetItem *item, int)
299 {
300  int tag = item->data (0, tag_role).toInt ();
301 
302  if (tag == folder_tag)
303  {
304  item->setExpanded (! item->isExpanded ());
305  return;
306  }
307 
308  if (tag == bookmark_tag)
309  {
310  QUrl url = item->data (0, url_role).toUrl ();
311  if (! url.isEmpty ())
312  m_browser->setSource (url);
313  return;
314  }
315 }
316 
317 void documentation_bookmarks::ctx_menu (const QPoint& xpos)
318 {
319  QMenu menu (this);
320 
321  m_ctx_menu_item = m_tree->itemAt (xpos);
322 
323  if (m_ctx_menu_item)
324  {
326 
327  menu.addAction (tr ("&Open"), this, &documentation_bookmarks::open);
328  menu.addAction (tr ("&Rename"), this, &documentation_bookmarks::edit);
329  menu.addAction (rmgr.icon ("window-close"), tr ("Remo&ve"),
331  menu.addSeparator ();
332  }
333 
334  menu.addAction (tr ("&Add Folder"), this,
336 
337  menu.addSeparator ();
338 
339  if (m_filter_shown)
340  menu.addAction (tr ("Hide &Filter"),
342  else
343  menu.addAction (tr ("Show &Filter"),
345 
346  menu.exec (m_tree->mapToGlobal (xpos));
347 }
348 
350 {
351  QList<QTreeWidgetItem *> items = m_tree->selectedItems ();
352 
353  if (items.size () > 0)
354  handle_double_click (items.at (0));
355 }
356 
358 {
359  QList<QTreeWidgetItem *> items = m_tree->selectedItems ();
360 
361  if (items.size () > 0)
362  m_tree->editItem (items.at (0));
363 }
364 
366 {
367  QList<QTreeWidgetItem *> items = m_tree->selectedItems ();
368 
369  for (auto it = items.begin () ; it != items.end (); it++)
370  {
371  if (*it)
372  m_tree->takeTopLevelItem (
373  m_tree->indexOfTopLevelItem (*it));
374  }
375 }
376 
378 {
380  m_filter_widget->setVisible (m_filter_shown);
381 }
382 
384 {
385  // Write the bookmarks to the xbel-file
386  write_bookmarks ();
387 
388  // Store settings
389  settings->setValue (dc_bookmark_filter_active.key, m_filter_checkbox->isChecked ());
391 
392  QStringList mru;
393  for (int i = 0; i < m_filter->count (); i++)
394  mru.append (m_filter->itemText (i));
395  settings->setValue (dc_bookmark_filter_mru.key, mru);
396 
397  settings->sync ();
398 }
399 
401 {
402  if (! m_xbel_file.open (QFile::WriteOnly | QFile::Text))
403  {
404  QMessageBox::warning (this, tr("Octave: Saving Documentation Bookmarks"),
405  tr("Unable to write file %1:\n%2.\n\n"
406  "Documentation bookmarks are not saved!\n")
407  .arg (m_xbel_file.fileName ())
408  .arg (m_xbel_file.errorString()));
409  return;
410  }
411 
412  QXmlStreamWriter xml_writer (&m_xbel_file);
413  xml_writer.setAutoFormatting (true);
414 
415  xml_writer.writeStartDocument ();
416  xml_writer.writeDTD (dc_xbel_doctype);
417  xml_writer.writeStartElement (dc_xbel_name_format);
418  xml_writer.writeAttribute (dc_xbel_attr_version, dc_xbel_value_version);
419 
420  for (int i = 0; i < m_tree->topLevelItemCount(); i++)
421  write_tree_item (&xml_writer, m_tree->topLevelItem (i));
422 
423  xml_writer.writeEndDocument();
424 
425  m_xbel_file.flush ();
426  m_xbel_file.close ();
427 }
428 
429 void documentation_bookmarks::write_tree_item (QXmlStreamWriter* xml_writer,
430  const QTreeWidgetItem *item)
431 {
432  switch (item->data (0, tag_role).toInt ())
433  {
434  case folder_tag:
435  xml_writer->writeStartElement (dc_xbel_name_folder);
436  xml_writer->writeAttribute (dc_xbel_attr_folded,
437  item->isExpanded () ? dc_xbel_value_no : dc_xbel_value_yes);
438  xml_writer->writeTextElement (dc_xbel_name_title, item->text(0));
439  for (int i = 0; i < item->childCount (); i++)
440  write_tree_item (xml_writer, item->child (i));
441  xml_writer->writeEndElement ();
442  break;
443 
444  case bookmark_tag:
445  xml_writer->writeStartElement (dc_xbel_name_bookmark);
446  xml_writer->writeAttribute (dc_xbel_attr_href, item->data (0, url_role).toString ());
447  xml_writer->writeTextElement (dc_xbel_name_title, item->text (0));
448  xml_writer->writeEndElement ();
449  break;
450  }
451 }
452 
454 {
455  QString error_message;
456 
457  // Check the file
458  if (! m_xbel_file.open (QFile::ReadOnly | QFile::Text))
459  {
460  error_message = tr ("Unable to read file %1:\n%2.")
461  .arg (m_xbel_file.fileName ())
462  .arg (m_xbel_file.errorString());
463  return error_message;
464  }
465 
466  QXmlStreamReader xml_reader (&m_xbel_file);
467 
468  if (! xml_reader.readNextStartElement ())
469  {
470  error_message = tr ("No start element found in %1.\n"
471  "Invalid bookmark file?")
472  .arg (m_xbel_file.fileName ());
473  return error_message;
474  }
475 
476  if (xml_reader.name() != dc_xbel_name_format
477  || xml_reader.attributes ().value (dc_xbel_attr_version) != dc_xbel_value_version)
478  {
479  error_message = tr ("The file\n"
480  "%1\n"
481  "is not a valid XBEL file version 1.0.")
482  .arg (m_xbel_file.fileName ());
483  return error_message;
484  }
485 
486  // Read the elements from the file
487  while (xml_reader.readNextStartElement ())
488  {
489  if (xml_reader.name () == dc_xbel_name_folder)
490  read_next_item (&xml_reader, folder_tag);
491  else if (xml_reader.name () == dc_xbel_name_bookmark)
492  read_next_item (&xml_reader, bookmark_tag);
493  else
494  xml_reader.skipCurrentElement ();
495  }
496 
497  m_xbel_file.close ();
498 
499  return error_message;
500 }
501 
502 void documentation_bookmarks::read_next_item (QXmlStreamReader *xml_reader,
503  item_tag tag, QTreeWidgetItem *item)
504 {
505  QString title (tr ("Unknown title"));
506  if (tag == folder_tag)
507  {
508  // Next item is a folder, which might also have children
509  bool expanded = (xml_reader->attributes().value (dc_xbel_attr_folded) == dc_xbel_value_no);
510 
511  QTreeWidgetItem *new_folder = add_folder (title, item, expanded);
512 
513  // Check elements of this folder for title and for recursively
514  // adding sub-items
515  while (xml_reader->readNextStartElement ())
516  {
517  if (xml_reader->name () == dc_xbel_name_title)
518  {
519  title = xml_reader->readElementText();
520  new_folder->setText (0, title);
521  }
522  else if (xml_reader->name () == dc_xbel_name_folder)
523  read_next_item (xml_reader, folder_tag, new_folder);
524  else if (xml_reader->name () == dc_xbel_name_bookmark)
525  read_next_item (xml_reader, bookmark_tag, new_folder);
526  else
527  xml_reader->skipCurrentElement ();
528  }
529  }
530  else if (tag == bookmark_tag)
531  {
532  // Next item is a bookmark, without children
533  QString url = xml_reader->attributes().value (dc_xbel_attr_href).toString ();
534  while (xml_reader->readNextStartElement ())
535  {
536  if (xml_reader->name() == dc_xbel_name_title)
537  title = xml_reader->readElementText();
538  else
539  xml_reader->skipCurrentElement ();
540  }
541  add_bookmark (title, url, item);
542  }
543 }
544 
OCTAVE_END_NAMESPACE(octave)
Base class for Octave interfaces that use Qt.
resource_manager & get_resource_manager(void)
documentation_browser * m_browser
void handle_double_click(QTreeWidgetItem *item, int col=0)
void save_settings(gui_settings *settings)
void write_tree_item(QXmlStreamWriter *xml_writer, const QTreeWidgetItem *item)
void read_next_item(QXmlStreamReader *xml_writer, item_tag tag, QTreeWidgetItem *item=nullptr)
void filter_bookmarks(const QString &pattern)
void ctx_menu(const QPoint &xpos)
Documentation browser derived from Textbrowser.
Definition: documentation.h:51
The documentation main class derived from QSplitter.
Definition: documentation.h:99
QString title_and_anchor(const QString &title, const QUrl &url)
gui_settings * get_settings(void) const
QIcon icon(const QString &icon_name, bool octave_only=false, const QString &icon_alt_name=QString())
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void warning(const char *fmt,...)
Definition: error.cc:1054
const QLatin1String dc_xbel_value_version("1.0")
const QLatin1String dc_xbel_value_yes("yes")
const gui_pref dc_bookmark_filter_mru("documentation_widget/bookmark_filter_mru", QVariant())
const QLatin1String dc_xbel_name_title("title")
const QLatin1String dc_xbel_name_bookmark("bookmark")
const QLatin1String dc_xbel_attr_version("version")
const QLatin1String dc_xbel_name_folder("folder")
const QLatin1String dc_xbel_name_format("xbel")
const QString dc_bookmark_file("octave-doc-bookmarks.xbel")
const gui_pref dc_bookmark_filter_shown("documentation_widget/filter_shown", QVariant(true))
const gui_pref dc_bookmark_filter_active("documentation_widget/filter_active", QVariant(false))
const QLatin1String dc_xbel_attr_folded("folded")
const QLatin1String dc_xbel_value_no("no")
const QLatin1String dc_xbel_doctype("<!DOCTYPE xbel>")
const QLatin1String dc_xbel_attr_href("href")
F77_RET_T const F77_DBLE const F77_DBLE * f
static uint32_t state[624]
Definition: randmtzig.cc:193
const QString key