Merge branch 'introducing_toc_panel' into 'master'

Initial setup of ToC

See merge request libreweb/browser!24
master
Melroy van den Berg 2022-02-04 22:00:57 +00:00
commit c7793e2536
10 changed files with 502 additions and 139 deletions

View File

@ -24,6 +24,7 @@ set(HEADERS
middleware-i.h
middleware.h
toolbar-button.h
toc-model-cols.h
mainwindow.h
md-parser.h
menu.h

View File

@ -3,6 +3,7 @@
#include "node.h"
#include "syntax_extension.h"
#include <cmark-gfm.h>
#include <cstdint>
#include <gdkmm/window.h>
#include <glibmm.h>
#include <gtkmm/textiter.h>
@ -276,7 +277,7 @@ void Draw::setMessage(const Glib::ustring& message, const Glib::ustring& details
/**
* \brief Draw homepage
*/
void Draw::showStartPage()
void Draw::showHomepage()
{
if (get_editable())
this->disableEdit();
@ -339,7 +340,7 @@ void Draw::newDocument()
{
this->undoPool.clear();
this->redoPool.clear();
this->clearText();
this->clear();
enableEdit();
grab_focus(); // Claim focus on text view
@ -363,12 +364,17 @@ void Draw::setText(const Glib::ustring& text)
}
/**
* \brief Clear all text on the screen
* Clear text-buffer & clear marks
*/
void Draw::clearText()
void Draw::clear()
{
auto buffer = get_buffer();
buffer->erase(buffer->begin(), buffer->end());
for (const Glib::RefPtr<Gtk::TextMark> mark : headingsToc)
{
buffer->delete_mark(mark);
}
headingsToc.clear();
}
/**
@ -425,6 +431,9 @@ void Draw::redo()
}
}
/**
* \brief Cut text into clipboard
*/
void Draw::cut()
{
if (get_editable())
@ -439,12 +448,18 @@ void Draw::cut()
}
}
/**
* \brief Copy text into clipboard
*/
void Draw::copy()
{
auto clipboard = get_clipboard("CLIPBOARD");
get_buffer()->copy_clipboard(clipboard);
}
/**
* \brief Paste text from clipboard
*/
void Draw::paste()
{
if (get_editable())
@ -454,6 +469,9 @@ void Draw::paste()
}
}
/**
* \brief Delete selected text (only if textview is editable)
*/
void Draw::del()
{
if (get_editable())
@ -474,12 +492,23 @@ void Draw::del()
}
}
/**
* \brief Select all text
*/
void Draw::selectAll()
{
auto buffer = get_buffer();
buffer->select_range(buffer->begin(), buffer->end());
}
/**
* \brief Return the Texr mark headings for Table of contents
*/
std::vector<Glib::RefPtr<Gtk::TextMark>> Draw::getHeadings()
{
return headingsToc;
}
/*************************************************************
* Editor signals calls
*************************************************************/
@ -1440,19 +1469,16 @@ void Draw::insertText(std::string text, const Glib::ustring& url, CodeTypeEnum c
default:
break;
}
// Already add the mark for the heading
addHeadingMark(text, headingLevel);
}
if (isQuote)
{
tagNames.push_back("quote");
}
// Insert URL
if (!url.empty())
{
this->insertLink(text, url);
}
// Insert text/heading
else
// Insert text (+ headings)
if (url.empty())
{
// Special case for code blocks within quote
if ((codeType == Draw::CodeTypeEnum::CODE_TYPE_CODE_BLOCK) && isQuote)
@ -1473,16 +1499,21 @@ void Draw::insertText(std::string text, const Glib::ustring& url, CodeTypeEnum c
insertTagText("\uFF5C ", "quote");
insertTagText(text, tagNames);
}
// Just insert text/heading the normal way
else
{
// Just insert text (default) - which includes headings as well
insertTagText(text, tagNames);
}
}
// Insert URL
else
{
insertLink(text, url);
}
}
/**
* Insert pango text with tags
* Insert pango text with multiple tags
*/
void Draw::insertTagText(const Glib::ustring& text, std::vector<Glib::ustring> const& tagNames)
{
@ -1491,6 +1522,21 @@ void Draw::insertTagText(const Glib::ustring& text, std::vector<Glib::ustring> c
buffer->insert_with_tags_by_name(endIter, text, tagNames);
}
/**
* \brief Add mark for heading (ToC)
*/
void Draw::addHeadingMark(const Glib::ustring& text, int headingLevel)
{
auto buffer = get_buffer();
auto endIter = buffer->end();
// Make anonymous marks, to avoid existing naming conflicts
Glib::RefPtr<Gtk::TextMark> textMark = Gtk::TextMark::create();
textMark->set_data("name", g_strdup(text.c_str()));
textMark->set_data("level", (void*)(intptr_t)headingLevel);
buffer->add_mark(textMark, endIter);
headingsToc.push_back(textMark);
}
/**
* Insert pango text with a single tag name
*/
@ -1538,17 +1584,6 @@ void Draw::truncateText(int charsTruncated)
buffer->erase(beginIter, endIter);
}
/**
* Clear text-buffer
*/
void Draw::clear()
{
auto buffer = get_buffer();
auto beginIter = buffer->begin();
auto endIter = buffer->end();
buffer->erase(beginIter, endIter);
}
/**
* Looks at all tags covering the position (x, y) in the text view,
* and if one of them is a link, change the cursor to the "hands" cursor

View File

@ -39,13 +39,13 @@ public:
explicit Draw(MiddlewareInterface& middleware);
void setMessage(const Glib::ustring& message, const Glib::ustring& details = "");
void showStartPage();
void showHomepage();
void setDocument(cmark_node* rootNode);
void setViewSourceMenuItem(bool isEnabled);
void newDocument();
Glib::ustring getText() const;
void setText(const Glib::ustring& text);
void clearText();
void clear();
void undo();
void redo();
void cut();
@ -53,6 +53,7 @@ public:
void paste();
void del();
void selectAll();
std::vector<Glib::RefPtr<Gtk::TextMark>> getHeadings();
// Signals editor calls
void make_heading(int headingLevel);
@ -104,6 +105,7 @@ private:
Glib::RefPtr<Gdk::Cursor> textCursor;
bool hovingOverLink;
bool isUserAction;
std::vector<Glib::RefPtr<Gtk::TextMark>> headingsToc;
std::vector<UndoRedoData> undoPool;
std::vector<UndoRedoData> redoPool;
@ -120,11 +122,11 @@ private:
void encodeText(std::string& string) const;
void insertText(std::string text, const Glib::ustring& url = "", CodeTypeEnum codeType = CodeTypeEnum::CODE_TYPE_NONE);
void insertTagText(const Glib::ustring& text, std::vector<Glib::ustring> const& tagNames);
void addHeadingMark(const Glib::ustring& text, int headingLevel);
void insertTagText(const Glib::ustring& text, const Glib::ustring& tagName);
void insertMarkupText(const Glib::ustring& text);
void insertLink(const Glib::ustring& text, const Glib::ustring& url);
void truncateText(int charsTruncated);
void clear();
void changeCursor(int x, int y);
static Glib::ustring intToRoman(int num);
};

View File

@ -2,6 +2,7 @@
#include "menu.h"
#include "project_config.h"
#include <cstdint>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <giomm/file.h>
#include <giomm/notification.h>
@ -27,15 +28,22 @@ MainWindow::MainWindow(const std::string& timeout)
m_indentAdjustment(Gtk::Adjustment::create(0, 0, 1000, 5, 10)),
m_drawCSSProvider(Gtk::CssProvider::create()),
m_menu(m_accelGroup),
m_draw_main(middleware_),
m_draw_primary(middleware_),
m_draw_secondary(middleware_),
m_about(*this),
m_vbox(Gtk::ORIENTATION_VERTICAL, 0),
m_vboxMain(Gtk::ORIENTATION_VERTICAL, 0),
m_vboxToc(Gtk::ORIENTATION_VERTICAL),
m_vboxSearch(Gtk::ORIENTATION_VERTICAL),
m_vboxStatus(Gtk::ORIENTATION_VERTICAL),
m_vboxSettings(Gtk::ORIENTATION_VERTICAL),
m_vboxIconTheme(Gtk::ORIENTATION_VERTICAL),
m_searchMatchCase("Match _Case", true),
m_wrapNone(m_wrappingGroup, "None"),
m_wrapChar(m_wrappingGroup, "Char"),
m_wrapWord(m_wrappingGroup, "Word"),
m_wrapWordChar(m_wrappingGroup, "Word+Char"),
m_closeTocWindowButton("Close table of contents"),
m_openTocButton("Show table of contents (Ctrl+Shift+T)", true),
m_backButton("Go back one page (Alt+Left arrow)", true),
m_forwardButton("Go forward one page (Alt+Right arrow)", true),
m_refreshButton("Reload current page (Ctrl+R)", true),
@ -61,6 +69,7 @@ MainWindow::MainWindow(const std::string& timeout)
m_bulletListButton("Add a bullet list"),
m_numberedListButton("Add a numbered list"),
m_highlightButton("Add highlight text"),
m_tableOfContentsLabel("Table of Contents"),
m_networkHeadingLabel("IPFS Network"),
m_networkRateHeadingLabel("Network rate"),
m_connectivityLabel("Status:"),
@ -75,6 +84,7 @@ MainWindow::MainWindow(const std::string& timeout)
m_spacingLabel("Spacing"),
m_marginsLabel("Margins"),
m_indentLabel("Indent"),
m_textWrappingLabel("Wrapping"),
m_themeLabel("Dark Theme"),
m_iconThemeLabel("Active Theme"),
// Private members
@ -98,44 +108,50 @@ MainWindow::MainWindow(const std::string& timeout)
loadStoredSettings();
loadIcons();
initButtons();
initToolbarButtons();
setTheme();
initSearchPopover();
initStatusPopover();
initSettingsPopover();
initTableofContents();
initSignals();
// Add custom CSS Provider to draw textviews
auto styleMain = m_draw_main.get_style_context();
auto stylePrimary = m_draw_primary.get_style_context();
auto styleSecondary = m_draw_secondary.get_style_context();
styleMain->add_provider(m_drawCSSProvider, GTK_STYLE_PROVIDER_PRIORITY_USER);
stylePrimary->add_provider(m_drawCSSProvider, GTK_STYLE_PROVIDER_PRIORITY_USER);
styleSecondary->add_provider(m_drawCSSProvider, GTK_STYLE_PROVIDER_PRIORITY_USER);
// Load the default font family and font size
updateCSS();
// Browser text main drawing area
m_scrolledWindowMain.add(m_draw_main);
m_scrolledWindowMain.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
// Primary drawing area
m_scrolledWindowPrimary.add(m_draw_primary);
m_scrolledWindowPrimary.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
// Secondary drawing area
m_draw_secondary.setViewSourceMenuItem(false);
m_scrolledWindowSecondary.add(m_draw_secondary);
m_scrolledWindowSecondary.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
m_paned.pack1(m_scrolledWindowMain, true, false);
m_paned.pack2(m_scrolledWindowSecondary, true, true);
m_vbox.pack_start(m_menu, false, false, 0);
m_vbox.pack_start(m_hboxBrowserToolbar, false, false, 6);
m_vbox.pack_start(m_hboxStandardEditorToolbar, false, false, 6);
m_vbox.pack_start(m_hboxFormattingEditorToolbar, false, false, 6);
m_vbox.pack_start(m_paned, true, true, 0);
add(m_vbox);
m_panedDraw.pack1(m_scrolledWindowPrimary, true, false);
m_panedDraw.pack2(m_scrolledWindowSecondary, true, true);
// Left the vbox for the table of contents,
// right the drawing paned windows (primary/secondary).
m_panedRoot.pack1(m_vboxToc, true, false);
m_panedRoot.pack2(m_panedDraw, true, false);
// Main virtual box
m_vboxMain.pack_start(m_menu, false, false, 0);
m_vboxMain.pack_start(m_hboxBrowserToolbar, false, false, 6);
m_vboxMain.pack_start(m_hboxStandardEditorToolbar, false, false, 6);
m_vboxMain.pack_start(m_hboxFormattingEditorToolbar, false, false, 6);
m_vboxMain.pack_start(m_panedRoot, true, true, 0);
add(m_vboxMain);
show_all_children();
// Hide by default the replace entry, editor box & secondary text view
// Hide by default the table of contents, secondary textview, replace entry and editor toolbars
m_vboxToc.hide();
m_scrolledWindowSecondary.hide();
m_searchReplaceEntry.hide();
m_hboxStandardEditorToolbar.hide();
m_hboxFormattingEditorToolbar.hide();
m_scrolledWindowSecondary.hide();
// Grap focus to input field by default
m_addressBar.grab_focus();
@ -188,6 +204,9 @@ void MainWindow::preRequest(const std::string& path, const std::string& title, b
m_menu.setBackMenuSensitive(currentHistoryIndex_ > 0);
m_forwardButton.set_sensitive(currentHistoryIndex_ < history_.size() - 1);
m_menu.setForwardMenuSensitive(currentHistoryIndex_ < history_.size() - 1);
// Clear table of contents (ToC)
m_tocTreeModel->clear();
}
/**
@ -232,11 +251,11 @@ void MainWindow::refreshRequest()
}
/**
* \brief Show startpage
* \brief Show home page
*/
void MainWindow::showStartpage()
void MainWindow::showHomepage()
{
m_draw_main.showStartPage();
m_draw_primary.showHomepage();
}
/**
@ -245,16 +264,18 @@ void MainWindow::showStartpage()
*/
void MainWindow::setText(const Glib::ustring& content)
{
m_draw_main.setText(content);
m_draw_primary.setText(content);
}
/**
* \brief Set markdown document (cmark). cmark_node pointer will be freed automatically.
* \brief Set markdown document (common mark) on primary window. cmark_node pointer will be freed automatically.
* And set the ToC.
* \param rootNode cmark root data struct
*/
void MainWindow::setDocument(cmark_node* rootNode)
{
m_draw_main.setDocument(rootNode);
m_draw_primary.setDocument(rootNode);
setTableofContents(m_draw_primary.getHeadings());
}
/**
@ -264,7 +285,7 @@ void MainWindow::setDocument(cmark_node* rootNode)
*/
void MainWindow::setMessage(const Glib::ustring& message, const Glib::ustring& details)
{
m_draw_main.setMessage(message, details);
m_draw_primary.setMessage(message, details);
}
/**
@ -317,9 +338,9 @@ void MainWindow::loadStoredSettings()
if (!isInstalled())
{
// Relative to the binary path
std::vector<std::string> relativePath{"..", "src", "gsettings"};
std::vector<std::string> relativePath{".."};
std::string schemaDir = Glib::build_path(G_DIR_SEPARATOR_S, relativePath);
std::cout << "INFO: Try to find the schema file using the following directory first: " << schemaDir << std::endl;
std::cout << "INFO: Binary not installed. Try to find the gschema file one directory up (..)." << std::endl;
Glib::setenv("GSETTINGS_SCHEMA_DIR", schemaDir);
}
@ -343,10 +364,11 @@ void MainWindow::loadStoredSettings()
m_spacingAdjustment->set_value(fontSpacing_);
m_marginsAdjustment->set_value(margins);
m_indentAdjustment->set_value(indent);
m_draw_main.set_left_margin(margins);
m_draw_main.set_right_margin(margins);
m_draw_main.set_indent(indent);
m_draw_primary.set_left_margin(margins);
m_draw_primary.set_right_margin(margins);
m_draw_primary.set_indent(indent);
int tocDividerPosition = m_settings->get_int("position-divider-toc");
m_panedRoot.set_position(tocDividerPosition);
iconTheme_ = m_settings->get_string("icon-theme");
useCurrentGTKIconTheme_ = m_settings->get_boolean("icon-gtk-theme");
brightnessScale_ = m_settings->get_double("brightness");
@ -355,17 +377,18 @@ void MainWindow::loadStoredSettings()
else
{
std::cerr << "ERROR: Gsettings schema file could not be found!" << std::endl;
// Fallback settings if schema isn't found,
// for adjustment controls
// Adjustment controls
int margins = 20;
int indent = 0;
m_spacingAdjustment->set_value(0);
m_marginsAdjustment->set_value(margins);
m_indentAdjustment->set_value(indent);
// For drawing
m_draw_main.set_left_margin(margins);
m_draw_main.set_right_margin(margins);
m_draw_main.set_indent(indent);
m_draw_primary.set_left_margin(margins);
m_draw_primary.set_right_margin(margins);
m_draw_primary.set_indent(indent);
// ToC paned divider
m_panedRoot.set_position(300);
}
}
@ -375,6 +398,7 @@ void MainWindow::loadStoredSettings()
void MainWindow::setGTKIcons()
{
// Toolbox buttons
m_tocIcon.set_from_icon_name("view-list-symbolic", Gtk::IconSize(Gtk::ICON_SIZE_MENU));
m_backIcon.set_from_icon_name("go-previous", Gtk::IconSize(Gtk::ICON_SIZE_MENU));
m_forwardIcon.set_from_icon_name("go-next", Gtk::IconSize(Gtk::ICON_SIZE_MENU));
m_refreshIcon.set_from_icon_name("view-refresh", Gtk::IconSize(Gtk::ICON_SIZE_MENU));
@ -424,6 +448,7 @@ void MainWindow::loadIcons()
else
{
// Toolbox buttons
m_tocIcon.set(Gdk::Pixbuf::create_from_file(getIconImageFromTheme("square_list", "editor"), iconSize_, iconSize_));
m_backIcon.set(Gdk::Pixbuf::create_from_file(getIconImageFromTheme("right_arrow_1", "arrows"), iconSize_, iconSize_)->flip());
m_forwardIcon.set(Gdk::Pixbuf::create_from_file(getIconImageFromTheme("right_arrow_1", "arrows"), iconSize_, iconSize_));
m_refreshIcon.set(Gdk::Pixbuf::create_from_file(getIconImageFromTheme("reload_centered", "arrows"), iconSize_ * 1.13, iconSize_));
@ -458,7 +483,7 @@ void MainWindow::loadIcons()
/**
* Init all buttons / comboboxes from the toolbars
*/
void MainWindow::initButtons()
void MainWindow::initToolbarButtons()
{
// Add icons to the toolbar editor buttons
m_openButton.add(m_openIcon);
@ -510,6 +535,7 @@ void MainWindow::initButtons()
m_settingsButton.set_relief(Gtk::RELIEF_NONE);
// Add icons to the toolbar buttons
m_openTocButton.add(m_tocIcon);
m_backButton.add(m_backIcon);
m_forwardButton.add(m_forwardIcon);
m_refreshButton.add(m_refreshIcon);
@ -542,7 +568,8 @@ void MainWindow::initButtons()
* Adding the buttons to the boxes
*/
// Browser Toolbar
m_backButton.set_margin_left(6);
m_openTocButton.set_margin_left(6);
m_hboxBrowserToolbar.pack_start(m_openTocButton, false, false, 0);
m_hboxBrowserToolbar.pack_start(m_backButton, false, false, 0);
m_hboxBrowserToolbar.pack_start(m_forwardButton, false, false, 0);
m_hboxBrowserToolbar.pack_start(m_refreshButton, false, false, 0);
@ -706,7 +733,29 @@ void MainWindow::initStatusPopover()
}
/**
* Init the settings pop-over
* \brief Init table of contents window (left side-panel)
*/
void MainWindow::initTableofContents()
{
m_closeTocWindowButton.set_image_from_icon_name("window-close-symbolic", Gtk::IconSize(Gtk::ICON_SIZE_SMALL_TOOLBAR));
m_tableOfContentsLabel.set_margin_start(6);
m_hboxToc.pack_start(m_tableOfContentsLabel, false, false);
m_hboxToc.pack_end(m_closeTocWindowButton, false, false);
tocTreeView.append_column("Level", m_tocColumns.m_col_level);
tocTreeView.append_column("Name", m_tocColumns.m_col_heading);
tocTreeView.set_activate_on_single_click(true);
tocTreeView.set_headers_visible(false);
tocTreeView.set_tooltip_column(2);
m_scrolledToc.add(tocTreeView);
m_scrolledToc.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
m_tocTreeModel = Gtk::TreeStore::create(m_tocColumns);
tocTreeView.set_model(m_tocTreeModel);
m_vboxToc.pack_start(m_hboxToc, Gtk::PackOptions::PACK_SHRINK);
m_vboxToc.pack_end(m_scrolledToc);
}
/**
* \brief Init the settings pop-over
*/
void MainWindow::initSettingsPopover()
{
@ -741,7 +790,17 @@ void MainWindow::initSettingsPopover()
m_hboxSetingsBrightness.pack_end(m_scaleSettingsBrightness);
// Dark theme switch
m_themeSwitch.set_active(useDarkTheme_); // Override with current dark theme preference
// Spin buttons
// Settings buttons
m_wrapWordChar.set_active(true); // Default wrapping mode
m_fontLabel.set_tooltip_text("Font familiy");
m_spacingLabel.set_tooltip_text("Text spacing");
m_marginsLabel.set_tooltip_text("Text margins");
m_indentLabel.set_tooltip_text("Text indentation");
m_textWrappingLabel.set_tooltip_text("Text wrapping");
m_wrapNone.set_tooltip_text("No wrapping");
m_wrapChar.set_tooltip_text("Character wrapping");
m_wrapWord.set_tooltip_text("Word wrapping");
m_wrapWordChar.set_tooltip_text("Word wrapping (+ character)");
m_spacingSpinButton.set_adjustment(m_spacingAdjustment);
m_marginsSpinButton.set_adjustment(m_marginsAdjustment);
m_indentSpinButton.set_adjustment(m_indentAdjustment);
@ -749,27 +808,34 @@ void MainWindow::initSettingsPopover()
m_spacingLabel.set_xalign(1);
m_marginsLabel.set_xalign(1);
m_indentLabel.set_xalign(1);
m_textWrappingLabel.set_xalign(1);
m_themeLabel.set_xalign(1);
m_fontLabel.get_style_context()->add_class("dim-label");
m_spacingLabel.get_style_context()->add_class("dim-label");
m_marginsLabel.get_style_context()->add_class("dim-label");
m_indentLabel.get_style_context()->add_class("dim-label");
m_textWrappingLabel.get_style_context()->add_class("dim-label");
m_themeLabel.get_style_context()->add_class("dim-label");
m_settingsGrid.set_margin_start(6);
m_settingsGrid.set_margin_top(6);
m_settingsGrid.set_margin_bottom(6);
m_settingsGrid.set_row_spacing(10);
m_settingsGrid.set_column_spacing(10);
m_settingsGrid.attach(m_fontLabel, 0, 0);
m_settingsGrid.attach(m_fontButton, 1, 0);
m_settingsGrid.attach(m_spacingLabel, 0, 1);
m_settingsGrid.attach(m_spacingSpinButton, 1, 1);
m_settingsGrid.attach(m_marginsLabel, 0, 2);
m_settingsGrid.attach(m_marginsSpinButton, 1, 2);
m_settingsGrid.attach(m_indentLabel, 0, 3);
m_settingsGrid.attach(m_indentSpinButton, 1, 3);
m_settingsGrid.attach(m_themeLabel, 0, 4);
m_settingsGrid.attach(m_themeSwitch, 1, 4);
m_settingsGrid.attach(m_fontLabel, 0, 0, 1);
m_settingsGrid.attach(m_fontButton, 1, 0, 2);
m_settingsGrid.attach(m_spacingLabel, 0, 1, 1);
m_settingsGrid.attach(m_spacingSpinButton, 1, 1, 2);
m_settingsGrid.attach(m_marginsLabel, 0, 2, 1);
m_settingsGrid.attach(m_marginsSpinButton, 1, 2, 2);
m_settingsGrid.attach(m_indentLabel, 0, 3, 1);
m_settingsGrid.attach(m_indentSpinButton, 1, 3, 2);
m_settingsGrid.attach(m_textWrappingLabel, 0, 4, 1);
m_settingsGrid.attach(m_wrapNone, 1, 4, 1);
m_settingsGrid.attach(m_wrapChar, 2, 4, 1);
m_settingsGrid.attach(m_wrapWord, 1, 5, 1);
m_settingsGrid.attach(m_wrapWordChar, 2, 5, 1);
m_settingsGrid.attach(m_themeLabel, 0, 6, 1);
m_settingsGrid.attach(m_themeSwitch, 1, 6, 2);
// Icon theme (+ submenu)
m_iconThemeButton.set_label("Icon Theme");
m_iconThemeButton.property_menu_name() = "icon-theme";
@ -837,7 +903,9 @@ void MainWindow::initSignals()
{
// Window signals
signal_delete_event().connect(sigc::mem_fun(this, &MainWindow::delete_window));
// Table of contents
m_closeTocWindowButton.signal_clicked().connect(sigc::mem_fun(m_vboxToc, &Gtk::Widget::hide));
tocTreeView.signal_row_activated().connect(sigc::mem_fun(this, &MainWindow::on_toc_row_activated));
// Menu & toolbar signals
m_menu.new_doc.connect(sigc::mem_fun(this, &MainWindow::new_doc)); /*!< Menu item for new document */
m_menu.open.connect(sigc::mem_fun(this, &MainWindow::open)); /*!< Menu item for opening existing document */
@ -847,8 +915,8 @@ void MainWindow::initSignals()
m_menu.save_as.connect(sigc::mem_fun(this, &MainWindow::save_as)); /*!< Menu item for save document as */
m_menu.publish.connect(sigc::mem_fun(this, &MainWindow::publish)); /*!< Menu item for publishing */
m_menu.quit.connect(sigc::mem_fun(this, &MainWindow::close)); /*!< close main window and therefor closes the app */
m_menu.undo.connect(sigc::mem_fun(m_draw_main, &Draw::undo)); /*!< Menu item for undo text */
m_menu.redo.connect(sigc::mem_fun(m_draw_main, &Draw::redo)); /*!< Menu item for redo text */
m_menu.undo.connect(sigc::mem_fun(m_draw_primary, &Draw::undo)); /*!< Menu item for undo text */
m_menu.redo.connect(sigc::mem_fun(m_draw_primary, &Draw::redo)); /*!< Menu item for redo text */
m_menu.cut.connect(sigc::mem_fun(this, &MainWindow::cut)); /*!< Menu item for cut text */
m_menu.copy.connect(sigc::mem_fun(this, &MainWindow::copy)); /*!< Menu item for copy text */
m_menu.paste.connect(sigc::mem_fun(this, &MainWindow::paste)); /*!< Menu item for paste text */
@ -860,12 +928,14 @@ void MainWindow::initSignals()
m_menu.forward.connect(sigc::mem_fun(this, &MainWindow::forward)); /*!< Menu item for next page */
m_menu.reload.connect(sigc::mem_fun(this, &MainWindow::refreshRequest)); /*!< Menu item for reloading the page */
m_menu.home.connect(sigc::mem_fun(this, &MainWindow::go_home)); /*!< Menu item for home page */
m_menu.toc.connect(sigc::mem_fun(this, &MainWindow::show_toc)); /*!< Menu item for table of contents */
m_menu.source_code.connect(sigc::mem_fun(this, &MainWindow::show_source_code_dialog)); /*!< Source code dialog */
m_sourceCodeDialog.signal_response().connect(sigc::mem_fun(m_sourceCodeDialog, &SourceCodeDialog::hide_dialog)); /*!< Close source code dialog */
m_menu.about.connect(sigc::mem_fun(m_about, &About::show_about)); /*!< Display about dialog */
m_draw_main.source_code.connect(sigc::mem_fun(this, &MainWindow::show_source_code_dialog)); /*!< Open source code dialog */
m_draw_primary.source_code.connect(sigc::mem_fun(this, &MainWindow::show_source_code_dialog)); /*!< Open source code dialog */
m_about.signal_response().connect(sigc::mem_fun(m_about, &About::hide_about)); /*!< Close about dialog */
m_addressBar.signal_activate().connect(sigc::mem_fun(this, &MainWindow::address_bar_activate)); /*!< User pressed enter the address bar */
m_openTocButton.signal_clicked().connect(sigc::mem_fun(this, &MainWindow::show_toc)); /*!< Button for showing Table of Contents */
m_backButton.signal_clicked().connect(sigc::mem_fun(this, &MainWindow::back)); /*!< Button for previous page */
m_forwardButton.signal_clicked().connect(sigc::mem_fun(this, &MainWindow::forward)); /*!< Button for next page */
m_refreshButton.signal_clicked().connect(sigc::mem_fun(this, &MainWindow::refreshRequest)); /*!< Button for reloading the page */
@ -879,22 +949,22 @@ void MainWindow::initSignals()
m_cutButton.signal_clicked().connect(sigc::mem_fun(this, &MainWindow::cut));
m_copyButton.signal_clicked().connect(sigc::mem_fun(this, &MainWindow::copy));
m_pasteButton.signal_clicked().connect(sigc::mem_fun(this, &MainWindow::paste));
m_undoButton.signal_clicked().connect(sigc::mem_fun(m_draw_main, &Draw::undo));
m_redoButton.signal_clicked().connect(sigc::mem_fun(m_draw_main, &Draw::redo));
m_undoButton.signal_clicked().connect(sigc::mem_fun(m_draw_primary, &Draw::undo));
m_redoButton.signal_clicked().connect(sigc::mem_fun(m_draw_primary, &Draw::redo));
m_headingsComboBox.signal_changed().connect(sigc::mem_fun(this, &MainWindow::get_heading));
m_boldButton.signal_clicked().connect(sigc::mem_fun(m_draw_main, &Draw::make_bold));
m_italicButton.signal_clicked().connect(sigc::mem_fun(m_draw_main, &Draw::make_italic));
m_strikethroughButton.signal_clicked().connect(sigc::mem_fun(m_draw_main, &Draw::make_strikethrough));
m_superButton.signal_clicked().connect(sigc::mem_fun(m_draw_main, &Draw::make_super));
m_subButton.signal_clicked().connect(sigc::mem_fun(m_draw_main, &Draw::make_sub));
m_linkButton.signal_clicked().connect(sigc::mem_fun(m_draw_main, &Draw::insert_link));
m_imageButton.signal_clicked().connect(sigc::mem_fun(m_draw_main, &Draw::insert_image));
m_boldButton.signal_clicked().connect(sigc::mem_fun(m_draw_primary, &Draw::make_bold));
m_italicButton.signal_clicked().connect(sigc::mem_fun(m_draw_primary, &Draw::make_italic));
m_strikethroughButton.signal_clicked().connect(sigc::mem_fun(m_draw_primary, &Draw::make_strikethrough));
m_superButton.signal_clicked().connect(sigc::mem_fun(m_draw_primary, &Draw::make_super));
m_subButton.signal_clicked().connect(sigc::mem_fun(m_draw_primary, &Draw::make_sub));
m_linkButton.signal_clicked().connect(sigc::mem_fun(m_draw_primary, &Draw::insert_link));
m_imageButton.signal_clicked().connect(sigc::mem_fun(m_draw_primary, &Draw::insert_image));
m_emojiButton.signal_clicked().connect(sigc::mem_fun(this, &MainWindow::insert_emoji));
m_quoteButton.signal_clicked().connect(sigc::mem_fun(m_draw_main, &Draw::make_quote));
m_codeButton.signal_clicked().connect(sigc::mem_fun(m_draw_main, &Draw::make_code));
m_bulletListButton.signal_clicked().connect(sigc::mem_fun(m_draw_main, &Draw::insert_bullet_list));
m_numberedListButton.signal_clicked().connect(sigc::mem_fun(m_draw_main, &Draw::insert_numbered_list));
m_highlightButton.signal_clicked().connect(sigc::mem_fun(m_draw_main, &Draw::make_highlight));
m_quoteButton.signal_clicked().connect(sigc::mem_fun(m_draw_primary, &Draw::make_quote));
m_codeButton.signal_clicked().connect(sigc::mem_fun(m_draw_primary, &Draw::make_code));
m_bulletListButton.signal_clicked().connect(sigc::mem_fun(m_draw_primary, &Draw::insert_bullet_list));
m_numberedListButton.signal_clicked().connect(sigc::mem_fun(m_draw_primary, &Draw::insert_numbered_list));
m_highlightButton.signal_clicked().connect(sigc::mem_fun(m_draw_primary, &Draw::make_highlight));
// Status pop-over buttons
m_copyIDButton.signal_clicked().connect(sigc::mem_fun(this, &MainWindow::copy_client_id));
m_copyPublicKeyButton.signal_clicked().connect(sigc::mem_fun(this, &MainWindow::copy_client_public_key));
@ -906,6 +976,10 @@ void MainWindow::initSignals()
m_spacingSpinButton.signal_value_changed().connect(sigc::mem_fun(this, &MainWindow::on_spacing_changed));
m_marginsSpinButton.signal_value_changed().connect(sigc::mem_fun(this, &MainWindow::on_margins_changed));
m_indentSpinButton.signal_value_changed().connect(sigc::mem_fun(this, &MainWindow::on_indent_changed));
m_wrapNone.signal_toggled().connect(sigc::bind(sigc::mem_fun(this, &MainWindow::on_wrap_toggled), Gtk::WrapMode::WRAP_NONE));
m_wrapChar.signal_toggled().connect(sigc::bind(sigc::mem_fun(this, &MainWindow::on_wrap_toggled), Gtk::WrapMode::WRAP_CHAR));
m_wrapWord.signal_toggled().connect(sigc::bind(sigc::mem_fun(this, &MainWindow::on_wrap_toggled), Gtk::WrapMode::WRAP_WORD));
m_wrapWordChar.signal_toggled().connect(sigc::bind(sigc::mem_fun(this, &MainWindow::on_wrap_toggled), Gtk::WrapMode::WRAP_WORD_CHAR));
m_themeSwitch.property_active().signal_changed().connect(sigc::mem_fun(this, &MainWindow::on_theme_changed));
m_iconThemeListBox.signal_row_activated().connect(sigc::mem_fun(this, &MainWindow::on_icon_theme_activated));
m_aboutButton.signal_clicked().connect(sigc::mem_fun(m_about, &About::show_about));
@ -922,10 +996,12 @@ bool MainWindow::delete_window(GdkEventAny* any_event __attribute__((unused)))
m_settings->set_int("width", get_width());
m_settings->set_int("height", get_height());
m_settings->set_boolean("maximized", is_maximized());
if (m_panedRoot.get_position() > 0)
m_settings->set_int("position-divider-toc", m_panedRoot.get_position());
// Only store a divider value bigger than zero,
// because the secondary draw window is hidden by default, resulting into a zero value.
if (m_paned.get_position() > 0)
m_settings->set_int("position-divider", m_paned.get_position());
if (m_panedDraw.get_position() > 0)
m_settings->set_int("position-divider-draw", m_panedDraw.get_position());
// Fullscreen will be availible with gtkmm-4.0
// m_settings->set_boolean("fullscreen", is_fullscreen());
m_settings->set_string("font-family", fontFamily_);
@ -946,9 +1022,9 @@ bool MainWindow::delete_window(GdkEventAny* any_event __attribute__((unused)))
*/
void MainWindow::cut()
{
if (m_draw_main.has_focus())
if (m_draw_primary.has_focus())
{
m_draw_main.cut();
m_draw_primary.cut();
}
else if (m_draw_secondary.has_focus())
{
@ -970,9 +1046,9 @@ void MainWindow::cut()
void MainWindow::copy()
{
if (m_draw_main.has_focus())
if (m_draw_primary.has_focus())
{
m_draw_main.copy();
m_draw_primary.copy();
}
else if (m_draw_secondary.has_focus())
{
@ -994,9 +1070,9 @@ void MainWindow::copy()
void MainWindow::paste()
{
if (m_draw_main.has_focus())
if (m_draw_primary.has_focus())
{
m_draw_main.paste();
m_draw_primary.paste();
}
else if (m_draw_secondary.has_focus())
{
@ -1018,9 +1094,9 @@ void MainWindow::paste()
void MainWindow::del()
{
if (m_draw_main.has_focus())
if (m_draw_primary.has_focus())
{
m_draw_main.del();
m_draw_primary.del();
}
else if (m_draw_secondary.has_focus())
{
@ -1069,9 +1145,9 @@ void MainWindow::del()
void MainWindow::selectAll()
{
if (m_draw_main.has_focus())
if (m_draw_primary.has_focus())
{
m_draw_main.selectAll();
m_draw_primary.selectAll();
}
else if (m_draw_secondary.has_focus())
{
@ -1091,6 +1167,27 @@ void MainWindow::selectAll()
}
}
/**
* \brief Triggered when user clicked on the column in ToC
*/
void MainWindow::on_toc_row_activated(const Gtk::TreeModel::Path& path, __attribute__((unused)) Gtk::TreeViewColumn* column)
{
const auto iter = m_tocTreeModel->get_iter(path);
if (iter)
{
const auto row = *iter;
if (row[m_tocColumns.m_col_valid])
{
Gtk::TextIter textIter = row[m_tocColumns.m_col_iter];
// Scroll to to mark iterator
if (isEditorEnabled())
m_draw_secondary.scroll_to(textIter);
else
m_draw_primary.scroll_to(textIter);
}
}
}
/**
* \brief Trigger when user selected 'new document' from menu item
*/
@ -1241,7 +1338,7 @@ void MainWindow::edit()
if (!isEditorEnabled())
enableEdit();
m_draw_main.setText(middleware_.getContent());
m_draw_primary.setText(middleware_.getContent());
// Set title
set_title("Untitled * - " + appName_);
}
@ -1430,6 +1527,17 @@ void MainWindow::go_home()
middleware_.doRequest("about:home", true, false, true);
}
/**
* \brief Show/hide table of contents
*/
void MainWindow::show_toc()
{
if (m_vboxToc.is_visible())
m_vboxToc.hide();
else
m_vboxToc.show();
}
/**
* \brief Copy the IPFS Client ID to clipboard
*/
@ -1469,7 +1577,7 @@ void MainWindow::on_search()
{
// Forward search, find and select
std::string text = m_searchEntry.get_text();
auto buffer = m_draw_main.get_buffer();
auto buffer = m_draw_primary.get_buffer();
Gtk::TextBuffer::iterator iter = buffer->get_iter_at_mark(buffer->get_mark("insert"));
Gtk::TextBuffer::iterator start, end;
bool matchCase = m_searchMatchCase.get_active();
@ -1481,7 +1589,7 @@ void MainWindow::on_search()
if (iter.forward_search(text, flags, start, end))
{
buffer->select_range(end, start);
m_draw_main.scroll_to(start);
m_draw_primary.scroll_to(start);
}
else
{
@ -1491,7 +1599,7 @@ void MainWindow::on_search()
if (secondIter.forward_search(text, flags, start, end))
{
buffer->select_range(end, start);
m_draw_main.scroll_to(start);
m_draw_primary.scroll_to(start);
}
}
}
@ -1501,9 +1609,9 @@ void MainWindow::on_search()
*/
void MainWindow::on_replace()
{
if (m_draw_main.get_editable())
if (m_draw_primary.get_editable())
{
auto buffer = m_draw_main.get_buffer();
auto buffer = m_draw_primary.get_buffer();
Gtk::TextBuffer::iterator startIter = buffer->get_iter_at_mark(buffer->get_mark("insert"));
Gtk::TextBuffer::iterator endIter = buffer->get_iter_at_mark(buffer->get_mark("selection_bound"));
if (startIter != endIter)
@ -1525,8 +1633,8 @@ void MainWindow::on_replace()
void MainWindow::address_bar_activate()
{
middleware_.doRequest(m_addressBar.get_text(), false);
// When user actually entered the address bar, focus on the main draw
m_draw_main.grab_focus();
// When user actually entered the address bar, focus on the primary draw
m_draw_primary.grab_focus();
}
/**
@ -1594,6 +1702,149 @@ void MainWindow::forward()
}
}
/**
* \brief Fill-in table of contents and show
*/
void MainWindow::setTableofContents(std::vector<Glib::RefPtr<Gtk::TextMark>> headings)
{
Gtk::TreeRow heading1Row, heading2Row, heading3Row, heading4Row, heading5Row;
int previousLevel = 1; // Default heading 1
for (const Glib::RefPtr<Gtk::TextMark> headerMark : headings)
{
Glib::ustring heading = static_cast<char*>(headerMark->get_data("name"));
auto level = reinterpret_cast<std::intptr_t>(headerMark->get_data("level"));
switch (level)
{
case 1:
{
heading1Row = *(m_tocTreeModel->append());
heading1Row[m_tocColumns.m_col_iter] = headerMark->get_iter();
heading1Row[m_tocColumns.m_col_level] = level;
heading1Row[m_tocColumns.m_col_heading] = heading;
heading1Row[m_tocColumns.m_col_valid] = true;
// Reset
if (previousLevel > 1)
{
heading2Row = Gtk::TreeRow();
heading3Row = Gtk::TreeRow();
heading4Row = Gtk::TreeRow();
heading5Row = Gtk::TreeRow();
}
break;
}
case 2:
{
if (heading1Row->get_model_gobject() == nullptr)
{
// Add missing heading as top-level
heading1Row = *(m_tocTreeModel->append());
heading1Row[m_tocColumns.m_col_level] = 1;
heading1Row[m_tocColumns.m_col_heading] = "-Missing heading-";
heading1Row[m_tocColumns.m_col_valid] = false;
}
heading2Row = *(m_tocTreeModel->append(heading1Row.children()));
heading2Row[m_tocColumns.m_col_iter] = headerMark->get_iter();
heading2Row[m_tocColumns.m_col_level] = level;
heading2Row[m_tocColumns.m_col_heading] = heading;
heading2Row[m_tocColumns.m_col_valid] = true;
// Reset
if (previousLevel > 2)
{
heading3Row = Gtk::TreeRow();
heading4Row = Gtk::TreeRow();
heading5Row = Gtk::TreeRow();
}
break;
}
case 3:
{
if (heading2Row->get_model_gobject() == nullptr)
{
// Add missing heading as top-level
heading2Row = *(m_tocTreeModel->append(heading1Row.children()));
heading2Row[m_tocColumns.m_col_level] = 2;
heading2Row[m_tocColumns.m_col_heading] = "-Missing heading-";
heading2Row[m_tocColumns.m_col_valid] = false;
}
heading3Row = *(m_tocTreeModel->append(heading2Row.children()));
heading3Row[m_tocColumns.m_col_iter] = headerMark->get_iter();
heading3Row[m_tocColumns.m_col_level] = level;
heading3Row[m_tocColumns.m_col_heading] = heading;
heading3Row[m_tocColumns.m_col_valid] = true;
// Reset
if (previousLevel > 3)
{
heading4Row = Gtk::TreeRow();
heading5Row = Gtk::TreeRow();
}
break;
}
case 4:
{
if (heading3Row->get_model_gobject() == nullptr)
{
// Add missing heading as top-level
heading3Row = *(m_tocTreeModel->append(heading2Row.children()));
heading3Row[m_tocColumns.m_col_level] = 3;
heading3Row[m_tocColumns.m_col_heading] = "-Missing heading-";
heading3Row[m_tocColumns.m_col_valid] = false;
}
heading4Row = *(m_tocTreeModel->append(heading3Row.children()));
heading4Row[m_tocColumns.m_col_iter] = headerMark->get_iter();
heading4Row[m_tocColumns.m_col_level] = level;
heading4Row[m_tocColumns.m_col_heading] = heading;
heading4Row[m_tocColumns.m_col_valid] = true;
// Reset
if (previousLevel > 4)
{
heading5Row = Gtk::TreeRow();
}
break;
}
case 5:
{
if (heading4Row->get_model_gobject() == nullptr)
{
// Add missing heading as top-level
heading4Row = *(m_tocTreeModel->append(heading3Row.children()));
heading4Row[m_tocColumns.m_col_level] = 4;
heading4Row[m_tocColumns.m_col_heading] = "-Missing heading-";
heading4Row[m_tocColumns.m_col_valid] = false;
}
heading5Row = *(m_tocTreeModel->append(heading4Row.children()));
heading5Row[m_tocColumns.m_col_iter] = headerMark->get_iter();
heading5Row[m_tocColumns.m_col_level] = level;
heading5Row[m_tocColumns.m_col_heading] = heading;
heading5Row[m_tocColumns.m_col_valid] = true;
break;
}
case 6:
{
if (heading5Row->get_model_gobject() == nullptr)
{
// Add missing heading as top-level
heading5Row = *(m_tocTreeModel->append(heading4Row.children()));
heading5Row[m_tocColumns.m_col_level] = 5;
heading5Row[m_tocColumns.m_col_heading] = "- Missing heading -";
heading5Row[m_tocColumns.m_col_valid] = false;
}
auto heading6Row = *(m_tocTreeModel->append(heading5Row.children()));
heading6Row[m_tocColumns.m_col_iter] = headerMark->get_iter();
heading6Row[m_tocColumns.m_col_level] = level;
heading6Row[m_tocColumns.m_col_heading] = heading;
heading6Row[m_tocColumns.m_col_valid] = true;
break;
}
default:
std::cerr << "ERROR: Out of range heading level detected." << std::endl;
break;
}
previousLevel = level;
}
tocTreeView.columns_autosize();
tocTreeView.expand_all();
}
/**
* \brief Determing if browser is installed to the installation directory at runtime
* \return true if the current running process is installed (to the installed prefix path)
@ -1636,7 +1887,7 @@ bool MainWindow::isInstalled()
void MainWindow::enableEdit()
{
// Inform the Draw class that we are creating a new document
m_draw_main.newDocument();
m_draw_primary.newDocument();
// Show editor toolbars
m_hboxStandardEditorToolbar.show();
m_hboxFormattingEditorToolbar.show();
@ -1644,7 +1895,7 @@ void MainWindow::enableEdit()
int location = 0;
int positionSettings = 42;
if (m_settings)
positionSettings = m_settings->get_int("position-divider");
positionSettings = m_settings->get_int("position-divider-draw");
int currentWidth, _ = 0;
get_size(currentWidth, _);
// If position from settings is still default (42) or too big,
@ -1657,14 +1908,14 @@ void MainWindow::enableEdit()
{
location = positionSettings;
}
m_paned.set_position(location);
m_panedDraw.set_position(location);
// Enabled secondary text view (on the right)
m_scrolledWindowSecondary.show();
// Disable "view source" menu item
m_draw_main.setViewSourceMenuItem(false);
m_draw_primary.setViewSourceMenuItem(false);
// Connect changed signal
textChangedSignalHandler_ = m_draw_main.get_buffer().get()->signal_changed().connect(sigc::mem_fun(this, &MainWindow::editor_changed_text));
textChangedSignalHandler_ = m_draw_primary.get_buffer()->signal_changed().connect(sigc::mem_fun(this, &MainWindow::editor_changed_text));
// Enable publish menu item
m_menu.setPublishMenuSensitive(true);
// Disable edit menu item (you are already editing)
@ -1686,8 +1937,8 @@ void MainWindow::disableEdit()
// Disconnect text changed signal
textChangedSignalHandler_.disconnect();
// Show "view source" menu item again
m_draw_main.setViewSourceMenuItem(true);
m_draw_secondary.clearText();
m_draw_primary.setViewSourceMenuItem(true);
m_draw_secondary.clear();
// Disable publish menu item
m_menu.setPublishMenuSensitive(false);
// Enable edit menu item
@ -1740,7 +1991,7 @@ std::string MainWindow::getIconImageFromTheme(const std::string& iconName, const
}
/**
* \brief Update the CSS provider data on the main draw text view
* \brief Update the CSS provider data
*/
void MainWindow::updateCSS()
{
@ -1799,14 +2050,17 @@ void MainWindow::editor_changed_text()
// TODO: Just execute the code below in a signal_idle call?
// So it will never block the GUI thread. Or is this already running in another context
// Clear table of contents (ToC)
m_tocTreeModel->clear();
// Retrieve text from editor and parse the markdown contents
middleware_.setContent(m_draw_main.getText());
middleware_.setContent(m_draw_primary.getText());
cmark_node* doc = middleware_.parseContent();
/* // Can be enabled to show the markdown format in terminal:
std::string md = Parser::renderMarkdown(doc);
std::cout << "Markdown:\n" << md << std::endl;*/
// Show the document as a preview on the right side text-view panel
m_draw_secondary.setDocument(doc);
setTableofContents(m_draw_secondary.getHeadings());
}
/**
@ -1832,7 +2086,7 @@ void MainWindow::get_heading()
try
{
int headingLevel = std::stoi(active, &sz, 10);
m_draw_main.make_heading(headingLevel);
m_draw_primary.make_heading(headingLevel);
}
catch (const std::invalid_argument&)
{
@ -1849,7 +2103,7 @@ void MainWindow::get_heading()
void MainWindow::insert_emoji()
{
// Note: The "insert-emoji" signal is not exposed in Gtkmm library (at least not in gtk3)
g_signal_emit_by_name(m_draw_main.gobj(), "insert-emoji");
g_signal_emit_by_name(m_draw_primary.gobj(), "insert-emoji");
}
void MainWindow::on_zoom_out()
@ -1889,13 +2143,18 @@ void MainWindow::on_spacing_changed()
void MainWindow::on_margins_changed()
{
m_draw_main.set_left_margin(m_marginsSpinButton.get_value_as_int());
m_draw_main.set_right_margin(m_marginsSpinButton.get_value_as_int());
m_draw_primary.set_left_margin(m_marginsSpinButton.get_value_as_int());
m_draw_primary.set_right_margin(m_marginsSpinButton.get_value_as_int());
}
void MainWindow::on_indent_changed()
{
m_draw_main.set_indent(m_indentSpinButton.get_value_as_int());
m_draw_primary.set_indent(m_indentSpinButton.get_value_as_int());
}
void MainWindow::on_wrap_toggled(Gtk::WrapMode mode)
{
m_draw_primary.set_wrap_mode(mode);
}
void MainWindow::on_brightness_changed()

View File

@ -6,6 +6,7 @@
#include "menu.h"
#include "middleware.h"
#include "source-code-dialog.h"
#include "toc-model-cols.h"
#include "toolbar-button.h"
#include <giomm/settings.h>
@ -26,6 +27,7 @@
#include <gtkmm/paned.h>
#include <gtkmm/popover.h>
#include <gtkmm/popovermenu.h>
#include <gtkmm/radiobutton.h>
#include <gtkmm/scale.h>
#include <gtkmm/scrolledwindow.h>
#include <gtkmm/searchbar.h>
@ -34,6 +36,9 @@
#include <gtkmm/spinbutton.h>
#include <gtkmm/switch.h>
#include <gtkmm/togglebutton.h>
#include <gtkmm/treemodel.h>
#include <gtkmm/treestore.h>
#include <gtkmm/treeview.h>
#include <gtkmm/window.h>
#include <sigc++/connection.h>
@ -53,7 +58,7 @@ public:
void startedRequest();
void finishedRequest();
void refreshRequest();
void showStartpage();
void showHomepage();
void setText(const Glib::ustring& content);
void setDocument(cmark_node* rootNode);
void setMessage(const Glib::ustring& message, const Glib::ustring& details = "");
@ -67,6 +72,7 @@ protected:
void paste();
void del();
void selectAll();
void on_toc_row_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column);
void new_doc();
void open();
void open_and_edit();
@ -78,6 +84,7 @@ protected:
void on_save_as_dialog_response(int response_id, Gtk::FileChooserDialog* dialog);
void publish();
void go_home();
void show_toc();
void copy_client_id();
void copy_client_public_key();
void address_bar_activate();
@ -100,6 +107,7 @@ protected:
void on_spacing_changed();
void on_margins_changed();
void on_indent_changed();
void on_wrap_toggled(Gtk::WrapMode mode);
void on_brightness_changed();
void on_theme_changed();
void on_icon_theme_activated(Gtk::ListBoxRow* row);
@ -114,20 +122,25 @@ protected:
// Child widgets
Menu m_menu;
Draw m_draw_main;
Draw m_draw_primary;
Draw m_draw_secondary;
SourceCodeDialog m_sourceCodeDialog;
About m_about;
Gtk::HPaned m_paned;
Gtk::TreeView tocTreeView;
Glib::RefPtr<Gtk::TreeStore> m_tocTreeModel;
Gtk::HPaned m_panedRoot;
Gtk::HPaned m_panedDraw;
Gtk::SearchBar m_search;
Gtk::SearchBar m_searchReplace;
Gtk::SearchEntry m_searchEntry;
Gtk::Entry m_searchReplaceEntry;
Gtk::Box m_vbox;
Gtk::Box m_vboxMain;
Gtk::Box m_hboxBrowserToolbar;
Gtk::Box m_hboxStandardEditorToolbar;
Gtk::Box m_hboxFormattingEditorToolbar;
Gtk::Box m_hboxSearch;
Gtk::Box m_hboxToc;
Gtk::Box m_vboxToc;
Gtk::Box m_vboxSearch;
Gtk::Box m_vboxStatus;
Gtk::Box m_vboxSettings;
@ -146,12 +159,19 @@ protected:
Gtk::SpinButton m_spacingSpinButton;
Gtk::SpinButton m_marginsSpinButton;
Gtk::SpinButton m_indentSpinButton;
Gtk::RadioButton::Group m_wrappingGroup;
Gtk::RadioButton m_wrapNone;
Gtk::RadioButton m_wrapChar;
Gtk::RadioButton m_wrapWord;
Gtk::RadioButton m_wrapWordChar;
Gtk::ModelButton m_iconThemeButton;
Gtk::ModelButton m_aboutButton;
Gtk::ModelButton m_iconThemeBackButton;
Gtk::Grid m_statusGrid;
Gtk::Grid m_activityStatusGrid;
Gtk::Grid m_settingsGrid;
ToolbarButton m_closeTocWindowButton;
ToolbarButton m_openTocButton;
ToolbarButton m_backButton;
ToolbarButton m_forwardButton;
ToolbarButton m_refreshButton;
@ -184,6 +204,7 @@ protected:
Gtk::Image m_zoomOutImage;
Gtk::Image m_zoomInImage;
Gtk::Image m_brightnessImage;
Gtk::Image m_tocIcon;
Gtk::Image m_backIcon;
Gtk::Image m_forwardIcon;
Gtk::Image m_refreshIcon;
@ -220,6 +241,7 @@ protected:
Gtk::ModelButton m_copyIDButton;
Gtk::ModelButton m_copyPublicKeyButton;
Gtk::Switch m_themeSwitch;
Gtk::Label m_tableOfContentsLabel;
Gtk::Label m_networkHeadingLabel;
Gtk::Label m_networkRateHeadingLabel;
Gtk::Label m_connectivityLabel;
@ -241,10 +263,12 @@ protected:
Gtk::Label m_spacingLabel;
Gtk::Label m_marginsLabel;
Gtk::Label m_indentLabel;
Gtk::Label m_textWrappingLabel;
Gtk::Label m_themeLabel;
Gtk::Label m_iconThemeLabel;
std::unique_ptr<Gtk::MessageDialog> m_contentPublishedDialog;
Gtk::ScrolledWindow m_scrolledWindowMain;
Gtk::ScrolledWindow m_scrolledToc;
Gtk::ScrolledWindow m_scrolledWindowPrimary;
Gtk::ScrolledWindow m_scrolledWindowSecondary;
Gtk::SeparatorMenuItem m_separator1;
Gtk::SeparatorMenuItem m_separator2;
@ -256,6 +280,7 @@ protected:
Gtk::Separator m_separator8;
Gtk::Separator m_separator9;
Gtk::Separator m_separator10;
TocModelCols m_tocColumns;
private:
Middleware middleware_;
@ -277,13 +302,15 @@ private:
void loadStoredSettings();
void setGTKIcons();
void loadIcons();
void initButtons();
void initToolbarButtons();
void setTheme();
void initSearchPopover();
void initStatusPopover();
void initSettingsPopover();
void initTableofContents();
void initSignals();
bool isInstalled();
void setTableofContents(std::vector<Glib::RefPtr<Gtk::TextMark>> headings);
void enableEdit();
void disableEdit();
bool isEditorEnabled();

View File

@ -78,6 +78,10 @@ Menu::Menu(const Glib::RefPtr<Gtk::AccelGroup>& accelgroup)
auto homePageMenuItem = createMenuItem("_Homepage");
homePageMenuItem->add_accelerator("activate", accelgroup, GDK_KEY_Home, Gdk::ModifierType::MOD1_MASK, Gtk::AccelFlags::ACCEL_VISIBLE);
homePageMenuItem->signal_activate().connect(home);
auto tocMenuItem = createMenuItem("_Table of Contents");
tocMenuItem->add_accelerator("activate", accelgroup, GDK_KEY_T, Gdk::ModifierType::CONTROL_MASK | Gdk::ModifierType::SHIFT_MASK,
Gtk::AccelFlags::ACCEL_VISIBLE);
tocMenuItem->signal_activate().connect(toc);
auto sourceCodeMenuItem = createMenuItem("View _Source");
sourceCodeMenuItem->signal_activate().connect(source_code);
@ -114,6 +118,8 @@ Menu::Menu(const Glib::RefPtr<Gtk::AccelGroup>& accelgroup)
m_viewSubmenu.append(*reloadMenuItem);
m_viewSubmenu.append(*homePageMenuItem);
m_viewSubmenu.append(m_separator7);
m_viewSubmenu.append(*tocMenuItem);
m_viewSubmenu.append(m_separator8);
m_viewSubmenu.append(*sourceCodeMenuItem);
m_helpSubmenu.append(*aboutMenuItem);

View File

@ -35,6 +35,7 @@ public:
sigc::signal<void> forward;
sigc::signal<void> reload;
sigc::signal<void> home;
sigc::signal<void> toc;
sigc::signal<void> source_code;
sigc::signal<void> about;
@ -62,6 +63,7 @@ protected:
Gtk::SeparatorMenuItem m_separator5;
Gtk::SeparatorMenuItem m_separator6;
Gtk::SeparatorMenuItem m_separator7;
Gtk::SeparatorMenuItem m_separator8;
private:
Gtk::MenuItem* createMenuItem(const Glib::ustring& label_text);

View File

@ -254,7 +254,7 @@ void Middleware::processRequest(const std::string& path, bool isParseContent)
// Handle homepage
else if (requestPath_.compare("about:home") == 0)
{
Glib::signal_idle().connect_once(sigc::mem_fun(mainWindow, &MainWindow::showStartpage));
Glib::signal_idle().connect_once(sigc::mem_fun(mainWindow, &MainWindow::showHomepage));
}
// Handle disk or IPFS file paths
else

View File

@ -17,9 +17,13 @@
<default>false</default>
<summary>Is window fullscreen</summary>
</key>
<key name="position-divider" type="i">
<key name="position-divider-toc" type="i">
<default>300</default>
<summary>Position of paned divider for ToC and draw windows</summary>
</key>
<key name="position-divider-draw" type="i">
<default>42</default>
<summary>Position of paned divider</summary>
<summary>Position of paned divider for primary and secondary draw window</summary>
</key>
<key name="font-family" type="s">
<default>"Sans"</default>

27
src/toc-model-cols.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef TOC_MODEL_COLS_H
#define TOC_MODEL_COLS_H
#include <gtkmm/treemodel.h>
/**
* \class TocModelCols
* \brief Table of Contents Model Columns
*/
class TocModelCols : public Gtk::TreeModel::ColumnRecord
{
public:
TocModelCols()
{
add(m_col_iter);
add(m_col_level);
add(m_col_heading);
add(m_col_valid);
}
Gtk::TreeModelColumn<Gtk::TextIter> m_col_iter;
Gtk::TreeModelColumn<int> m_col_level;
Gtk::TreeModelColumn<Glib::ustring> m_col_heading;
Gtk::TreeModelColumn<bool> m_col_valid;
};
#endif