GNU Octave 7.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-2022 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"
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
50namespace octave
51{
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
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!"));
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
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
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
545}
Base class for Octave interfaces that use Qt.
resource_manager & get_resource_manager(void)
void write_tree_item(QXmlStreamWriter *xml_writer, const QTreeWidgetItem *item)
void save_settings(gui_settings *settings)
void handle_double_click(QTreeWidgetItem *item, int col=0)
documentation_bookmarks(documentation *doc, documentation_browser *browser, base_qobject &oct_qobj, QWidget *p=nullptr)
void filter_bookmarks(const QString &pattern)
void read_next_item(QXmlStreamReader *xml_writer, item_tag tag, QTreeWidgetItem *item=nullptr)
Documentation browser derived from Textbrowser.
Definition: documentation.h:51
The documentation main class derived from QSplitter.
QString title_and_anchor(const QString &title, const QUrl &url)
gui_settings * get_settings(void) const
QIcon icon(const QString &icon_name, bool fallback=true)
void warning(const char *fmt,...)
Definition: error.cc:1055
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")
static uint32_t state[624]
Definition: randmtzig.cc:192
static double f(double k, double l_nu, double c_pm)
Definition: randpoisson.cc:118
const QString key