diff options
Diffstat (limited to 'src/ui/desktop')
-rw-r--r-- | src/ui/desktop/README | 27 | ||||
-rw-r--r-- | src/ui/desktop/document-check.cpp | 140 | ||||
-rw-r--r-- | src/ui/desktop/document-check.h | 28 | ||||
-rw-r--r-- | src/ui/desktop/menu-icon-shift.cpp | 153 | ||||
-rw-r--r-- | src/ui/desktop/menu-icon-shift.h | 46 | ||||
-rw-r--r-- | src/ui/desktop/menubar.cpp | 322 | ||||
-rw-r--r-- | src/ui/desktop/menubar.h | 49 |
7 files changed, 765 insertions, 0 deletions
diff --git a/src/ui/desktop/README b/src/ui/desktop/README new file mode 100644 index 0000000..e2932ca --- /dev/null +++ b/src/ui/desktop/README @@ -0,0 +1,27 @@ + + +This directory contains code related to the Inkscape desktop, that is +code that is directly used by the InkscapeWindow class and in linking +the desktop to the canvas. It should not contain basic widgets, +dialogs, toolbars, etc. + +To do: + +* widgets/desktop-widget.h/cpp should disappear with code ending up in either + InkscapeWindow.h/cpp or desktop.h/cpp (or in new files). + +* ui/view/view-widget.h/cpp should disappear ('view' should be member of window) + +* desktop.h/cpp should only contain code that links the desktop to the canvas. + +* Convert GUI to use actions where possible. + +* Future Structure: + Main menu bar (menubar.h/.cpp) + Tool bar + Multipaned widget containing + Dialogs + Tools + Canvas + Palette (maybe turn into dialog). + Status bar diff --git a/src/ui/desktop/document-check.cpp b/src/ui/desktop/document-check.cpp new file mode 100644 index 0000000..6a8a809 --- /dev/null +++ b/src/ui/desktop/document-check.cpp @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Check for data loss when closing a document window. + * + * Copyright (C) 2004-2021 Authors + * + * The contents of this file may be used under the GNU General Public License Version 2 or later. + * + */ + +/* Authors: + * MenTaLguY + * Link Mauve + * Thomas Holder + * Tavmjong Bah + */ + +#include "document-check.h" + +#include <glibmm/i18n.h> // Internationalization +#include <gtkmm.h> + +#include "inkscape-window.h" +#include "object/sp-namedview.h" + +#include "file.h" +#include "extension/system.h" // Inkscape::Extension::FILE... + +/** Check if closing document associated with window will cause data loss, and if so opens a dialog + * that gives user options to save or ignore. + * + * Returns true if document should remain open. + */ +bool +document_check_for_data_loss(InkscapeWindow* window) +{ + auto document = window->get_document(); + + if (document->isModifiedSinceSave()) { + // Document has been modified! + + Glib::ustring message = g_markup_printf_escaped( + _("<span weight=\"bold\" size=\"larger\">Save changes to document \"%s\" before closing?</span>\n\n" + "If you close without saving, your changes will be discarded."), + document->getDocumentName()); + + Gtk::MessageDialog dialog = + Gtk::MessageDialog(*window, message, true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE); + dialog.property_destroy_with_parent() = true; + + // Don't allow text to be selected (via tabbing). + Gtk::Container *ma = dialog.get_message_area(); + std::vector<Gtk::Widget*> ma_labels = ma->get_children(); + ma_labels[0]->set_can_focus(false); + + dialog.add_button(_("Close _without saving"), Gtk::RESPONSE_NO); + dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL); + dialog.add_button(_("_Save"), Gtk::RESPONSE_YES); + dialog.set_default_response(Gtk::RESPONSE_YES); + + int response = dialog.run(); + + switch (response) { + case GTK_RESPONSE_YES: + { + // Save document + sp_namedview_document_from_window(window->get_desktop()); // Save window geometry in document. + if (!sp_file_save_document(*window, document)) { + // Save dialog cancelled or save failed. + return true; + } + break; + } + case GTK_RESPONSE_NO: + break; + default: // cancel pressed, or dialog was closed + return true; + break; + } + } + + // Check for data loss due to saving in lossy format. + bool allow_data_loss = false; + while (document->getReprRoot()->attribute("inkscape:dataloss") != nullptr && allow_data_loss == false) { + // This loop catches if the user saves to a lossy format when in the loop. + + Glib::ustring message = g_markup_printf_escaped( + _("<span weight=\"bold\" size=\"larger\">The file \"%s\" was saved with a format that may cause data loss!</span>\n\n" + "Do you want to save this file as Inkscape SVG?"), + document->getDocumentName() ? document->getDocumentName() : "Unnamed"); + + Gtk::MessageDialog dialog = + Gtk::MessageDialog(*window, message, true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE); + dialog.property_destroy_with_parent() = true; + + // Don't allow text to be selected (via tabbing). + Gtk::Container *ma = dialog.get_message_area(); + std::vector<Gtk::Widget*> ma_labels = ma->get_children(); + ma_labels[0]->set_can_focus(false); + + dialog.add_button(_("Close _without saving"), Gtk::RESPONSE_NO); + dialog.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL); + dialog.add_button(_("_Save as Inkscape SVG"), Gtk::RESPONSE_YES); + dialog.set_default_response(Gtk::RESPONSE_YES); + + int response = dialog.run(); + + switch (response) { + case GTK_RESPONSE_YES: + { + if (!sp_file_save_dialog(*window, document, Inkscape::Extension::FILE_SAVE_METHOD_INKSCAPE_SVG)) { + // Save dialog cancelled or save failed. + return TRUE; + } + + break; + } + case GTK_RESPONSE_NO: + allow_data_loss = true; + break; + default: // cancel pressed, or dialog was closed + return true; + break; + } + } + + return false; +} + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/ui/desktop/document-check.h b/src/ui/desktop/document-check.h new file mode 100644 index 0000000..3fa1e5d --- /dev/null +++ b/src/ui/desktop/document-check.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Check for data loss when closing a document window. + * + * Copyright (C) 2021 Tavmjong Bah + * + * The contents of this file may be used under the GNU General Public License Version 2 or later. + * + */ + +#ifndef DOCUMENT_CHECK_H + +class InkscapeWindow; + +bool document_check_for_data_loss(InkscapeWindow* window); + +#endif // DOCUMENT_CHECK_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/ui/desktop/menu-icon-shift.cpp b/src/ui/desktop/menu-icon-shift.cpp new file mode 100644 index 0000000..0da36d5 --- /dev/null +++ b/src/ui/desktop/menu-icon-shift.cpp @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * @file + * Shift Gtk::MenuItems with icons to align with Toggle and Radio buttons. + */ +/* + * Authors: + * Tavmjong Bah <tavmjong@free.fr> + * Patrick Storz <eduard.braun2@gmx.de> + * + * Copyright (C) 2020 Authors + * + * The contents of this file may be used under the GNU General Public License Version 2 or later. + * Read the file 'COPYING' for more information. + * + */ + +#include "menu-icon-shift.h" + +#include <iostream> +#include <gtkmm.h> + +#include "inkscape-application.h" // Action extra data + +// Could be used to update status bar. +// bool on_enter_notify(GdkEventCrossing* crossing_event, Gtk::MenuItem* menuitem) +// { +// return false; +// } + +/* + * Install CSS to shift icons into the space reserved for toggles (i.e. check and radio items). + * The CSS will apply to all menu icons but is updated as each menu is shown. + */ +void +shift_icons(Gtk::MenuShell* menu) +{ + static Glib::RefPtr<Gtk::CssProvider> provider; + if (!provider) { + provider = Gtk::CssProvider::create(); + auto const screen = Gdk::Screen::get_default(); + Gtk::StyleContext::add_provider_for_screen(screen, provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + } + + // Calculate required shift. We need an example! + // Search for Gtk::MenuItem -> Gtk::Box -> Gtk::Image + for (auto child : menu->get_children()) { + + auto menuitem = dynamic_cast<Gtk::MenuItem *>(child); + if (menuitem) { + + auto box = dynamic_cast<Gtk::Box *>(menuitem->get_child()); + if (box) { + + box->set_spacing(0); // Match ImageMenuItem + + auto children = box->get_children(); + if (children.size() == 2) { // Image + Label + + auto image = dynamic_cast<Gtk::Image *>(box->get_children()[0]); + if (image) { + + // OK, we have an example, do calculation. + auto allocation_menuitem = menuitem->get_allocation(); + auto allocation_image = image->get_allocation(); + + int shift = -allocation_image.get_x(); + if (menuitem->get_direction() == Gtk::TEXT_DIR_RTL) { + shift += (allocation_menuitem.get_width() - allocation_image.get_width()); + } + + static int current_shift = 0; + if (std::abs(current_shift - shift) > 2) { + // Only do this once per menu, and only if there is a large change. + current_shift = shift; + std::string css_str; + if (menuitem->get_direction() == Gtk::TEXT_DIR_RTL) { + css_str = "menuitem box image {margin-right:" + std::to_string(shift) + "px;}"; + } else { + css_str = "menuitem box image {margin-left:" + std::to_string(shift) + "px;}"; + } + provider->load_from_data(css_str); + } + } + } + } + } + } + // If we get here, it means there were no examples... but we don't care as there are no icons to shift anyway. +} + +/* + * Find all submenus to add "shift_icons" callback. We need to do this for + * all submenus as some submenus are children of other submenus without icons. + */ +void +shift_icons_recursive(Gtk::MenuShell *menu) +{ + if (menu) { + + // Connect signal + menu->signal_map().connect(sigc::bind<Gtk::MenuShell *>(sigc::ptr_fun(&shift_icons), menu)); + + static auto app = InkscapeApplication::instance(); + auto label_to_tooltip_map = app->get_menu_label_to_tooltip_map(); + + // Look for descendent menus. + auto children = menu->get_children(); // Should be Gtk::MenuItem's + for (auto child : children) { + auto menuitem = dynamic_cast<Gtk::MenuItem *>(child); + if (menuitem) { + + // Use label as alternative way to figure out tooltip. + auto label = menuitem->get_label(); + if (label.empty()) { + auto container = menuitem->get_child(); + auto box = dynamic_cast<Gtk::Box *>(container); + if (box) { + std::vector<Gtk::Widget *> children = box->get_children(); + if (children.size() == 2) { + auto label_widget = dynamic_cast<Gtk::Label *>(children[1]); + if (label_widget) { + label = label_widget->get_label(); + } + } + } + } + + auto it = label_to_tooltip_map.find(label); + if (it != label_to_tooltip_map.end()) { + menuitem->set_tooltip_text(it->second); + } + + auto submenu = menuitem->get_submenu(); + if (submenu) { + shift_icons_recursive(submenu); + } + } + } + } +} + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/desktop/menu-icon-shift.h b/src/ui/desktop/menu-icon-shift.h new file mode 100644 index 0000000..78462a8 --- /dev/null +++ b/src/ui/desktop/menu-icon-shift.h @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#ifndef SEEN_DESKTOP_MENU_ITEM_SHIFT_H +#define SEEN_DESKTOP_MENU_ITEM_SHIFT_H + +/** + * @file + * Shift Gtk::MenuItems with icons to align with Toggle and Radio buttons. + */ +/* + * Authors: + * Tavmjong Bah <tavmjong@free.fr> + * Patrick Storz <eduard.braun2@gmx.de> + * + * Copyright (C) 2020 Authors + * + * The contents of this file may be used under the GNU General Public License Version 2 or later. + * Read the file 'COPYING' for more information. + * + */ + +namespace Gtk { + class MenuShell; +} + +/** + * Call back to shift icons into place reserved for toggles (i.e. check and radio items). + */ +void shift_icons(Gtk::MenuShell *menu); + +/** + * Add callbacks recursively to menus. + */ +void shift_icons_recursive(Gtk::MenuShell *menu); + +#endif // SEEN_DESKTOP_MENU_ITEM_SHIFT_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/desktop/menubar.cpp b/src/ui/desktop/menubar.cpp new file mode 100644 index 0000000..a686070 --- /dev/null +++ b/src/ui/desktop/menubar.cpp @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * @file + * Desktop main menu bar code. + */ +/* + * Authors: + * Tavmjong Bah <tavmjong@free.fr> + * Alex Valavanis <valavanisalex@gmail.com> + * Patrick Storz <eduard.braun2@gmx.de> + * Krzysztof KosiĆski <tweenk.pl@gmail.com> + * Kris De Gussem <Kris.DeGussem@gmail.com> + * Sushant A.A. <sushant.co19@gmail.com> + * + * Copyright (C) 2018 Authors + * + * The contents of this file may be used under the GNU General Public License Version 2 or later. + * Read the file 'COPYING' for more information. + * + */ + +#include "menubar.h" + +#include <iostream> +#include <iomanip> +#include <map> +#include <regex> + +#include <glibmm/i18n.h> + +#include "inkscape-application.h" // Open recent +#include "preferences.h" // Use icons or not +#include "io/resource.h" // UI File location + +// =================== Main Menu ================ +void +build_menu() +{ + std::string filename = Inkscape::IO::Resource::get_filename(Inkscape::IO::Resource::UIS, "menus.ui"); + auto refBuilder = Gtk::Builder::create(); + + try + { + refBuilder->add_from_file(filename); + } + catch (const Glib::Error& err) + { + std::cerr << "build_menu: failed to load Main menu from: " + << filename <<": " + << err.what().raw() << std::endl; + } + + const auto object = refBuilder->get_object("menus"); +#if GTK_CHECK_VERSION(4, 0 ,0) + const auto gmenu = std::dynamic_pointer_cast<Gio::Menu>(object); +#else + const auto gmenu = Glib::RefPtr<Gio::Menu>::cast_dynamic(object); +#endif + + if (!gmenu) { + std::cerr << "build_menu: failed to build Main menu!" << std::endl; + } else { + + static auto app = InkscapeApplication::instance(); + std::map<Glib::ustring, Glib::ustring>& label_to_tooltip_map = app->get_menu_label_to_tooltip_map(); + label_to_tooltip_map.clear(); + + { // Filters and Extensions + + auto effects_object = refBuilder->get_object("effect-menu-effects"); + auto filters_object = refBuilder->get_object("filter-menu-filters"); + auto effects_menu = Glib::RefPtr<Gio::Menu>::cast_dynamic(effects_object); + auto filters_menu = Glib::RefPtr<Gio::Menu>::cast_dynamic(filters_object); + + if (!filters_menu) { + std::cerr << "build_menu(): Couldn't find Filters menu entry!" << std::endl; + } + if (!effects_menu) { + std::cerr << "build_menu(): Couldn't find Extensions menu entry!" << std::endl; + } + + std::map<Glib::ustring, Glib::RefPtr<Gio::Menu>> submenus; + + for (auto &[ entry_id, submenu_name_list, entry_name ] : app->get_action_effect_data().give_all_data()) + { + if (submenu_name_list.size() > 0) { + + // Effect data is used for both filters menu and extensions menu... we need to + // add to correct menu. 'submenu_name_list' either starts with 'Effects' or 'Filters'. + // Note "Filters" is translated! + Glib::ustring path; // Only used as index to map of submenus. + auto top_menu = filters_menu; + if (submenu_name_list.front() == "Effects") { + top_menu = effects_menu; + path += "Effects"; + } else { + path += "Filters"; + } + submenu_name_list.pop_front(); + + if (top_menu) { // It's possible that the menu doesn't exist (Kid's Inkscape?) + auto current_menu = top_menu; + for (auto &submenu_name : submenu_name_list) { + path += submenu_name + "-"; + auto it = submenus.find(path); + if (it == submenus.end()) { + auto new_gsubmenu = Gio::Menu::create(); + submenus[path] = new_gsubmenu; + current_menu->append_submenu(submenu_name, new_gsubmenu); + current_menu = new_gsubmenu; + } else { + current_menu = it->second; + } + } + current_menu->append(entry_name, "app." + entry_id); + } else { + std::cerr << "build_menu(): menu doesn't exist!" << std::endl; // Warn for now. + } + } + } + } + + // Recent file + auto recent_manager = Gtk::RecentManager::get_default(); + + auto sub_object = refBuilder->get_object("recent-files"); + auto sub_gmenu = Glib::RefPtr<Gio::Menu>::cast_dynamic(sub_object); + auto recent_menu_quark = Glib::Quark("recent-manager"); + sub_gmenu->set_data(recent_menu_quark, recent_manager.get()); // mark submenu, so we can find it + + auto rebuild = [](Glib::RefPtr<Gio::Menu> submenu) { + auto recent_manager = Gtk::RecentManager::get_default(); + submenu->remove_all(); + int max_files = Inkscape::Preferences::get()->getInt("/options/maxrecentdocuments/value"); + if (max_files <= 0) { + return; + } + + auto recent_files = recent_manager->get_items(); // all recent files not necessarily inkscape only + // sort by "last modified" time, which puts the most recently opened files first + std::sort(begin(recent_files), end(recent_files), + [](Glib::RefPtr<Gtk::RecentInfo> a, Glib::RefPtr<Gtk::RecentInfo> b) -> bool { + return a->get_modified() > b->get_modified(); + } + ); + + unsigned inserted_entries = 0; + for (auto const &recent_file : recent_files) { + // check if given was generated by inkscape + bool valid_file = recent_file->has_application(g_get_prgname()) || + recent_file->has_application("org.inkscape.Inkscape") || + recent_file->has_application("inkscape") +#ifdef _WIN32 + || recent_file->has_application("inkscape.exe") +#endif + ; + + // this is potentially expensive: local FS access (remote files are not checked) + valid_file = valid_file && recent_file->exists(); + + if (!valid_file) { + continue; + } + + // Escape underscores to prevent them from being interpreted as accelerator mnemonics + std::regex underscore{"_"}; + std::string const dunder{"__"}; + std::string raw_name = recent_file->get_short_name(); + Glib::ustring escaped = std::regex_replace(raw_name, underscore, dunder); + + auto item { Gio::MenuItem::create(std::move(escaped), Glib::ustring()) }; + auto target { Glib::Variant<Glib::ustring>::create(recent_file->get_uri_display()) }; + // note: setting action and target separately rather than using convenience menu method append + // since some filename characters can result in invalid "direct action" string + item->set_action_and_target(Glib::ustring("app.file-open-window"), target); + submenu->append_item(item); + inserted_entries++; + + if (--max_files == 0) { + break; + } + } + + if (!inserted_entries) { // Create a placeholder with a non-existent action + auto nothing2c = Gio::MenuItem::create(_("No items found"), "app.nop"); + submenu->append_item(nothing2c); + } + }; + + rebuild(sub_gmenu); + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + auto useicons = static_cast<UseIcons>(prefs->getInt("/theme/menuIcons", 0)); + + // Remove all or some icons. Also create label to tooltip map. + auto gmenu_copy = Gio::Menu::create(); + // menu gets recreated; keep track of new recent items submenu + rebuild_menu(gmenu, gmenu_copy, useicons, recent_menu_quark, sub_gmenu); + app->gtk_app()->set_menubar(gmenu_copy); + + // rebuild recent items submenu when the list changes + recent_manager->signal_changed().connect([=](){ rebuild(sub_gmenu); }); + } +} + + +/* + * Disable all or some menu icons. + * + * This is quite nasty: + * + * We must disable icons in the Gio::Menu as there is no way to pass + * the needed information to the children of Gtk::PopoverMenu and no + * way to set visibility via CSS. + * + * MenuItems are immutable and not copyable so you have to recreate + * the menu tree. The format for accessing MenuItem data is not the + * same as what you need to create a new MenuItem. + * + * NOTE: Input is a Gio::MenuModel, Output is a Gio::Menu!! + */ +#if GTK_CHECK_VERSION(4, 0, 0) +void rebuild_menu (std::shared_ptr<Gio::MenuModel> menu, std::shared_ptr<Gio::Menu> menu_copy, UseIcons useIcons, Glib::Quark quark, Glib::RefPtr<Gio::Menu>& recent_files) { +#else +void rebuild_menu (Glib::RefPtr<Gio::MenuModel> menu, Glib::RefPtr<Gio::Menu> menu_copy, UseIcons useIcons, Glib::Quark quark, Glib::RefPtr<Gio::Menu>& recent_files) { +#endif + + static auto app = InkscapeApplication::instance(); + auto& extra_data = app->get_action_extra_data(); + auto& label_to_tooltip_map = app->get_menu_label_to_tooltip_map(); + + for (int i = 0; i < menu->get_n_items(); ++i) { + + Glib::ustring label; + Glib::ustring action; + Glib::ustring target; + Glib::VariantBase icon; + Glib::ustring use_icon; + std::map<Glib::ustring, Glib::VariantBase> attributes; + + auto attribute_iter = menu->iterate_item_attributes(i); + while (attribute_iter->next()) { + + // Attributes we need to create MenuItem or set icon. + if (attribute_iter->get_name() == "label") { + // Convert label while preserving unicode translations + label = Glib::VariantBase::cast_dynamic<Glib::Variant<std::string> >(attribute_iter->get_value()).get(); + } else if (attribute_iter->get_name() == "action") { + action = attribute_iter->get_value().print(); + action.erase(0, 1); + action.erase(action.size()-1, 1); + } else if (attribute_iter->get_name() == "target") { + target = attribute_iter->get_value().print(); + } else if (attribute_iter->get_name() == "icon") { + icon = attribute_iter->get_value(); + } else if (attribute_iter->get_name() == "use-icon") { + use_icon = attribute_iter->get_value().print(); + } else { + // All the remaining attributes. + attributes[attribute_iter->get_name()] = attribute_iter->get_value(); + } + } + Glib::ustring detailed_action = action; + if (target.size() > 0) { + detailed_action += "(" + target + ")"; + } + + auto tooltip = extra_data.get_tooltip_for_action(detailed_action); + label_to_tooltip_map[label] = tooltip; + + // std::cout << " " << std::setw(30) << detailed_action + // << " label: " << std::setw(30) << label.c_str() + // << " use_icon (.ui): " << std::setw(6) << use_icon + // << " icon: " << (icon ? "yes" : "no ") + // << " useIcons: " << (int)useIcons + // << " use_icon.size(): " << use_icon.size() + // << " tooltip: " << tooltip.c_str() + // << std::endl; + + auto menu_item = Gio::MenuItem::create(label, detailed_action); + if (icon && + (useIcons == UseIcons::always || + (useIcons == UseIcons::as_requested && use_icon.size() > 0))) { + menu_item->set_attribute_value("icon", icon); + } + + // Add remaining attributes + for (auto const& [key, value] : attributes) { + menu_item->set_attribute_value(key, value); + } + + // Add submenus + auto link_iter = menu->iterate_item_links(i); + while (link_iter->next()) { + auto submenu = Gio::Menu::create(); + if (link_iter->get_name() == "submenu") { + menu_item->set_submenu(submenu); + if (link_iter->get_value()->get_data(quark)) { + recent_files = submenu; + } + } else if (link_iter->get_name() == "section") { + menu_item->set_section(submenu); + } else { + std::cerr << "rebuild_menu: Unknown link type: " << link_iter->get_name() << std::endl; + } + rebuild_menu (link_iter->get_value(), submenu, useIcons, quark, recent_files); + } + + menu_copy->append_item(menu_item); + } +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/desktop/menubar.h b/src/ui/desktop/menubar.h new file mode 100644 index 0000000..704f26f --- /dev/null +++ b/src/ui/desktop/menubar.h @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#ifndef SEEN_DESKTOP_MENUBAR_H +#define SEEN_DESKTOP_MENUBAR_H + +/** + * @file + * Desktop main menu bar code. + */ +/* + * Authors: + * Tavmjong Bah + * Sushant A.A. + * + * Copyright (C) 2018 Authors + * + * The contents of this file may be used under the GNU General Public License Version 2 or later. + * Read the file 'COPYING' for more information. + * + */ + +#include <gtkmm.h> // GTK_CHECK_VERSION + +void build_menu(); + +enum class UseIcons { + never = -1, // Match existing preference numbering. + as_requested, + always, +}; + +// Rebuild menu with icons enabled or disabled. Recursive. +#if GTK_CHECK_VERSION(4, 0, 0) +void rebuild_menu (std::shared_ptr<Gio::MenuModel> menu, std::shared_ptr<Gio::Menu> menu_copy, UseIcons useIcons, Glib::Quark quark, Glib::RefPtr<Gio::Menu>& recent_files); +#else +void rebuild_menu (Glib::RefPtr<Gio::MenuModel> menu, Glib::RefPtr<Gio::Menu> menu_copy, UseIcons useIcons, Glib::Quark quark, Glib::RefPtr<Gio::Menu>& recent_files); +#endif + +#endif // SEEN_DESKTOP_MENUBAR_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : |