/* * Main menu and button * * Copyright (C) 2017 Patrick McDermott * * This file is part of Marquee. * * Marquee 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. * * Marquee 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 Marquee. If not, see . */ #include "main-menu.h" #include #include #include #include "../../application.h" #include "../../config/profiles.h" #include "../../i18n.h" #include "../../notebook.h" #include "../../tab-page.h" #include "../../utils/profile-icon.h" #include "../../web-view.h" #include "../find-toolbar.h" struct _MqMainMenu { GtkToolButton parent_instance; MqTabPage *tab_page; MqFindToolbar *find_toolbar; MqWebView *web_view; GtkWidget *popover; }; enum { PROP_TAB_PAGE = 1, PROP_FIND_TOOLBAR, PROP_WEB_VIEW, N_PROPERTIES }; static GParamSpec *obj_properties[N_PROPERTIES] = {NULL,}; struct _MqMainMenuClass { GtkToolButtonClass parent_class; }; G_DEFINE_TYPE(MqMainMenu, mq_main_menu, GTK_TYPE_TOOL_BUTTON) static void can_edit_cb(WebKitWebView *web_view, GAsyncResult *result, GtkWidget *button) { gtk_widget_set_sensitive(button, webkit_web_view_can_execute_editing_command_finish(web_view, result, NULL)); } static void cut_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu) { webkit_web_view_execute_editing_command( WEBKIT_WEB_VIEW(main_menu->web_view), WEBKIT_EDITING_COMMAND_CUT); } static void copy_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu) { webkit_web_view_execute_editing_command( WEBKIT_WEB_VIEW(main_menu->web_view), WEBKIT_EDITING_COMMAND_COPY); } static void paste_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu) { webkit_web_view_execute_editing_command( WEBKIT_WEB_VIEW(main_menu->web_view), WEBKIT_EDITING_COMMAND_PASTE); } static void open_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu) { gtk_widget_hide(main_menu->popover); mq_web_view_open(main_menu->web_view); } static void save_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu) { gtk_widget_hide(main_menu->popover); mq_web_view_save(main_menu->web_view); } static void email_link_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu G_GNUC_UNUSED *main_menu) { /* TODO */ } static void print_preview_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu G_GNUC_UNUSED *main_menu) { /* TODO */ } static void print_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu G_GNUC_UNUSED *main_menu) { /* TODO */ } static void zoom_out_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu) { mq_web_view_zoom_out(main_menu->web_view); } static void zoom_reset_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu) { mq_web_view_zoom_reset(main_menu->web_view); } static void zoom_in_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu) { mq_web_view_zoom_in(main_menu->web_view); } static void new_window_clicked_cb(GtkWidget G_GNUC_UNUSED *button, MqMainMenu *main_menu) { mq_application_add_window( mq_tab_page_get_application(main_menu->tab_page), NULL); gtk_widget_hide(main_menu->popover); } static void find_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu) { gtk_widget_hide(main_menu->popover); mq_find_toolbar_reveal(main_menu->find_toolbar); } static void fullscreen_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu) { gtk_widget_hide(main_menu->popover); mq_window_toggle_fullscreen(mq_tab_page_get_window(main_menu->tab_page)); } static void inspector_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu) { gtk_widget_hide(main_menu->popover); webkit_web_inspector_show(webkit_web_view_get_inspector( WEBKIT_WEB_VIEW(main_menu->web_view))); } static void profile_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu G_GNUC_UNUSED *main_menu) { gtk_widget_hide(main_menu->popover); mq_notebook_insert_sibling( MQ_NOTEBOOK(gtk_widget_get_parent( GTK_WIDGET(main_menu->tab_page))), "about:profiles", main_menu->tab_page, TRUE); } static void preferences_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu) { gtk_widget_hide(main_menu->popover); mq_notebook_insert_sibling( MQ_NOTEBOOK(gtk_widget_get_parent( GTK_WIDGET(main_menu->tab_page))), "about:preferences", main_menu->tab_page, TRUE); } static void about_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu) { gtk_widget_hide(main_menu->popover); mq_notebook_insert_sibling( MQ_NOTEBOOK(gtk_widget_get_parent( GTK_WIDGET(main_menu->tab_page))), "about:", main_menu->tab_page, TRUE); } static void quit_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu) { /* mq_tab_page_quit() just calls mq_window_quit(), which just calls * mq_application_quit(), which is asynchronous. So close the menu. */ gtk_widget_hide(main_menu->popover); mq_tab_page_quit(main_menu->tab_page); } #define NEW_BTN(ID, ICON, TOOLTIP) \ G_STMT_START { \ ID##_button = gtk_button_new_from_icon_name(ICON, \ GTK_ICON_SIZE_BUTTON); \ gtk_widget_set_tooltip_text(ID##_button, TOOLTIP); \ gtk_box_pack_start(GTK_BOX(box), ID##_button, \ TRUE, TRUE, 0); \ g_signal_connect(ID##_button, "clicked", \ G_CALLBACK(ID##_clicked_cb), main_menu); \ } G_STMT_END static GtkWidget * create_edit_buttons(MqMainMenu *main_menu) { GtkWidget *box; GtkWidget *cut_button; GtkWidget *copy_button; GtkWidget *paste_button; box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_set_homogeneous(GTK_BOX(box), TRUE); gtk_style_context_add_class(gtk_widget_get_style_context(box), "linked"); /* ID, ICON, TOOLTIP */ NEW_BTN(cut, "edit-cut", _("Cut")); gtk_widget_set_sensitive(cut_button, FALSE); webkit_web_view_can_execute_editing_command( WEBKIT_WEB_VIEW(main_menu->web_view), WEBKIT_EDITING_COMMAND_CUT, NULL, (GAsyncReadyCallback) can_edit_cb, cut_button); /* ID, ICON, TOOLTIP */ NEW_BTN(copy, "edit-copy", _("Copy")); gtk_widget_set_sensitive(copy_button, FALSE); webkit_web_view_can_execute_editing_command( WEBKIT_WEB_VIEW(main_menu->web_view), WEBKIT_EDITING_COMMAND_COPY, NULL, (GAsyncReadyCallback) can_edit_cb, copy_button); /* ID, ICON, TOOLTIP */ NEW_BTN(paste, "edit-paste", _("Paste")); gtk_widget_set_sensitive(paste_button, FALSE); webkit_web_view_can_execute_editing_command( WEBKIT_WEB_VIEW(main_menu->web_view), WEBKIT_EDITING_COMMAND_PASTE, NULL, (GAsyncReadyCallback) can_edit_cb, paste_button); return box; } static GtkWidget * create_zoom_buttons(MqMainMenu *main_menu) { GtkWidget *box; GtkWidget *zoom_out_button; GtkWidget *zoom_reset_button; GtkWidget *zoom_in_button; box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_set_homogeneous(GTK_BOX(box), TRUE); gtk_style_context_add_class(gtk_widget_get_style_context(box), "linked"); /* ID, ICON, TOOLTIP */ NEW_BTN(zoom_out, "zoom-out", _("Zoom out")); NEW_BTN(zoom_reset, "zoom-original", _("Reset zoom")); NEW_BTN(zoom_in, "zoom-in", _("Zoom in")); return box; } #undef NEW_BTN #define BUTTON_ROWS 3 #define BUTTON_COLS 3 #define BTN(Y, X) buttons[Y * BUTTON_COLS + X] #define NEW_BTN(Y, X, ID, ICON, LABEL, TOOLTIP) \ G_STMT_START { \ BTN(Y, X) = gtk_button_new_from_icon_name(ICON, \ GTK_ICON_SIZE_BUTTON); \ gtk_button_set_relief(GTK_BUTTON(BTN(Y, X)), GTK_RELIEF_NONE); \ gtk_button_set_label(GTK_BUTTON(BTN(Y, X)), LABEL); \ gtk_button_set_image_position(GTK_BUTTON(BTN(Y, X)), \ GTK_POS_TOP); \ gtk_button_set_always_show_image(GTK_BUTTON(BTN(Y, X)), TRUE); \ gtk_widget_set_tooltip_text(BTN(Y, X), TOOLTIP); \ g_signal_connect(BTN(Y, X), "clicked", \ G_CALLBACK(ID ## _clicked_cb), main_menu); \ gtk_grid_attach(GTK_GRID(grid), BTN(Y, X), X, Y, 1, 1); \ } G_STMT_END static GtkWidget * create_main_grid(MqMainMenu *main_menu) { GtkWidget *grid; GtkWidget *buttons[BUTTON_ROWS * BUTTON_COLS]; /* Set up the grid. */ grid = gtk_grid_new(); gtk_grid_set_row_homogeneous(GTK_GRID(grid), TRUE); gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE); /* Y, X, ID, ICON, LABEL, * TOOLTIP */ NEW_BTN(0, 0, new_window, "window-new", _("New Window"), _("Open a new window")); NEW_BTN(0, 1, find, "edit-find", _("Find"), _("Find text in this page")); NEW_BTN(0, 2, fullscreen, "view-fullscreen", _("Full Screen"), _("Display the window in full screen")); /* Y, X, ID, ICON, LABEL, * TOOLTIP */ NEW_BTN(1, 0, open, "document-open", _("Open File"), _("Open a local file")); NEW_BTN(1, 1, save, "document-save-as", _("Save Page"), _("Save this page")); NEW_BTN(1, 2, email_link, "mail-message-new", _("E-mail Link"), _("E-mail a link to this page")); /* Y, X, ID, ICON, LABEL, * TOOLTIP */ NEW_BTN(2, 0, print_preview,"document-print-preview",_("Print Preview"), _("Preview this page")); NEW_BTN(2, 1, print, "document-print", _("Print"), _("Print this page")); NEW_BTN(2, 2, inspector, "preferences-system", _("Inspector"), _("Open the Web inspector")); gtk_widget_set_sensitive(BTN(1, 2), FALSE); /* E-mail link */ gtk_widget_set_sensitive(BTN(2, 0), FALSE); /* Print preview */ gtk_widget_set_sensitive(BTN(2, 1), FALSE); /* Print */ return grid; } #undef BUTTON_ROWS #undef BUTTON_COLS #undef BTN #undef NEW_BTN #define NEW_BTN(ID, ICON, LABEL, TOOLTIP) \ G_STMT_START { \ ID##_button = gtk_button_new_from_icon_name(ICON, \ GTK_ICON_SIZE_BUTTON); \ gtk_button_set_label(GTK_BUTTON(ID##_button), LABEL); \ gtk_button_set_image_position(GTK_BUTTON(ID##_button), \ GTK_POS_LEFT); \ gtk_button_set_always_show_image(GTK_BUTTON(ID##_button), \ TRUE); \ gtk_widget_set_tooltip_text(ID##_button, TOOLTIP); \ g_signal_connect(ID##_button, "clicked", \ G_CALLBACK(ID##_clicked_cb), main_menu); \ } G_STMT_END static GtkWidget * create_profile_buttons(MqMainMenu *main_menu) { MqProfiles *profiles; gchar *cur_prof; gchar *cur_prof_name; gchar *cur_prof_color; GtkWidget *profile_button; GtkWidget *preferences_button; GtkWidget *grid; profiles = mq_application_get_profiles( mq_tab_page_get_application(main_menu->tab_page)); cur_prof = mq_profiles_get_current(profiles); cur_prof_name = mq_profiles_get_name(profiles, cur_prof); cur_prof_color = mq_profiles_get_color(profiles, cur_prof); /* ID, ICON, LABEL, TOOLTIP */ NEW_BTN(profile, NULL, cur_prof_name, _("Manage profiles")); gtk_button_set_image(GTK_BUTTON(profile_button), gtk_image_new_from_pixbuf( mq_profile_icon_new_pixbuf(cur_prof_color))); g_free(cur_prof); g_free(cur_prof_name); g_free(cur_prof_color); /* ID, ICON, LABEL, TOOLTIP */ NEW_BTN(preferences, "preferences-desktop", NULL, _("Preferences")); /* Set up the grid. */ grid = gtk_grid_new(); gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE); gtk_style_context_add_class(gtk_widget_get_style_context(grid), "linked"); gtk_grid_attach(GTK_GRID(grid), profile_button, 0, 0, 2, 1); gtk_grid_attach(GTK_GRID(grid), preferences_button, 2, 0, 1, 1); return grid; } static GtkWidget * create_application_buttons(MqMainMenu *main_menu) { GtkWidget *about_button; GtkWidget *quit_button; GtkWidget *grid; /* ID, ICON, LABEL, TOOLTIP */ NEW_BTN(about,"help-about", _("About Marquee"),_("About Marquee")); NEW_BTN(quit, "application-exit",NULL, _("Quit")); /* Set up the grid. */ grid = gtk_grid_new(); gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE); gtk_style_context_add_class(gtk_widget_get_style_context(grid), "linked"); gtk_grid_attach(GTK_GRID(grid), about_button, 0, 0, 2, 1); gtk_grid_attach(GTK_GRID(grid), quit_button, 2, 0, 1, 1); return grid; } #undef NEW_BTN static void menu_clicked_cb(MqMainMenu *main_menu) { GtkWidget *box; box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); gtk_box_pack_start(GTK_BOX(box), create_edit_buttons(main_menu), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(box), create_zoom_buttons(main_menu), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(box), create_main_grid(main_menu), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(box), create_profile_buttons(main_menu), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(box), create_application_buttons(main_menu), TRUE, TRUE, 0); /* Set up the popover. */ main_menu->popover = gtk_popover_new(GTK_WIDGET(main_menu)); gtk_container_add(GTK_CONTAINER(main_menu->popover), box); /* NB: gtk_popover_popup() is new in GTK+ 3.22. */ gtk_widget_show_all(main_menu->popover); } static void get_property(GObject *object, guint property_id, GValue *value, GParamSpec *param_spec) { MqMainMenu *main_menu; main_menu = MQ_MAIN_MENU(object); switch (property_id) { case PROP_TAB_PAGE: g_value_set_object(value, main_menu->tab_page); break; case PROP_FIND_TOOLBAR: g_value_set_object(value, main_menu->find_toolbar); break; case PROP_WEB_VIEW: g_value_set_object(value, main_menu->web_view); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, param_spec); break; } } static void set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *param_spec) { MqMainMenu *main_menu; main_menu = MQ_MAIN_MENU(object); switch (property_id) { case PROP_TAB_PAGE: main_menu->tab_page = g_value_get_object(value); break; case PROP_FIND_TOOLBAR: main_menu->find_toolbar = g_value_get_object(value); break; case PROP_WEB_VIEW: main_menu->web_view = g_value_get_object(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, param_spec); break; } } static void mq_main_menu_class_init(MqMainMenuClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); object_class->get_property = get_property; object_class->set_property = set_property; obj_properties[PROP_TAB_PAGE] = g_param_spec_object( "tab-page", P_("MqTabPage"), P_("The ancestral MqTabPage instance"), MQ_TYPE_TAB_PAGE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); obj_properties[PROP_FIND_TOOLBAR] = g_param_spec_object( "find-toolbar", P_("MqFindToolbar"), P_("The associated MqFindToolbar instance"), MQ_TYPE_FIND_TOOLBAR, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); obj_properties[PROP_WEB_VIEW] = g_param_spec_object( "web-view", P_("MqWebView"), P_("The associated MqWebView instance"), MQ_TYPE_WEB_VIEW, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_properties(object_class, N_PROPERTIES, obj_properties); } static void mq_main_menu_init(MqMainMenu *main_menu) { gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(main_menu), gtk_image_new_from_icon_name("open-menu-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR)); gtk_tool_button_set_label(GTK_TOOL_BUTTON(main_menu), _("Menu")); gtk_widget_set_can_focus(gtk_bin_get_child(GTK_BIN(main_menu)), FALSE); gtk_widget_set_tooltip_text(GTK_WIDGET(main_menu), _("Open menu")); g_signal_connect(main_menu, "clicked", G_CALLBACK(menu_clicked_cb), NULL); } GtkToolItem * mq_main_menu_new(MqTabPage *tab_page, MqFindToolbar *find_toolbar, MqWebView *web_view) { return g_object_new(MQ_TYPE_MAIN_MENU, "tab-page", tab_page, "find-toolbar", find_toolbar, "web-view", web_view, NULL); }