Mercurial > octave
changeset 29330:b92614cfdfed
add bookmark functionality to the documentation browser (bug #54938)
* libgui/src/documentation-bookmarks.cc: new file for the bookmarks tab;
(documentation_bookmarks): initialize the tab, the filter and the tree;
prepare the bookmark file and read existing bookmarks;
(~documentation_bookmarks): empty destructor;
(add_bookmark): slot for the adding bookmark action, get current title
and url;
(add_bookmark): do the adding, possibly as child of a parent item;
(add_folder): slot for context menu action for adding a folder, check
current position where to insert the new folder;
(add_folder): do the folder adding;
(filter_bookmarks): filter bookmarks following the changed filter text;
(filter_activate): enable/disable filter;
(update_filter_history): save a search term when acknowledged by return;
(handle_double_click): open the clicked bookmark;
(ctx_menu): show context menu at current mouse position;
(open): open a bookmark via context menu;
(edit): edit a bookmark via context menu;
(remove): remove selected bookmarks via context menu;
(show_filter): toggle visibility of the filter;
(save_settings): save settings and initiate writing the bookmarks;
(write_bookmarks): open file and initiate writing all top level items;
(write_tree_item): write a single item and its children;
(read_bookmarks): open the file, check if it is valid and loop over
all top level items for reading them from the file;
(read_next_item): read an item and its children in case of a folder;
* documentation-bookmarks.h: class documentation_bookmarks derived
from QWidget, declaration of all required functions and class variables;
* documentation-dock-widget.cc (save_settings): new derived method,
emitting a signal for saving setting in child widgets and callinf the
original method
* documentation-dock-widget.h: derived method save_settings and signal
for child widgets
* documentation.cc: include documentation-bookmarks.h;
(documentation): add bookmark tab, connect signal for saving settings;
(add_action): check reveiver before connection the actions signal;
(construct_tool_bar): add toolbar button for adding a bookmark, connect
its signal the add_bookmark slot and connect signal for saving settings;
(notice_settings): add shortcut for adding a bookmark;
(update_history): move combining page title and current anchor into a
more specific title ...
(title_and_anchor): to here;
* documentation.h: new function title_and_anchor, new action
* gui-preferences-dc.h: new file with constants for some bookmark settings
and for the xbel file syntax
* gui-preferences-sc.h: default value for bookmark action
* bookmark-new.png/bookmark-new.svg: icon for tool bar button
* icons_license: add new icon
* module.mk: add new files
* shortcut-manager.cc (init_data): initialize new short for bookmarking
author | Torsten Lilge <ttl-octave@mailbox.org> |
---|---|
date | Sun, 10 Jan 2021 14:04:35 +0100 |
parents | 24b9c62b453d |
children | 3cbc3f96028c |
files | libgui/src/documentation-bookmarks.cc libgui/src/documentation-bookmarks.h libgui/src/documentation-dock-widget.cc libgui/src/documentation-dock-widget.h libgui/src/documentation.cc libgui/src/documentation.h libgui/src/gui-preferences-dc.h libgui/src/gui-preferences-sc.h libgui/src/icons/bookmark-new.png libgui/src/icons/bookmark-new.svg libgui/src/icons/icons_license libgui/src/module.mk libgui/src/resource.qrc libgui/src/shortcut-manager.cc |
diffstat | 14 files changed, 1502 insertions(+), 32 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/documentation-bookmarks.cc Sun Jan 10 14:04:35 2021 +0100 @@ -0,0 +1,547 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2018-2020 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or <https://octave.org/copyright/>. +// +// This file is part of Octave. +// +// Octave is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Octave is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Octave; see the file COPYING. If not, see +// <https://www.gnu.org/licenses/>. +// +//////////////////////////////////////////////////////////////////////// + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include <QCompleter> +#include <QMenu> +#include <QShortcut> +#include <QVBoxLayout> +#include <QWidget> + +#include "documentation.h" +#include "documentation-bookmarks.h" + +#include "gui-settings.h" +#include "gui-preferences-global.h" +#include "gui-preferences-dc.h" +#include "gui-preferences-sc.h" +#include "shortcut-manager.h" + +#include "defaults.h" +#include "file-ops.h" +#include "oct-env.h" + +namespace octave +{ + documentation_bookmarks::documentation_bookmarks ( + documentation *doc, documentation_browser *browser, + base_qobject& oct_qobj, QWidget *p) + : QWidget (p), + m_doc (doc), m_browser (browser), m_octave_qobj (oct_qobj), + m_ctx_menu_item (nullptr) + { + setObjectName ("documentation_tab_bookmarks"); + + resource_manager& rmgr = m_octave_qobj.get_resource_manager (); + gui_settings *settings = rmgr.get_settings (); + + // Setup the tree view with the bookmarks + m_tree = new QTreeWidget (p); + + m_tree->setContextMenuPolicy (Qt::CustomContextMenu); + m_tree->setSelectionMode (QAbstractItemView::ExtendedSelection); + m_tree->setSortingEnabled (false); + m_tree->setDragEnabled(true); + m_tree->viewport()->setAcceptDrops(true); + m_tree->setDropIndicatorShown(true); + m_tree->setDragDropMode(QAbstractItemView::InternalMove); + m_tree->setColumnCount (1); + m_tree->setHeaderHidden (true); + m_tree->setEditTriggers (QAbstractItemView::EditKeyPressed + | QAbstractItemView::SelectedClicked); + + connect (m_tree, SIGNAL (customContextMenuRequested (const QPoint &)), + this, SLOT (ctx_menu (const QPoint &))); + connect (m_tree, SIGNAL (itemDoubleClicked (QTreeWidgetItem*, int)), + this, SLOT (handle_double_click (QTreeWidgetItem*, int))); + + // Define the icons for the tree view + icon_folder.addPixmap (style ()->standardPixmap(QStyle::SP_DirClosedIcon), + QIcon::Normal, QIcon::Off); + icon_folder.addPixmap (style ()->standardPixmap(QStyle::SP_DirOpenIcon), + QIcon::Normal, QIcon::On); + icon_bookmark.addPixmap (style ()->standardPixmap(QStyle::SP_FileIcon)); + + // Setup and read the bookmarkfile + QFileInfo f (settings->fileName ()); + QString f_path = f.absolutePath (); + f.setFile (QDir (f_path), dc_bookmark_file); + m_xbel_file.setFileName (f.absoluteFilePath ()); + + if (m_xbel_file.exists ()) + { + QString err = read_bookmarks (); + if ( !err.isEmpty ()) + { + err.append (tr ("\nNo documentation bookmarks loaded!")); + QMessageBox::warning (this, + tr ("Octave: Loading Documentation Bookmarks"), + err); + m_xbel_file.close (); + } + } + + // Setup the filter widget + m_filter_widget = new QWidget (p); + m_filter = new QComboBox (m_filter_widget); + + m_filter->setToolTip (tr ("Enter text to search the bookmarks")); + m_filter->setEditable (true); + m_filter->setInsertPolicy (QComboBox::NoInsert); + m_filter->setMaxCount (10); + m_filter->setMaxVisibleItems (10); + m_filter->setSizeAdjustPolicy (QComboBox::AdjustToMinimumContentsLengthWithIcon); + QSizePolicy size_pol (QSizePolicy::Expanding, QSizePolicy::Preferred); + m_filter->setSizePolicy (size_pol); + m_filter->completer ()->setCaseSensitivity (Qt::CaseSensitive); + + m_filter->addItems (settings->value (dc_bookmark_filter_mru).toStringList ()); + + connect (m_filter, SIGNAL (editTextChanged (const QString &)), + this, SLOT (filter_bookmarks (const QString &))); + connect (m_filter->lineEdit (), SIGNAL (editingFinished (void)), + this, SLOT (update_filter_history (void))); + + m_filter_checkbox = new QCheckBox (m_filter_widget); + bool filter_state = settings->value (dc_bookmark_filter_active).toBool (); + m_filter_checkbox->setChecked (filter_state); + filter_activate (filter_state); + + connect (m_filter_checkbox, SIGNAL (toggled (bool)), + this, SLOT (filter_activate (bool))); + + QLabel *filter_label = new QLabel (tr ("Filter"), m_filter_widget); + QHBoxLayout *h_box_bm = new QHBoxLayout (m_filter_widget); + h_box_bm->addWidget (filter_label); + h_box_bm->addWidget (m_filter_checkbox); + h_box_bm->addWidget (m_filter); + h_box_bm->setMargin (2); + m_filter_widget->setLayout (h_box_bm); + + m_filter_shown = settings->value (dc_bookmark_filter_shown).toBool (); + m_filter_widget->setVisible (m_filter_shown); + + // Resulting Layout of this widget + QVBoxLayout *v_box_bm = new QVBoxLayout (this); + v_box_bm->addWidget (m_filter_widget); + v_box_bm->addWidget (m_tree); + setLayout (v_box_bm); + } + + documentation_bookmarks::~documentation_bookmarks (void) + { } + + // Slot for adding the current page as a bookmark + void documentation_bookmarks::add_bookmark (void) + { + QUrl url = m_browser->historyUrl (0); + + // Check if bookmark already exists and select if yes + QTreeWidgetItemIterator it (m_tree); + while (*it) + { + QUrl url_i = (*it)->data (0, url_role).toUrl (); + if (url == url_i) + { + m_tree->setCurrentItem (*it); + (*it)->setExpanded (true); + return; + } + it++; + } + + // Add the anchor name to the title of the page and add the bookmark + // as top-level-item + QString title = m_doc->title_and_anchor (m_browser->historyTitle (0), url); + add_bookmark (title, url.toString ()); + } + + // Function for actually adding a bookmark to the tree + void documentation_bookmarks::add_bookmark (const QString& title, + const QString& url, + QTreeWidgetItem* item) + { + // Create new bookmark + QTreeWidgetItem *new_item = new QTreeWidgetItem (QStringList (title)); + new_item->setData (0, tag_role, QVariant (bookmark_tag)); + new_item->setData (0, url_role, QVariant (url)); + new_item->setFlags ((new_item->flags () & (~Qt::ItemIsDropEnabled)) + | Qt::ItemIsEditable + | Qt::ItemIsDragEnabled); + new_item->setIcon (0, icon_bookmark); + + // Insert as top level or child item + // TODO: Open dialog allowing to select a target folder if this + // bookmark is added manually and not by reading a bookmark file + if (item) + item->addChild (new_item); + else + m_tree->addTopLevelItem (new_item); + } + + // Slot for adding a folder from the context menu + void documentation_bookmarks::add_folder (bool) + { + QTreeWidgetItem *parent_item = nullptr; + + if (m_ctx_menu_item) + { + if (m_ctx_menu_item->data (0, tag_role).toInt () == folder_tag) + parent_item = m_ctx_menu_item; + else + { + QTreeWidgetItem *p = m_ctx_menu_item->parent (); + if (p) + parent_item = p; + } + } + + QTreeWidgetItem *new_folder = add_folder (tr ("New Folder"), parent_item); + + m_tree->setCurrentItem (new_folder); + m_tree->editItem (new_folder); + } + + // Function for actually adding a folder to the tree + QTreeWidgetItem* documentation_bookmarks::add_folder (const QString& folder, + QTreeWidgetItem *item, bool expanded) + { + QTreeWidgetItem *new_folder = new QTreeWidgetItem (QStringList (folder)); + new_folder->setData (0, tag_role, QVariant (folder_tag)); + new_folder->setFlags (new_folder->flags() | Qt::ItemIsEditable + | Qt::ItemIsDragEnabled + | Qt::ItemIsDropEnabled); + new_folder->setChildIndicatorPolicy (QTreeWidgetItem::DontShowIndicatorWhenChildless); + new_folder->setIcon (0, icon_folder); + new_folder->setExpanded (expanded); + + // Insert as top level or child item + if (item) + item->addChild (new_folder); + else + m_tree->addTopLevelItem (new_folder); + + return new_folder; + } + + void documentation_bookmarks::filter_bookmarks (const QString& pattern) + { + QTreeWidgetItemIterator it (m_tree); + + while (*it) + { + if ((*it)->text (0).contains (pattern, Qt::CaseInsensitive)) + { + (*it)->setHidden (false); + (*it)->setExpanded (true); + QTreeWidgetItem *p = (*it)->parent (); + while (p) + { + p->setHidden (false); + p->setExpanded (true); + p = p->parent (); + } + } + else + (*it)->setHidden (true); + + it++; + } + } + + void documentation_bookmarks::filter_activate (bool state) + { + m_filter->setEnabled (state); + + QString pattern; + if (state) + pattern = m_filter->currentText (); + + filter_bookmarks (pattern); + } + + void documentation_bookmarks::update_filter_history (void) + { + QString text = m_filter->currentText (); // get current text + int index = m_filter->findText (text); // and its actual index + + if (index > -1) + m_filter->removeItem (index); // remove if already existing + + m_filter->insertItem (0, text); // (re)insert at beginning + m_filter->setCurrentIndex (0); + } + + void documentation_bookmarks::handle_double_click (QTreeWidgetItem *item, int) + { + int tag = item->data (0, tag_role).toInt (); + + if (tag == folder_tag) + { + item->setExpanded (! item->isExpanded ()); + return; + } + + if (tag == bookmark_tag) + { + QUrl url = item->data (0, url_role).toUrl (); + if (! url.isEmpty ()) + m_browser->setSource (url); + return; + } + } + + void documentation_bookmarks::ctx_menu (const QPoint& xpos) + { + QMenu menu (this); + + m_ctx_menu_item = m_tree->itemAt (xpos); + + if (m_ctx_menu_item) + { + resource_manager& rmgr = m_octave_qobj.get_resource_manager (); + + menu.addAction (tr ("&Open"), this, SLOT (open (bool))); + menu.addAction (tr ("&Rename"), this, SLOT (edit (bool))); + menu.addAction (rmgr.icon ("window-close"), tr ("Remo&ve"), this, + SLOT (remove (bool))); + menu.addSeparator (); + } + + menu.addAction (tr ("&Add Folder"), this, SLOT (add_folder (bool))); + + menu.addSeparator (); + + if (m_filter_shown) + menu.addAction (tr ("Hide &Filter"), this, SLOT (show_filter (bool))); + else + menu.addAction (tr ("Show &Filter"), this, SLOT (show_filter (bool))); + + menu.exec (m_tree->mapToGlobal (xpos)); + } + + void documentation_bookmarks::open (bool) + { + QList<QTreeWidgetItem *> items = m_tree->selectedItems (); + + if (items.size () > 0) + handle_double_click (items.at (0)); + } + + void documentation_bookmarks::edit (bool) + { + QList<QTreeWidgetItem *> items = m_tree->selectedItems (); + + if (items.size () > 0) + m_tree->editItem (items.at (0)); + } + + void documentation_bookmarks::remove (bool) + { + QList<QTreeWidgetItem *> items = m_tree->selectedItems (); + + for (auto it = items.begin () ; it != items.end (); it++) + { + if (*it) + m_tree->takeTopLevelItem ( + m_tree->indexOfTopLevelItem (*it)); + } + } + + void documentation_bookmarks::show_filter (bool) + { + m_filter_shown = ! m_filter_shown; + m_filter_widget->setVisible (m_filter_shown); + } + + void documentation_bookmarks::save_settings (void) + { + // Write the bookmarks to the xbel-file + write_bookmarks (); + + // Store settings + resource_manager& rmgr = m_octave_qobj.get_resource_manager (); + gui_settings *settings = rmgr.get_settings (); + + settings->setValue (dc_bookmark_filter_active.key, m_filter_checkbox->isChecked ()); + settings->setValue (dc_bookmark_filter_shown.key, m_filter_shown); + + QStringList mru; + for (int i = 0; i < m_filter->count (); i++) + mru.append (m_filter->itemText (i)); + settings->setValue (dc_bookmark_filter_mru.key, mru); + + settings->sync (); + } + + void documentation_bookmarks::write_bookmarks (void) + { + if (! m_xbel_file.open (QFile::WriteOnly | QFile::Text)) + { + QMessageBox::warning (this, tr("Octave: Saving Documentation Bookmarks"), + tr("Unable to write file %1:\n%2.\n\n" + "Documentation bookmarks are not saved!\n") + .arg (m_xbel_file.fileName ()) + .arg (m_xbel_file.errorString())); + return; + } + + QXmlStreamWriter xml_writer (&m_xbel_file); + xml_writer.setAutoFormatting (true); + + xml_writer.writeStartDocument (); + xml_writer.writeDTD (dc_xbel_doctype); + xml_writer.writeStartElement (dc_xbel_name_format); + xml_writer.writeAttribute (dc_xbel_attr_version, dc_xbel_value_version); + + for (int i = 0; i < m_tree->topLevelItemCount(); i++) + write_tree_item (&xml_writer, m_tree->topLevelItem (i)); + + xml_writer.writeEndDocument(); + + m_xbel_file.flush (); + m_xbel_file.close (); + } + + void documentation_bookmarks::write_tree_item (QXmlStreamWriter* xml_writer, + const QTreeWidgetItem *item) + { + switch (item->data (0, tag_role).toInt ()) + { + case folder_tag: + xml_writer->writeStartElement (dc_xbel_name_folder); + xml_writer->writeAttribute (dc_xbel_attr_folded, + item->isExpanded () ? dc_xbel_value_no : dc_xbel_value_yes); + xml_writer->writeTextElement (dc_xbel_name_title, item->text(0)); + for (int i = 0; i < item->childCount (); i++) + write_tree_item (xml_writer, item->child (i)); + xml_writer->writeEndElement (); + break; + + case bookmark_tag: + xml_writer->writeStartElement (dc_xbel_name_bookmark); + xml_writer->writeAttribute (dc_xbel_attr_href, item->data (0, url_role).toString ()); + xml_writer->writeTextElement (dc_xbel_name_title, item->text (0)); + xml_writer->writeEndElement (); + break; + } + } + + QString documentation_bookmarks::read_bookmarks (void) + { + QString error_message; + + // Check the file + if (! m_xbel_file.open (QFile::ReadOnly | QFile::Text)) + { + error_message = tr ("Unable to read file %1:\n%2.") + .arg (m_xbel_file.fileName ()) + .arg (m_xbel_file.errorString()); + return error_message; + } + + QXmlStreamReader xml_reader (&m_xbel_file); + + if (! xml_reader.readNextStartElement ()) + { + error_message = tr ("No start element found in %1.\n" + "Invalid bookmark file?") + .arg (m_xbel_file.fileName ()); + return error_message; + } + + if (xml_reader.name() != dc_xbel_name_format + || xml_reader.attributes ().value (dc_xbel_attr_version) != dc_xbel_value_version) + { + error_message = tr ("The file\n" + "%1\n" + "is not a valid XBEL file verison 1.0.") + .arg (m_xbel_file.fileName ()); + return error_message; + } + + // Read the elements from the file + while (xml_reader.readNextStartElement ()) + { + if (xml_reader.name () == dc_xbel_name_folder) + read_next_item (&xml_reader, folder_tag); + else if (xml_reader.name () == dc_xbel_name_bookmark) + read_next_item (&xml_reader, bookmark_tag); + else + xml_reader.skipCurrentElement (); + } + + m_xbel_file.close (); + + return error_message; + } + + void documentation_bookmarks::read_next_item (QXmlStreamReader *xml_reader, + item_tag tag, QTreeWidgetItem *item) + { + QString title (tr ("Unknown title")); + if (tag == folder_tag) + { + // Next item is a folder, which might also have children + bool expanded = (xml_reader->attributes().value (dc_xbel_attr_folded) == dc_xbel_value_no); + + QTreeWidgetItem *new_folder = add_folder (title, item, expanded); + + // Check elements of this folder for title and for recursively + // adding sub-items + while (xml_reader->readNextStartElement ()) + { + if (xml_reader->name () == dc_xbel_name_title) + { + title = xml_reader->readElementText(); + new_folder->setText (0, title); + } + else if (xml_reader->name () == dc_xbel_name_folder) + read_next_item (xml_reader, folder_tag, new_folder); + else if (xml_reader->name () == dc_xbel_name_bookmark) + read_next_item (xml_reader, bookmark_tag, new_folder); + else + xml_reader->skipCurrentElement (); + } + } + else if (tag == bookmark_tag) + { + // Next item is a bookmark, without children + QString url = xml_reader->attributes().value (dc_xbel_attr_href).toString (); + while (xml_reader->readNextStartElement ()) + { + if (xml_reader->name() == dc_xbel_name_title) + title = xml_reader->readElementText(); + else + xml_reader->skipCurrentElement (); + } + add_bookmark (title, url, item); + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/documentation-bookmarks.h Sun Jan 10 14:04:35 2021 +0100 @@ -0,0 +1,124 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2018-2020 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or <https://octave.org/copyright/>. +// +// This file is part of Octave. +// +// Octave is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Octave is distributed in the hope that it will be useful, but +// WiTHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Octave; see the file COPYING. If not, see +// <https://www.gnu.org/licenses/>. +// +//////////////////////////////////////////////////////////////////////// + +#if ! defined (octave_documentation_bookmarks_h) +#define octave_documentation_bookmarks_h 1 + +#include <QCheckBox> +#include <QComboBox> +#include <QTreeWidget> +#include <QXmlStreamWriter> + +#include "documentation.h" +#include "octave-qobject.h" + + +namespace octave +{ + class base_qobject; + class documentation; + + class documentation_bookmarks : public QWidget + { + Q_OBJECT + + public: + + documentation_bookmarks ( + documentation *doc, documentation_browser *browser, + base_qobject& oct_qobj, QWidget *p = nullptr); + ~documentation_bookmarks (void); + + public slots: + + void add_bookmark (void); + void add_folder (bool); + void save_settings (void); + + private slots: + + void filter_bookmarks (const QString& pattern); + void filter_activate (bool state); + void update_filter_history (void); + void handle_double_click (QTreeWidgetItem *item, int col = 0); + void ctx_menu (const QPoint& xpos); + void open (bool); + void edit (bool); + void remove (bool); + void show_filter (bool); + + private: + + enum item_role + { + url_role = Qt::UserRole, + tag_role = Qt::UserRole + 1 + }; + enum item_tag + { + bookmark_tag, + folder_tag + }; + + void add_bookmark (const QString& title, const QString& url, + QTreeWidgetItem *item = nullptr); + QTreeWidgetItem* add_folder (const QString& folder, + QTreeWidgetItem *item = nullptr, + bool expanded = true); + + /*! + Writing to and reading bookmarks from an xbel-file as + proposed in the qt example + [QXmlStream Bookmarks Example](https://doc.qt.io/qt-5/qtxml-streambookmarks-example.html) + */ + void write_bookmarks (void); + void write_tree_item (QXmlStreamWriter *xml_writer, + const QTreeWidgetItem *item); + QString read_bookmarks (void); + void read_next_item (QXmlStreamReader *xml_writer, item_tag tag, + QTreeWidgetItem *item = nullptr); + + documentation *m_doc; + documentation_browser *m_browser; + base_qobject& m_octave_qobj; + + QComboBox *m_filter; + QTreeWidget *m_tree; + + QTreeWidgetItem *m_ctx_menu_item; + + QIcon icon_folder; + QIcon icon_bookmark; + + QWidget *m_filter_widget; + QCheckBox *m_filter_checkbox; + bool m_filter_shown; + + QFile m_xbel_file; + }; + +} + +#endif
--- a/libgui/src/documentation-dock-widget.cc Tue Jan 26 16:41:36 2021 +0100 +++ b/libgui/src/documentation-dock-widget.cc Sun Jan 10 14:04:35 2021 +0100 @@ -65,6 +65,12 @@ m_docs->notice_settings (settings); } + void documentation_dock_widget::save_settings (void) + { + emit save_settings_signal (); + octave_dock_widget::save_settings (); + } + void documentation_dock_widget::copyClipboard (void) { m_docs->copyClipboard ();
--- a/libgui/src/documentation-dock-widget.h Tue Jan 26 16:41:36 2021 +0100 +++ b/libgui/src/documentation-dock-widget.h Sun Jan 10 14:04:35 2021 +0100 @@ -42,9 +42,14 @@ documentation_dock_widget (QWidget *parent, base_qobject& oct_qobj); ~documentation_dock_widget (void); + signals: + + void save_settings_signal (void); + public slots: void notice_settings (const gui_settings *settings); + void save_settings (void); protected slots:
--- a/libgui/src/documentation.cc Tue Jan 26 16:41:36 2021 +0100 +++ b/libgui/src/documentation.cc Sun Jan 10 14:04:35 2021 +0100 @@ -50,6 +50,7 @@ #include <QVBoxLayout> #include "documentation.h" +#include "documentation-bookmarks.h" #include "gui-preferences-global.h" #include "gui-preferences-sc.h" #include "octave-qobject.h" @@ -253,6 +254,16 @@ connect (m_filter->lineEdit (), SIGNAL (editingFinished (void)), this, SLOT(filter_update_history (void))); + // Bookmarks (own class) + documentation_bookmarks *bookmarks + = new documentation_bookmarks (this, m_doc_browser, m_octave_qobj, navi); + navi->addTab (bookmarks, tr ("Bookmarks")); + + connect (m_action_bookmark, SIGNAL (triggered ()), + bookmarks, SLOT (add_bookmark ())); + connect (p, SIGNAL (save_settings_signal (void)), + bookmarks, SLOT (save_settings (void))); + // Search QHelpSearchEngine *search_engine = m_help_engine->searchEngine (); QHelpSearchQueryWidget *search = search_engine->queryWidget (); @@ -324,7 +335,9 @@ r = receiver; a = new QAction (icon, text, this); - connect (a, SIGNAL (triggered ()), r, member); + + if (member) + connect (a, SIGNAL (triggered ()), r, member); if (tool_bar) tool_bar->addAction (a); @@ -414,6 +427,12 @@ m_action_zoom_original = add_action (rmgr.icon ("zoom-original"), tr ("Zoom original"), SLOT (zoom_original (void)), m_doc_browser, m_tool_bar); + + // Bookmarks (connect slots later) + m_tool_bar->addSeparator (); + m_action_bookmark + = add_action (rmgr.icon ("bookmark-new"), tr ("Bookmark current page"), + nullptr, nullptr, m_tool_bar); } void documentation::global_search (void) @@ -613,6 +632,7 @@ scmgr.set_shortcut (m_action_go_home, sc_doc_go_home); scmgr.set_shortcut (m_action_go_prev, sc_doc_go_back); scmgr.set_shortcut (m_action_go_next, sc_doc_go_next); + scmgr.set_shortcut (m_action_bookmark, sc_doc_bookmark); } void documentation::copyClipboard (void) @@ -870,37 +890,9 @@ // Fill used menu entries for (int i = 0; i < count; i++) { - QString title = m_doc_browser->historyTitle (prev_next*(i+1)); - title.remove (QRegExp ("\\s*\\(*GNU Octave \\(version [^\\)]*\\)[: \\)]*")); - - // Since the title only contains the section name and not the - // specific anchor, extract the latter from the url and append - // it to the title - QString url = m_doc_browser->historyUrl (prev_next*(i+1)).toString (); - if (url.contains ('#')) - { - // Get the anchor from the url - QString anchor = url.split ('#').last (); - - // Remove internal string parts - anchor.remove (QRegExp ("^index-")); - anchor.remove (QRegExp ("^SEC_")); - anchor.remove (QRegExp ("^XREF")); - anchor.remove ("Concept-Index_cp_letter-"); - anchor.replace ("-"," "); - - // replace encoded special chars by their unencoded versions - QRegExp rx = QRegExp ("_00([0-7][0-9a-f])"); - int pos = 0; - while ((pos = rx.indexIn(anchor, pos)) != -1) - { - anchor.replace ("_00"+rx.cap (1), QChar (rx.cap (1).toInt (nullptr,16))); - pos += rx.matchedLength(); - } - - if (title != anchor) - title = title + ": " + anchor; - } + QString title + = title_and_anchor (m_doc_browser->historyTitle (prev_next*(i+1)), + m_doc_browser->historyUrl (prev_next*(i+1))); if (i == 0) a->setText (title); // set tool tip for prev/next buttons @@ -924,8 +916,51 @@ m_doc_browser->setSource (a->data ().toUrl ()); } + // Utility functions + QString documentation::title_and_anchor (const QString& title, const QUrl& url) + { + QString retval = title; + QString u = url.toString (); + + retval.remove (QRegExp ("\\s*\\(*GNU Octave \\(version [^\\)]*\\)[: \\)]*")); + + // Since the title only contains the section name and not the + // specific anchor, extract the latter from the url and append + // it to the title + if (u.contains ('#')) + { + // Get the anchor from the url + QString anchor = u.split ('#').last (); + // Remove internal string parts + anchor.remove (QRegExp ("^index-")); + anchor.remove (QRegExp ("^SEC_")); + anchor.remove (QRegExp ("^XREF")); + anchor.remove ("Concept-Index_cp_letter-"); + anchor.replace ("-"," "); + + // replace encoded special chars by their unencoded versions + QRegExp rx = QRegExp ("_00([0-7][0-9a-f])"); + int pos = 0; + while ((pos = rx.indexIn(anchor, pos)) != -1) + { + anchor.replace ("_00"+rx.cap (1), QChar (rx.cap (1).toInt (nullptr,16))); + pos += rx.matchedLength(); + } + + if (retval != anchor) + retval = retval + ": " + anchor; + } + + return retval; + } + + + + // // The documentation browser + // + documentation_browser::documentation_browser (QHelpEngine *he, QWidget *p) : QTextBrowser (p), m_help_engine (he), m_zoom_level (0) {
--- a/libgui/src/documentation.h Tue Jan 26 16:41:36 2021 +0100 +++ b/libgui/src/documentation.h Sun Jan 10 14:04:35 2021 +0100 @@ -32,6 +32,7 @@ #include <QSplitter> #include <QTextBrowser> #include <QToolBar> +#include <QListWidget> #include <QToolButton> #include <QWidget> #include <QtHelp/QHelpEngine> @@ -41,6 +42,7 @@ namespace octave { class base_qobject; + class documentation; //! Documentation browser derived from Textbrowser @@ -101,6 +103,17 @@ documentation (QWidget *parent, base_qobject& oct_qobj); ~documentation (void); + /*! + Generate a string with page name @p title and current anchor + from @p url for using in prev/next or bookmarks menu: + + @param title current title of the page as QString + @param url current url as QUrl + + @return QString "title: anchor" + */ + QString title_and_anchor (const QString& title, const QUrl& url); + signals: void show_single_result (const QUrl&); @@ -172,6 +185,8 @@ QAction *m_prev_pages_actions[max_history_entries]; QAction *m_next_pages_actions[max_history_entries]; + QAction *m_action_bookmark; + QAction *m_action_find; QShortcut *m_findnext_shortcut; QShortcut *m_findprev_shortcut; @@ -180,6 +195,7 @@ QAction *m_action_zoom_out; QAction *m_action_zoom_original; }; + } #endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/gui-preferences-dc.h Sun Jan 10 14:04:35 2021 +0100 @@ -0,0 +1,57 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2017-2020 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or <https://octave.org/copyright/>. +// +// This file is part of Octave. +// +// Octave is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Octave is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Octave; see the file COPYING. If not, see +// <https://www.gnu.org/licenses/>. +// +//////////////////////////////////////////////////////////////////////// + +#if ! defined (octave_gui_preferences_dc_h) +#define octave_gui_preferences_dc_h 1 + +#include "gui-preferences.h" + +// documentation properties + +const QString +dc_bookmark_file ("octave-doc-bookmarks.xbel"); +const gui_pref +dc_bookmark_filter_active ("documentation_widget/filter_active", QVariant (false)); + +const gui_pref +dc_bookmark_filter_shown ("documentation_widget/filter_shown", QVariant (true)); + +const gui_pref +dc_bookmark_filter_mru ("documentation_widget/bookmark_filter_mru", QVariant ()); + +// Constants for the xbel file format +const QLatin1String dc_xbel_doctype ("<!DOCTYPE xbel>"); +const QLatin1String dc_xbel_attr_href ("href"); +const QLatin1String dc_xbel_attr_folded ("folded"); +const QLatin1String dc_xbel_attr_version ("version"); +const QLatin1String dc_xbel_value_version ("1.0"); +const QLatin1String dc_xbel_value_yes ("yes"); +const QLatin1String dc_xbel_value_no ("no"); +const QLatin1String dc_xbel_name_title ("title"); +const QLatin1String dc_xbel_name_folder ("folder"); +const QLatin1String dc_xbel_name_bookmark ("bookmark"); +const QLatin1String dc_xbel_name_format ("xbel"); + +#endif
--- a/libgui/src/gui-preferences-sc.h Tue Jan 26 16:41:36 2021 +0100 +++ b/libgui/src/gui-preferences-sc.h Sun Jan 10 14:04:35 2021 +0100 @@ -255,6 +255,7 @@ 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_doc_go_next (sc_doc + ":go_next", QKeySequence::Forward); +const sc_pref sc_doc_bookmark (sc_doc + ":bookmark", CTRL + Qt::Key_D); // Other normal, shortcut related options
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/src/icons/bookmark-new.svg Sun Jan 10 14:04:35 2021 +0100 @@ -0,0 +1,672 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + inkscape:export-ydpi="240.00000" + inkscape:export-xdpi="240.00000" + inkscape:export-filename="/home/jimmac/gfx/novell/pdes/trunk/docs/BIGmime-text.png" + sodipodi:docname="bookmark-new.svg" + sodipodi:docbase="/home/jimmac/src/cvs/tango-icon-theme/scalable/actions" + inkscape:version="0.46" + sodipodi:version="0.32" + id="svg249" + height="48.000000px" + width="48.000000px" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs3"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 24 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="48 : 24 : 1" + inkscape:persp3d-origin="24 : 16 : 1" + id="perspective100" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient5060" + id="radialGradient5031" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)" + cx="605.71429" + cy="486.64789" + fx="605.71429" + fy="486.64789" + r="117.14286" /> + <linearGradient + inkscape:collect="always" + id="linearGradient5060"> + <stop + style="stop-color:black;stop-opacity:1;" + offset="0" + id="stop5062" /> + <stop + style="stop-color:black;stop-opacity:0;" + offset="1" + id="stop5064" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient5060" + id="radialGradient5029" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)" + cx="605.71429" + cy="486.64789" + fx="605.71429" + fy="486.64789" + r="117.14286" /> + <linearGradient + id="linearGradient5048"> + <stop + style="stop-color:black;stop-opacity:0;" + offset="0" + id="stop5050" /> + <stop + id="stop5056" + offset="0.5" + style="stop-color:black;stop-opacity:1;" /> + <stop + style="stop-color:black;stop-opacity:0;" + offset="1" + id="stop5052" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5048" + id="linearGradient5027" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)" + x1="302.85715" + y1="366.64789" + x2="302.85715" + y2="609.50507" /> + <linearGradient + inkscape:collect="always" + id="linearGradient2906"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop2908" /> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="1" + id="stop2910" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient2896"> + <stop + style="stop-color:#000000;stop-opacity:1;" + offset="0" + id="stop2898" /> + <stop + style="stop-color:#000000;stop-opacity:0;" + offset="1" + id="stop2900" /> + </linearGradient> + <linearGradient + id="linearGradient2598"> + <stop + style="stop-color:#859dbc;stop-opacity:1;" + offset="0" + id="stop2600" /> + <stop + style="stop-color:#547299;stop-opacity:1;" + offset="1" + id="stop2602" /> + </linearGradient> + <linearGradient + id="linearGradient2590"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop2592" /> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="1" + id="stop2594" /> + </linearGradient> + <linearGradient + id="linearGradient5897"> + <stop + style="stop-color:#000000;stop-opacity:0.0000000;" + offset="0.0000000" + id="stop5899" /> + <stop + id="stop5905" + offset="0.50000000" + style="stop-color:#000000;stop-opacity:0.56701028;" /> + <stop + style="stop-color:#000000;stop-opacity:0.0000000;" + offset="1.0000000" + id="stop5901" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient5866"> + <stop + style="stop-color:#000000;stop-opacity:1;" + offset="0" + id="stop5868" /> + <stop + style="stop-color:#000000;stop-opacity:0;" + offset="1" + id="stop5870" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient4404"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop4406" /> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="1" + id="stop4408" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient4542"> + <stop + style="stop-color:#000000;stop-opacity:1;" + offset="0" + id="stop4544" /> + <stop + style="stop-color:#000000;stop-opacity:0;" + offset="1" + id="stop4546" /> + </linearGradient> + <linearGradient + id="linearGradient15662"> + <stop + id="stop15664" + offset="0.0000000" + style="stop-color:#ffffff;stop-opacity:1.0000000;" /> + <stop + id="stop15666" + offset="1.0000000" + style="stop-color:#f8f8f8;stop-opacity:1.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient269"> + <stop + id="stop270" + offset="0.0000000" + style="stop-color:#a3a3a3;stop-opacity:1.0000000;" /> + <stop + id="stop271" + offset="1.0000000" + style="stop-color:#4c4c4c;stop-opacity:1.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient259"> + <stop + id="stop260" + offset="0.0000000" + style="stop-color:#fafafa;stop-opacity:1.0000000;" /> + <stop + id="stop261" + offset="1.0000000" + style="stop-color:#bbbbbb;stop-opacity:1.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient12512"> + <stop + id="stop12513" + offset="0.0000000" + style="stop-color:#ffffff;stop-opacity:1.0000000;" /> + <stop + id="stop12517" + offset="0.50000000" + style="stop-color:#fff520;stop-opacity:0.89108908;" /> + <stop + id="stop12514" + offset="1.0000000" + style="stop-color:#fff300;stop-opacity:0.0000000;" /> + </linearGradient> + <radialGradient + r="14.375000" + fy="125.00000" + fx="55.000000" + cy="125.00000" + cx="55.000000" + gradientUnits="userSpaceOnUse" + id="radialGradient278" + xlink:href="#linearGradient12512" + inkscape:collect="always" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient269" + id="radialGradient15656" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.968273,0.000000,0.000000,1.036374,3.250000,0.489522)" + cx="8.8244190" + cy="3.7561285" + fx="8.8244190" + fy="3.7561285" + r="37.751713" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient259" + id="radialGradient15658" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.960493,0.000000,0.000000,1.044769,-0.103553,-0.159183)" + cx="33.966679" + cy="35.736916" + fx="33.966679" + fy="35.736916" + r="86.708450" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient15662" + id="radialGradient15668" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.973033,0.000000,0.000000,1.034937,3.168754,0.555277)" + cx="8.1435566" + cy="7.2678967" + fx="8.1435566" + fy="7.2678967" + r="38.158695" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient4542" + id="radialGradient4548" + cx="24.306795" + cy="42.07798" + fx="24.306795" + fy="42.07798" + r="15.821514" + gradientTransform="matrix(1.000000,0.000000,0.000000,0.284916,0.000000,30.08928)" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4404" + id="linearGradient4410" + x1="16.812500" + y1="1.8750000" + x2="16.812500" + y2="4.7187500" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.319549,0.000000,0.000000,1.362060,40.38853,-0.362057)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5866" + id="linearGradient5872" + x1="19.452349" + y1="13.174174" + x2="19.685436" + y2="27.095339" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.224255,0.000000,0.000000,1.282176,0.371569,0.264657)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5897" + id="linearGradient5903" + x1="19.000000" + y1="9.7738247" + x2="19.000000" + y2="15.635596" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.319549,0.000000,0.000000,2.133926,-4.476133,-14.64845)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2590" + id="linearGradient2596" + x1="19.970377" + y1="6.1167107" + x2="19.970377" + y2="2.53125" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.319549,0.000000,0.000000,1.280356,-5.745298,0.249007)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2598" + id="linearGradient2604" + x1="18.431311" + y1="19.119474" + x2="18.402472" + y2="4.2702327" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.319549,0.000000,0.000000,1.299013,-3.106200,-1.336165)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2896" + id="linearGradient2902" + x1="14.584077" + y1="1.6392649" + x2="14.552828" + y2="2.4912448" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.000000,0.000000,0.000000,1.594214,0.000000,-0.790249)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2906" + id="linearGradient2912" + x1="13.354311" + y1="1.4866425" + x2="14.075844" + y2="2.4017651" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.000000,0.000000,0.000000,1.184816,0.000000,-0.727880)" /> + </defs> + <sodipodi:namedview + inkscape:window-y="158" + inkscape:window-x="433" + inkscape:window-height="690" + inkscape:window-width="872" + inkscape:document-units="px" + inkscape:grid-bbox="true" + showgrid="false" + inkscape:current-layer="layer6" + inkscape:cy="16.785697" + inkscape:cx="-154.12746" + inkscape:zoom="1" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + borderopacity="0.25490196" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + inkscape:showpageshadow="false" + showguides="true" + inkscape:guide-bbox="true" /> + <metadata + id="metadata4"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title>New Bookmark</dc:title> + <dc:subject> + <rdf:Bag> + <rdf:li>bookmark</rdf:li> + <rdf:li>remember</rdf:li> + <rdf:li>favorite</rdf:li> + </rdf:Bag> + </dc:subject> + <cc:license + rdf:resource="http://creativecommons.org/licenses/publicdomain/" /> + <dc:creator> + <cc:Agent> + <dc:title>Andreas Nilsson</dc:title> + </cc:Agent> + </dc:creator> + <dc:source /> + <dc:contributor> + <cc:Agent> + <dc:title>Jakub Steiner</dc:title> + </cc:Agent> + </dc:contributor> + <dc:description>create bookmark action</dc:description> + </cc:Work> + <cc:License + rdf:about="http://creativecommons.org/licenses/publicdomain/"> + <cc:permits + rdf:resource="http://creativecommons.org/ns#Reproduction" /> + <cc:permits + rdf:resource="http://creativecommons.org/ns#Distribution" /> + <cc:permits + rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /> + </cc:License> + </rdf:RDF> + </metadata> + <g + inkscape:groupmode="layer" + id="layer6" + inkscape:label="Shadow"> + <g + style="display:inline" + id="g5022" + transform="matrix(2.165152e-2,0,0,1.485743e-2,43.0076,42.68539)"> + <rect + y="-150.69685" + x="-1559.2523" + height="478.35718" + width="1339.6335" + id="rect4173" + style="opacity:0.40206185;color:black;fill:url(#linearGradient5027);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" /> + <path + sodipodi:nodetypes="cccc" + id="path5058" + d="M -219.61876,-150.68038 C -219.61876,-150.68038 -219.61876,327.65041 -219.61876,327.65041 C -76.744594,328.55086 125.78146,220.48075 125.78138,88.454235 C 125.78138,-43.572302 -33.655436,-150.68036 -219.61876,-150.68038 z " + style="opacity:0.40206185;color:black;fill:url(#radialGradient5029);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" /> + <path + style="opacity:0.40206185;color:black;fill:url(#radialGradient5031);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" + d="M -1559.2523,-150.68038 C -1559.2523,-150.68038 -1559.2523,327.65041 -1559.2523,327.65041 C -1702.1265,328.55086 -1904.6525,220.48075 -1904.6525,88.454235 C -1904.6525,-43.572302 -1745.2157,-150.68036 -1559.2523,-150.68038 z " + id="path5018" + sodipodi:nodetypes="cccc" /> + </g> + </g> + <g + style="display:inline" + inkscape:groupmode="layer" + inkscape:label="Base" + id="layer1"> + <rect + style="color:#000000;fill:url(#radialGradient15658);fill-opacity:1.0000000;fill-rule:nonzero;stroke:url(#radialGradient15656);stroke-width:0.99999982;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:1.0000000;visibility:visible;display:block;overflow:visible" + id="rect15391" + width="34.875000" + height="41.063431" + x="6.5000000" + y="3.5000000" + ry="1.1490481" + rx="1.1490486" /> + <rect + style="color:#000000;fill:none;fill-opacity:1.0000000;fill-rule:nonzero;stroke:url(#radialGradient15668);stroke-width:0.99999958;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:1.0000000;visibility:visible;display:block;overflow:visible" + id="rect15660" + width="32.937012" + height="39.028210" + x="7.5024552" + y="4.5010486" + ry="0.14904849" + rx="0.14904852" /> + <path + style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.98855311;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:0.017543854" + d="M 11.505723,5.4942766 L 11.505723,43.400869" + id="path15672" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.0000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:0.20467831" + d="M 12.500000,5.0205154 L 12.500000,43.038228" + id="path15674" + sodipodi:nodetypes="cc" /> + </g> + <g + inkscape:groupmode="layer" + id="layer5" + inkscape:label="Text" + style="display:inline"> + <g + id="g2188"> + <rect + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970759;fill-rule:nonzero;stroke:none;stroke-width:1.0000000;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:0.081871338;visibility:visible;display:block;overflow:visible" + id="rect15686" + width="20.000006" + height="1.0000000" + x="15.999994" + y="9.0000000" + rx="0.13778631" + ry="0.065390877" /> + <rect + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970759;fill-rule:nonzero;stroke:none;stroke-width:1.0000000;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:0.081871338;visibility:visible;display:block;overflow:visible" + id="rect15688" + width="20.000006" + height="1.0000000" + x="15.999994" + y="11.000000" + rx="0.13778631" + ry="0.065390877" /> + <rect + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970759;fill-rule:nonzero;stroke:none;stroke-width:1.0000000;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:0.081871338;visibility:visible;display:block;overflow:visible" + id="rect15690" + width="20.000006" + height="1.0000000" + x="15.999994" + y="13.000000" + rx="0.13778631" + ry="0.065390877" /> + <rect + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970759;fill-rule:nonzero;stroke:none;stroke-width:1.0000000;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:0.081871338;visibility:visible;display:block;overflow:visible" + id="rect15692" + width="20.000006" + height="1.0000000" + x="15.999994" + y="15.000000" + rx="0.13778631" + ry="0.065390877" /> + <rect + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970759;fill-rule:nonzero;stroke:none;stroke-width:1.0000000;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:0.081871338;visibility:visible;display:block;overflow:visible" + id="rect15694" + width="20.000006" + height="1.0000000" + x="15.999994" + y="17.000000" + rx="0.13778631" + ry="0.065390877" /> + <rect + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970759;fill-rule:nonzero;stroke:none;stroke-width:1.0000000;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:0.081871338;visibility:visible;display:block;overflow:visible" + id="rect15696" + width="20.000006" + height="1.0000000" + x="15.999994" + y="19.000000" + rx="0.13778631" + ry="0.065390877" /> + <rect + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970759;fill-rule:nonzero;stroke:none;stroke-width:1.0000000;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:0.081871338;visibility:visible;display:block;overflow:visible" + id="rect15698" + width="20.000006" + height="1.0000000" + x="15.999994" + y="21.000000" + rx="0.13778631" + ry="0.065390877" /> + <rect + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970759;fill-rule:nonzero;stroke:none;stroke-width:1.0000000;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:0.081871338;visibility:visible;display:block;overflow:visible" + id="rect15700" + width="20.000006" + height="1.0000000" + x="15.999994" + y="23.000000" + rx="0.13778631" + ry="0.065390877" /> + <rect + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970759;fill-rule:nonzero;stroke:none;stroke-width:1.0000000;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:0.081871338;visibility:visible;display:block;overflow:visible" + id="rect15732" + width="9.0000057" + height="1.0000000" + x="15.999986" + y="25.000000" + rx="0.062003858" + ry="0.065390877" /> + <rect + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970759;fill-rule:nonzero;stroke:none;stroke-width:1.0000000;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:0.081871338;visibility:visible;display:block;overflow:visible" + id="rect15736" + width="20.000006" + height="1.0000000" + x="15.999986" + y="29.000000" + rx="0.13778631" + ry="0.065390877" /> + <rect + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970759;fill-rule:nonzero;stroke:none;stroke-width:1.0000000;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:0.081871338;visibility:visible;display:block;overflow:visible" + id="rect15738" + width="20.000006" + height="1.0000000" + x="15.999986" + y="31.000000" + rx="0.13778631" + ry="0.065390877" /> + <rect + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970759;fill-rule:nonzero;stroke:none;stroke-width:1.0000000;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:0.081871338;visibility:visible;display:block;overflow:visible" + id="rect15740" + width="20.000006" + height="1.0000000" + x="15.999986" + y="33.000000" + rx="0.13778631" + ry="0.065390877" /> + <rect + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970759;fill-rule:nonzero;stroke:none;stroke-width:1.0000000;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:0.081871338;visibility:visible;display:block;overflow:visible" + id="rect15742" + width="20.000006" + height="1.0000000" + x="15.999986" + y="35.000000" + rx="0.13778631" + ry="0.065390877" /> + <rect + style="color:#000000;fill:#9b9b9b;fill-opacity:0.54970759;fill-rule:nonzero;stroke:none;stroke-width:1.0000000;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4.0000000;stroke-dashoffset:0.0000000;stroke-opacity:0.081871338;visibility:visible;display:block;overflow:visible" + id="rect15744" + width="14.000014" + height="1.0000000" + x="15.999986" + y="37.000000" + rx="0.096450485" + ry="0.065390877" /> + </g> + <path + style="opacity:0.28021976;fill:url(#linearGradient5872);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.25pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 28.245858,31.324906 L 21.147869,27.133701 L 14.30757,30.8838 L 13.761859,3.9475667 L 28.549598,3.9475667 L 28.245858,31.324906 z " + id="path5138" + sodipodi:nodetypes="cccccc" /> + <path + style="fill:url(#linearGradient2604);fill-opacity:1;fill-rule:evenodd;stroke:#364878;stroke-width:0.99999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;display:inline" + d="M 12.427339,3.5180202 C 12.427339,3.5180202 12.240033,0.60520607 15.107867,0.54270607 L 25.119343,0.50728624 C 26.277287,0.50728624 26.581888,1.1910178 26.581888,2.1095589 L 26.581888,29.729916 L 20.545426,24.533862 L 14.674346,29.729916 L 14.591655,3.519629 L 12.427339,3.5180202 z " + id="path2204" + sodipodi:nodetypes="ccccccccc" /> + <path + style="opacity:0.4450549;fill:url(#linearGradient4410);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.25pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 13.030252,3.0117919 C 13.011046,2.225362 13.312918,1.0801307 15.375418,1.0176307 L 25.027906,1 C 25.640922,1 26.090152,1.1674319 26.090152,1.7994802 L 26.060994,10.491851 L 15.317102,10.491851 L 15.192102,2.9993251 C 15.192102,2.9993251 13.030252,3.0117919 13.030252,3.0117919 z " + id="path3668" + sodipodi:nodetypes="cccccccs" /> + <rect + style="opacity:0.28021976;fill:url(#linearGradient5903);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect5895" + width="10.556392" + height="12.803556" + x="15.317101" + y="6.6907959" + rx="0.062003858" + ry="0.065390877" /> + <path + style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2596);stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.19125683;display:inline" + d="M 24.476832,2.2095507 L 25.575535,3.113139 L 25.547445,27.511911 L 20.497463,23.203758 L 15.704084,27.415203 L 15.699081,2.7495618 L 24.476832,2.2095507 z " + id="path5969" + sodipodi:nodetypes="ccccccc" /> + <path + sodipodi:type="arc" + style="color:#000000;fill:url(#radialGradient278);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25000024;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:block" + id="path12511" + sodipodi:cx="55" + sodipodi:cy="125" + sodipodi:rx="14.375" + sodipodi:ry="14.375" + d="M 69.375 125 A 14.375 14.375 0 1 1 40.625,125 A 14.375 14.375 0 1 1 69.375 125 z" + transform="matrix(0.611127,0.000000,0.000000,0.611127,5.632438,-67.28175)" + inkscape:export-filename="/home/jimmac/ximian_art/icons/nautilus/suse93/stock_new-16.png" + inkscape:export-xdpi="33.852203" + inkscape:export-ydpi="33.852203" /> + <path + style="opacity:0.48295456;color:#000000;fill:url(#linearGradient2912);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.10533953;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" + d="M 15.158602,3.9384083 L 15.114407,1.0335178 C 12.983906,1.0335178 12.993087,2.9680775 12.993087,3.9384083 L 15.158602,3.9384083 z " + id="path2894" + sodipodi:nodetypes="cccc" /> + <path + sodipodi:nodetypes="cccc" + id="path2904" + d="M 15.158602,3.9384086 L 15.114407,1.8247593 C 12.81631,1.8426926 12.993087,3.9384086 12.993087,3.9384086 L 15.158602,3.9384086 z " + style="opacity:0.35795455;color:#000000;fill:url(#linearGradient2902);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.10533953;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" /> + </g> +</svg>
--- a/libgui/src/icons/icons_license Tue Jan 26 16:41:36 2021 +0100 +++ b/libgui/src/icons/icons_license Sun Jan 10 14:04:35 2021 +0100 @@ -17,6 +17,7 @@ =========================================== applications-system.svg +bookmark-new.svg dialog-error.svg dialog-information.svg dialog-warning.svg
--- a/libgui/src/module.mk Tue Jan 26 16:41:36 2021 +0100 +++ b/libgui/src/module.mk Sun Jan 10 14:04:35 2021 +0100 @@ -135,6 +135,7 @@ %reldir%/moc-dialog.cc \ %reldir%/moc-documentation-dock-widget.cc \ %reldir%/moc-documentation.cc \ + %reldir%/moc-documentation-bookmarks.cc \ %reldir%/moc-dw-main-window.cc \ %reldir%/moc-files-dock-widget.cc \ %reldir%/moc-gui-settings.cc \ @@ -187,9 +188,11 @@ %reldir%/octave-dock-widget.h \ %reldir%/documentation-dock-widget.h \ %reldir%/documentation.h \ + %reldir%/documentation-bookmarks.h \ %reldir%/dw-main-window.h \ %reldir%/gui-preferences-all.h \ %reldir%/gui-preferences-cs.h \ + %reldir%/gui-preferences-dc.h \ %reldir%/gui-preferences-dw.h \ %reldir%/gui-preferences-ed.h \ %reldir%/gui-preferences-fb.h \ @@ -247,6 +250,7 @@ %reldir%/dialog.cc \ %reldir%/documentation-dock-widget.cc \ %reldir%/documentation.cc \ + %reldir%/documentation-bookmarks.cc \ %reldir%/dw-main-window.cc \ %reldir%/external-editor-interface.cc \ %reldir%/files-dock-widget.cc \
--- a/libgui/src/resource.qrc Tue Jan 26 16:41:36 2021 +0100 +++ b/libgui/src/resource.qrc Sun Jan 10 14:04:35 2021 +0100 @@ -1,6 +1,7 @@ <RCC> <qresource prefix="/actions"> <file>icons/applications-system.png</file> + <file>icons/bookmark-new.png</file> <file>icons/bp-toggle.png</file> <file>icons/bp-rm-all.png</file> <file>icons/bp-prev.png</file>
--- a/libgui/src/shortcut-manager.cc Tue Jan 26 16:41:36 2021 +0100 +++ b/libgui/src/shortcut-manager.cc Sun Jan 10 14:04:35 2021 +0100 @@ -305,6 +305,7 @@ init (tr ("Go to Homepage"), sc_doc_go_home); init (tr ("Go Back one Page"), sc_doc_go_back); init (tr ("Go Forward one Page"), sc_doc_go_next); + init (tr ("Bookmark this Page"), sc_doc_bookmark); } // write one or all actual shortcut set(s) into a settings file