src: switch main window to use GtkHeaderBar

This removes the main menu bar in favour of using the GtkHeaderBar with
integrated bars. This reduces the amount of vertical screen real estate
consumed by the application, leaving more for the guest display. The new
buttons are laid out to make the more common actions available with a
single click.

The buttons are grouped into two sets. On the left hand side of the
header are buttons that are interacting with the server

 - Send key
 - USB device selection
 - Monitor selection and VM pause/shutdown

whle on the right hand side are buttons interacting with the local user
interface

 - Fullscreen
 - Preferences / About / Guest detail / zoom level

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrangé 2021-02-11 14:14:54 +00:00
parent 2f11e0a407
commit bb9664de38
8 changed files with 315 additions and 521 deletions

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<menu id="action-menu">
<section>
<item>
<attribute name="label" translatable="yes">_Screenshot</attribute>
<attribute name="action">win.screenshot</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_Preferences…</attribute>
<attribute name="action">win.preferences</attribute>
</item>
</section>
<section>
<item>
<attribute name="label" translatable="yes">Zoom _In</attribute>
<attribute name="action">win.zoom-in</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Zoom _Out</attribute>
<attribute name="action">win.zoom-out</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_Normal Size</attribute>
<attribute name="action">win.zoom-reset</attribute>
</item>
</section>
<section>
<item>
<attribute name="label" translatable="yes">_About</attribute>
<attribute name="action">win.about</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_Guest details</attribute>
<attribute name="action">win.guest-details</attribute>
</item>
</section>
</menu>
<menu id="machine-menu">
<section>
</section>
<section>
<item>
<attribute name="label" translatable="yes">_Pause</attribute>
<attribute name="action">app.machine-pause</attribute>
</item>
</section>
<section>
<item>
<attribute name="label" translatable="yes">_Reset</attribute>
<attribute name="action">app.machine-reset</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Power _down</attribute>
<attribute name="action">app.machine-powerdown</attribute>
</item>
</section>
</menu>
</interface>

View File

@ -18,312 +18,7 @@
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkMenuBar" id="top-menu">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="menu-file">
<property name="use-action-appearance">False</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_File</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu" id="menu1">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="accel-group">accelgroup</property>
<child>
<object class="GtkMenuItem" id="menu-file-screenshot">
<property name="use-action-appearance">False</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can-focus">False</property>
<property name="action-name">win.screenshot</property>
<property name="label" translatable="yes">_Screenshot</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-file-usb-device-selection">
<property name="use-action-appearance">False</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can-focus">False</property>
<property name="action-name">win.usb-device-select</property>
<property name="label" translatable="yes">_USB device selection</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-file-usb-device-reset">
<property name="use-action-appearance">False</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="action-name">win.usb-device-reset</property>
<property name="accel-path">&lt;virt-viewer&gt;/file/usb-device-reset</property>
<property name="label" translatable="yes">USB device _reset</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-file-smartcard-insert">
<property name="use-action-appearance">False</property>
<property name="can-focus">False</property>
<property name="action-name">app.smartcard-insert</property>
<property name="accel-path">&lt;virt-viewer&gt;/file/smartcard-insert</property>
<property name="label" translatable="yes">Smartcard insertion</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-file-smartcard-remove">
<property name="use-action-appearance">False</property>
<property name="can-focus">False</property>
<property name="action-name">app.smartcard-remove</property>
<property name="accel-path">&lt;virt-viewer&gt;/file/smartcard-remove</property>
<property name="label" translatable="yes">Smartcard removal</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-change-cd">
<property name="can-focus">False</property>
<property name="action-name">win.change-cd</property>
<property name="label" translatable="yes">_Change CD</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-preferences">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can-focus">False</property>
<property name="action-name">win.preferences</property>
<property name="label" translatable="yes">_Preferences</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem" id="separatormenuitem1">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="imagemenuitem5">
<property name="use-action-appearance">False</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="action-name">win.quit</property>
<property name="label" translatable="yes">_Quit</property>
<property name="use-underline">True</property>
<accelerator key="q" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-machine">
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Machine</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkCheckMenuItem" id="menu-vm-pause">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="action-name">app.machine-pause</property>
<property name="label" translatable="yes">_Pause</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-vm-reset">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="action-name">app.machine-reset</property>
<property name="label" translatable="yes">_Reset</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-vm-powerdown">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="action-name">app.machine-powerdown</property>
<property name="label" translatable="yes">_Power down</property>
<property name="use-underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-view">
<property name="use-action-appearance">False</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_View</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu" id="menu2">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="accel-group">accelgroup</property>
<child>
<object class="GtkCheckMenuItem" id="menu-view-fullscreen">
<property name="use-action-appearance">False</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="action-name">win.fullscreen</property>
<property name="accel-path">&lt;virt-viewer&gt;/view/toggle-fullscreen</property>
<property name="label" translatable="yes">_Full screen</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-view-zoom">
<property name="use-action-appearance">False</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Zoom</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu" id="menu4">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="accel-group">accelgroup</property>
<child>
<object class="GtkMenuItem" id="menu-view-zoom-in">
<property name="use-action-appearance">False</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="action-name">win.zoom-in</property>
<property name="accel-path">&lt;virt-viewer&gt;/view/zoom-in</property>
<property name="label" translatable="yes">Zoom _In</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-view-zoom-out">
<property name="use-action-appearance">False</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="action-name">win.zoom-out</property>
<property name="accel-path">&lt;virt-viewer&gt;/view/zoom-out</property>
<property name="label" translatable="yes">Zoom _Out</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem" id="separatormenuitem4">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-view-zoom-reset">
<property name="use-action-appearance">False</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="action-name">win.zoom-reset</property>
<property name="accel-path">&lt;virt-viewer&gt;/view/zoom-reset</property>
<property name="label" translatable="yes">_Normal Size</property>
<property name="use-underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-displays">
<property name="use-action-appearance">False</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Displays</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-view-release-cursor">
<property name="use-action-appearance">False</property>
<property name="can-focus">False</property>
<property name="action-name">win.release-cursor</property>
<property name="accel-path">&lt;virt-viewer&gt;/view/release-cursor</property>
<property name="label" translatable="yes">Release cursor</property>
<property name="use-underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-send">
<property name="use-action-appearance">False</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Send key</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-help">
<property name="use-action-appearance">False</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Help</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu" id="menu3">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="menu-help-guest-details">
<property name="use-action-appearance">False</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="action-name">win.guest-details</property>
<property name="label" translatable="yes">_Guest Details</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="imagemenuitem10">
<property name="use-action-appearance">False</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="action-name">win.about</property>
<property name="label" translatable="yes">_About</property>
<property name="use-underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
<placeholder/>
</child>
</object>
<packing>
@ -332,5 +27,100 @@
</child>
</object>
</child>
<child type="titlebar">
<object class="GtkHeaderBar" id="header">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="show-close-button">True</property>
<child>
<object class="GtkMenuButton" id="header-action">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">open-menu-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="pack-type">end</property>
</packing>
</child>
<child>
<object class="GtkMenuButton" id="header-send-key">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">preferences-desktop-keyboard-shortcuts-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="header-usb">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="action-name">win.usb-device-select</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">audio-card-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkMenuButton" id="header-machine">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">computer-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkButton" id="header-fullscreen">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="action-name">win.fullscreen</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">view-fullscreen-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="pack-type">end</property>
<property name="position">4</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View File

@ -10,6 +10,7 @@
<file>ui/virt-viewer-vm-connection.ui</file>
<file>ui/virt-viewer.ui</file>
<file>ui/virt-viewer-file-transfer-dialog.ui</file>
<file>ui/virt-viewer-menus.ui</file>
<file alias="icons/16x16/virt-viewer.png">../../icons/16x16/virt-viewer.png</file>
<file alias="icons/22x22/virt-viewer.png">../../icons/22x22/virt-viewer.png</file>
<file alias="icons/24x24/virt-viewer.png">../../icons/24x24/virt-viewer.png</file>

View File

@ -105,6 +105,7 @@ static void virt_viewer_update_usbredir_accels(VirtViewerApp *self);
static void virt_viewer_app_add_option_entries(VirtViewerApp *self, GOptionContext *context, GOptionGroup *group);
static VirtViewerWindow *virt_viewer_app_get_nth_window(VirtViewerApp *self, gint nth);
static VirtViewerWindow *virt_viewer_app_get_vte_window(VirtViewerApp *self, const gchar *name);
static void virt_viewer_app_set_actions_sensitive(VirtViewerApp *self);
/* Application actions */
static void virt_viewer_app_action_monitor(GSimpleAction *act,
@ -1033,17 +1034,38 @@ virt_viewer_app_set_usb_reset_sensitive(VirtViewerApp *self, gboolean sensitive)
}
static void
set_menus_sensitive(gpointer value, gpointer user_data)
set_actions_sensitive(gpointer value, gpointer user_data)
{
virt_viewer_window_set_menus_sensitive(VIRT_VIEWER_WINDOW(value),
GPOINTER_TO_INT(user_data));
VirtViewerApp *self = VIRT_VIEWER_APP(user_data);
VirtViewerAppPrivate *priv = virt_viewer_app_get_instance_private(self);
virt_viewer_window_set_actions_sensitive(VIRT_VIEWER_WINDOW(value),
priv->connected);
}
void
virt_viewer_app_set_menus_sensitive(VirtViewerApp *self, gboolean sensitive)
static void
virt_viewer_app_set_actions_sensitive(VirtViewerApp *self)
{
VirtViewerAppPrivate *priv = virt_viewer_app_get_instance_private(self);
g_list_foreach(priv->windows, set_menus_sensitive, GINT_TO_POINTER(sensitive));
GActionMap *map = G_ACTION_MAP(self);
GAction *action;
g_list_foreach(priv->windows, set_actions_sensitive, self);
action = g_action_map_lookup_action(map, "machine-pause");
g_simple_action_set_enabled(G_SIMPLE_ACTION(action),
priv->connected &&
priv->vm_ui);
action = g_action_map_lookup_action(map, "machine-reset");
g_simple_action_set_enabled(G_SIMPLE_ACTION(action),
priv->connected &&
priv->vm_ui);
action = g_action_map_lookup_action(map, "machine-powerdown");
g_simple_action_set_enabled(G_SIMPLE_ACTION(action),
priv->connected &&
priv->vm_ui);
}
static VirtViewerWindow *
@ -1709,6 +1731,7 @@ virt_viewer_app_deactivate(VirtViewerApp *self, gboolean connect_error)
#endif
priv->grabbed = FALSE;
virt_viewer_app_update_title(self);
virt_viewer_app_set_actions_sensitive(self);
if (priv->authretry) {
priv->authretry = FALSE;
@ -1732,6 +1755,8 @@ virt_viewer_app_connected(VirtViewerSession *session G_GNUC_UNUSED,
virt_viewer_app_show_status(self, "%s", "");
else
virt_viewer_app_show_status(self, _("Connected to graphic server"));
virt_viewer_app_set_actions_sensitive(self);
}
static void
@ -1924,6 +1949,7 @@ virt_viewer_app_set_property (GObject *object, guint property_id,
g_return_if_fail(VIRT_VIEWER_IS_APP(object));
VirtViewerApp *self = VIRT_VIEWER_APP(object);
VirtViewerAppPrivate *priv = virt_viewer_app_get_instance_private(self);
GAction *action;
switch (property_id) {
case PROP_VERBOSE:
@ -1968,10 +1994,16 @@ virt_viewer_app_set_property (GObject *object, guint property_id,
case PROP_VM_UI:
priv->vm_ui = g_value_get_boolean(value);
virt_viewer_app_set_actions_sensitive(self);
break;
case PROP_VM_RUNNING:
priv->vm_running = g_value_get_boolean(value);
action = g_action_map_lookup_action(G_ACTION_MAP(self), "machine-pause");
g_simple_action_set_state(G_SIMPLE_ACTION(action),
g_variant_new_boolean(priv->vm_running));
break;
case PROP_CONFIG_SHARE_CLIPBOARD:
@ -2421,6 +2453,7 @@ virt_viewer_app_on_application_startup(GApplication *app)
opt_zoom = NORMAL_ZOOM_LEVEL;
}
virt_viewer_app_set_actions_sensitive(self);
virt_viewer_window_set_zoom_level(priv->main_window, opt_zoom);
// Restore initial state of config-share-clipboard property from config and notify about it
@ -2881,35 +2914,6 @@ update_menu_displays_sort(gconstpointer a, gconstpointer b)
return 0;
}
static GtkMenuShell *
window_empty_display_submenu(VirtViewerWindow *window)
{
/* Because of what apparently is a gtk+2 bug (rhbz#922712), we
* cannot recreate the submenu every time we need to refresh it,
* otherwise the application may get frozen with the keyboard and
* mouse grabbed if gtk_menu_item_set_submenu is called while
* the menu is displayed. Reusing the same menu every time
* works around this issue.
*/
GtkMenuItem *menu = virt_viewer_window_get_menu_displays(window);
GtkMenuShell *submenu;
submenu = GTK_MENU_SHELL(gtk_menu_item_get_submenu(menu));
if (submenu) {
GList *subitems;
GList *it;
subitems = gtk_container_get_children(GTK_CONTAINER(submenu));
for (it = subitems; it != NULL; it = it->next) {
gtk_container_remove(GTK_CONTAINER(submenu), GTK_WIDGET(it->data));
}
g_list_free(subitems);
} else {
submenu = GTK_MENU_SHELL(gtk_menu_new());
gtk_menu_item_set_submenu(menu, GTK_WIDGET(submenu));
}
return submenu;
}
static void
window_update_menu_displays_cb(gpointer value,
@ -2917,32 +2921,29 @@ window_update_menu_displays_cb(gpointer value,
{
VirtViewerApp *self = VIRT_VIEWER_APP(user_data);
VirtViewerAppPrivate *priv = virt_viewer_app_get_instance_private(self);
GtkMenuShell *submenu;
VirtViewerWindow *window = VIRT_VIEWER_WINDOW(value);
GMenuModel *menu;
GList *keys = g_hash_table_get_keys(priv->displays);
GList *tmp;
gboolean sensitive;
int nth;
keys = g_list_sort(keys, update_menu_displays_sort);
submenu = window_empty_display_submenu(VIRT_VIEWER_WINDOW(value));
sensitive = (keys != NULL);
virt_viewer_window_set_menu_displays_sensitive(VIRT_VIEWER_WINDOW(value), sensitive);
menu = virt_viewer_window_get_menu_displays(window);
g_menu_remove_all(G_MENU(menu));
tmp = keys;
while (tmp) {
GtkWidget *item;
GMenuItem *item;
gchar *label;
gchar *actionname;
nth = GPOINTER_TO_INT(tmp->data);
actionname = g_strdup_printf("app.monitor-%d", nth);
label = g_strdup_printf(_("Display _%d"), nth + 1);
item = gtk_check_menu_item_new_with_mnemonic(label);
item = g_menu_item_new(label, actionname);
gtk_actionable_set_action_name(GTK_ACTIONABLE(item), actionname);
gtk_menu_shell_append(submenu, item);
g_menu_append_item(G_MENU(menu), item);
g_free(label);
g_free(actionname);
@ -2956,24 +2957,21 @@ window_update_menu_displays_cb(gpointer value,
if (VIRT_VIEWER_IS_DISPLAY_VTE(display)) {
gchar *name = NULL;
GtkWidget *item;
GMenuItem *item;
gchar *actionname;
g_object_get(display, "name", &name, NULL);
actionname = g_strdup_printf("app.vte-%d", nth);
item = gtk_check_menu_item_new_with_label(name);
item = g_menu_item_new(name, actionname);
gtk_actionable_set_action_name(GTK_ACTIONABLE(item), actionname);
gtk_menu_shell_append(submenu, item);
g_menu_append_item(G_MENU(menu), item);
g_free(actionname);
g_free(name);
}
}
gtk_widget_show_all(GTK_WIDGET(submenu));
g_list_free(keys);
}

View File

@ -95,7 +95,6 @@ GList* virt_viewer_app_get_initial_displays(VirtViewerApp* self);
gint virt_viewer_app_get_initial_monitor_for_display(VirtViewerApp* self, gint display);
void virt_viewer_app_set_enable_accel(VirtViewerApp *app, gboolean enable);
void virt_viewer_app_show_preferences(VirtViewerApp *app, GtkWidget *parent);
void virt_viewer_app_set_menus_sensitive(VirtViewerApp *self, gboolean sensitive);
gboolean virt_viewer_app_get_session_cancelled(VirtViewerApp *self);
gboolean virt_viewer_app_get_config_share_clipboard(VirtViewerApp *self);

View File

@ -57,7 +57,7 @@ static void virt_viewer_window_enable_modifiers(VirtViewerWindow *self);
static void virt_viewer_window_disable_modifiers(VirtViewerWindow *self);
static void virt_viewer_window_queue_resize(VirtViewerWindow *self);
static void virt_viewer_window_toolbar_setup(VirtViewerWindow *self);
static GtkMenu* virt_viewer_window_get_keycombo_menu(VirtViewerWindow *self);
static GMenu* virt_viewer_window_get_keycombo_menu(VirtViewerWindow *self);
static void virt_viewer_window_get_minimal_dimensions(VirtViewerWindow *self, guint *width, guint *height);
static gint virt_viewer_window_get_minimal_zoom_level(VirtViewerWindow *self);
static void virt_viewer_window_set_fullscreen(VirtViewerWindow *self,
@ -81,6 +81,7 @@ struct _VirtViewerWindow {
GtkWidget *toolbar;
GtkWidget *toolbar_usb_device_selection;
GtkWidget *toolbar_send_key;
GtkWidget *toolbar_send_menu;
GtkAccelGroup *accel_group;
VirtViewerNotebook *notebook;
VirtViewerDisplay *display;
@ -205,37 +206,19 @@ rebuild_combo_menu(GObject *gobject G_GNUC_UNUSED,
gpointer user_data)
{
VirtViewerWindow *self = user_data;
GtkWidget *menu;
GObject *button;
GMenu *menu;
menu = GTK_WIDGET(gtk_builder_get_object(self->builder, "menu-send"));
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu),
GTK_WIDGET(virt_viewer_window_get_keycombo_menu(self)));
}
button = gtk_builder_get_object(self->builder, "header-send-key");
menu = virt_viewer_window_get_keycombo_menu(self);
static void
vm_ui_changed(GObject *gobject G_GNUC_UNUSED,
GParamSpec *pspec G_GNUC_UNUSED,
gpointer user_data)
{
VirtViewerWindow *self = user_data;
gboolean vm_ui;
gtk_menu_button_set_menu_model(
GTK_MENU_BUTTON(button),
G_MENU_MODEL(menu));
g_object_get(G_OBJECT(self->app), "vm-ui", &vm_ui, NULL);
gtk_widget_set_visible(GTK_WIDGET(gtk_builder_get_object(self->builder, "menu-machine")), vm_ui);
}
static void
vm_running_changed(GObject *gobject G_GNUC_UNUSED,
GParamSpec *pspec G_GNUC_UNUSED,
gpointer user_data)
{
VirtViewerWindow *self = user_data;
GtkCheckMenuItem *check = GTK_CHECK_MENU_ITEM(gtk_builder_get_object(self->builder, "menu-vm-pause"));
gboolean running;
g_object_get(G_OBJECT(self->app), "vm-running", &running, NULL);
gtk_check_menu_item_set_active(check, !running);
if (self->toolbar_send_menu)
gtk_widget_destroy(self->toolbar_send_menu);
self->toolbar_send_menu = gtk_menu_new_from_model(G_MENU_MODEL(menu));
}
static void
@ -248,10 +231,6 @@ virt_viewer_window_constructed(GObject *object)
g_signal_connect(self->app, "notify::enable-accel",
G_CALLBACK(rebuild_combo_menu), object);
g_signal_connect(self->app, "notify::vm-ui",
G_CALLBACK(vm_ui_changed), object);
g_signal_connect(self->app, "notify::vm-running",
G_CALLBACK(vm_running_changed), object);
rebuild_combo_menu(NULL, NULL, object);
}
@ -313,14 +292,6 @@ virt_viewer_window_class_init (VirtViewerWindowClass *klass)
}
static gboolean
can_activate_cb (GtkWidget *widget G_GNUC_UNUSED,
guint signal_id G_GNUC_UNUSED,
VirtViewerWindow *self G_GNUC_UNUSED)
{
return TRUE;
}
static void
virt_viewer_window_action_zoom_out(GSimpleAction *act G_GNUC_UNUSED,
GVariant *param G_GNUC_UNUSED,
@ -557,6 +528,8 @@ virt_viewer_window_init (VirtViewerWindow *self)
{
GtkWidget *vbox;
GSList *accels;
GObject *menu;
GtkBuilder *menuBuilder;
self->fullscreen_monitor = -1;
g_value_init(&self->accel_setting, G_TYPE_STRING);
@ -570,24 +543,6 @@ virt_viewer_window_init (VirtViewerWindow *self)
self->accel_group = GTK_ACCEL_GROUP(gtk_builder_get_object(self->builder, "accelgroup"));
/* make sure they can be activated even if the menu item is not visible */
g_signal_connect(gtk_builder_get_object(self->builder, "menu-view-fullscreen"),
"can-activate-accel", G_CALLBACK(can_activate_cb), self);
g_signal_connect(gtk_builder_get_object(self->builder, "menu-file-smartcard-insert"),
"can-activate-accel", G_CALLBACK(can_activate_cb), self);
g_signal_connect(gtk_builder_get_object(self->builder, "menu-file-smartcard-remove"),
"can-activate-accel", G_CALLBACK(can_activate_cb), self);
g_signal_connect(gtk_builder_get_object(self->builder, "menu-view-release-cursor"),
"can-activate-accel", G_CALLBACK(can_activate_cb), self);
g_signal_connect(gtk_builder_get_object(self->builder, "menu-view-zoom-reset"),
"can-activate-accel", G_CALLBACK(can_activate_cb), self);
g_signal_connect(gtk_builder_get_object(self->builder, "menu-view-zoom-in"),
"can-activate-accel", G_CALLBACK(can_activate_cb), self);
g_signal_connect(gtk_builder_get_object(self->builder, "menu-view-zoom-out"),
"can-activate-accel", G_CALLBACK(can_activate_cb), self);
g_signal_connect(gtk_builder_get_object(self->builder, "menu-file-usb-device-reset"),
"can-activate-accel", G_CALLBACK(can_activate_cb), self);
vbox = GTK_WIDGET(gtk_builder_get_object(self->builder, "viewer-box"));
virt_viewer_window_toolbar_setup(self);
@ -600,6 +555,19 @@ virt_viewer_window_init (VirtViewerWindow *self)
gtk_window_add_accel_group(GTK_WINDOW(self->window), self->accel_group);
menuBuilder =
gtk_builder_new_from_resource(VIRT_VIEWER_RESOURCE_PREFIX "/ui/virt-viewer-menus.ui");
menu = gtk_builder_get_object(self->builder, "header-action");
gtk_menu_button_set_menu_model(
GTK_MENU_BUTTON(menu),
G_MENU_MODEL(gtk_builder_get_object(menuBuilder, "action-menu")));
menu = gtk_builder_get_object(self->builder, "header-machine");
gtk_menu_button_set_menu_model(
GTK_MENU_BUTTON(menu),
G_MENU_MODEL(gtk_builder_get_object(menuBuilder, "machine-menu")));
virt_viewer_window_update_title(self);
gtk_window_set_resizable(GTK_WINDOW(self->window), TRUE);
self->accel_enabled = TRUE;
@ -718,19 +686,9 @@ mapped(GtkWidget *widget, GdkEvent *event G_GNUC_UNUSED,
return FALSE;
}
static void
virt_viewer_window_menu_fullscreen_set_active(VirtViewerWindow *self, gboolean active)
{
GtkCheckMenuItem *check = GTK_CHECK_MENU_ITEM(gtk_builder_get_object(self->builder, "menu-view-fullscreen"));
gtk_check_menu_item_set_active(check, active);
}
void
virt_viewer_window_leave_fullscreen(VirtViewerWindow *self)
{
GtkWidget *menu = GTK_WIDGET(gtk_builder_get_object(self->builder, "top-menu"));
/* if we enter and leave fullscreen mode before being shown, make sure to
* disconnect the mapped signal handler */
g_signal_handlers_disconnect_by_func(self->window, mapped, self);
@ -738,7 +696,6 @@ virt_viewer_window_leave_fullscreen(VirtViewerWindow *self)
if (!self->fullscreen)
return;
virt_viewer_window_menu_fullscreen_set_active(self, FALSE);
self->fullscreen = FALSE;
self->fullscreen_monitor = -1;
if (self->display) {
@ -746,7 +703,6 @@ virt_viewer_window_leave_fullscreen(VirtViewerWindow *self)
virt_viewer_display_set_fullscreen(self->display, FALSE);
}
virt_viewer_timed_revealer_force_reveal(self->revealer, FALSE);
gtk_widget_show(menu);
gtk_widget_hide(self->toolbar);
gtk_widget_set_size_request(self->window, -1, -1);
gtk_window_unfullscreen(GTK_WINDOW(self->window));
@ -756,8 +712,6 @@ virt_viewer_window_leave_fullscreen(VirtViewerWindow *self)
void
virt_viewer_window_enter_fullscreen(VirtViewerWindow *self, gint monitor)
{
GtkWidget *menu = GTK_WIDGET(gtk_builder_get_object(self->builder, "top-menu"));
if (self->fullscreen && self->fullscreen_monitor != monitor)
virt_viewer_window_leave_fullscreen(self);
@ -778,9 +732,6 @@ virt_viewer_window_enter_fullscreen(VirtViewerWindow *self, gint monitor)
return;
}
virt_viewer_window_menu_fullscreen_set_active(self, TRUE);
gtk_widget_hide(menu);
if (!self->kiosk) {
gtk_widget_show(self->toolbar);
virt_viewer_timed_revealer_force_reveal(self->revealer, TRUE);
@ -839,24 +790,19 @@ get_nkeys(const guint32 *keys)
}
static void
virt_viewer_menu_add_combo(VirtViewerWindow *self G_GNUC_UNUSED, GtkMenu *menu,
virt_viewer_menu_add_combo(VirtViewerWindow *self G_GNUC_UNUSED, GMenu *menu,
const guint *keys, const gchar *label)
{
GtkWidget *item;
GMenuItem *item = g_menu_item_new(label, NULL);
if (keys == NULL || keys[0] == GDK_KEY_VoidSymbol) {
item = gtk_separator_menu_item_new();
} else {
item = gtk_menu_item_new_with_label(label);
gtk_actionable_set_action_name(GTK_ACTIONABLE(item), "win.send-key");
gtk_actionable_set_action_target_value(GTK_ACTIONABLE(item),
g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
keys,
get_nkeys(keys),
sizeof(guint32)));
}
g_menu_item_set_action_and_target_value(item,
"win.send-key",
g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
keys,
get_nkeys(keys),
sizeof(guint32)));
gtk_container_add(GTK_CONTAINER(menu), item);
g_menu_append_item(menu, item);
}
static guint*
@ -892,28 +838,40 @@ accel_key_to_keys(guint accel_key,
return keys;
}
static GtkMenu*
static GMenu *
virt_viewer_window_get_keycombo_menu(VirtViewerWindow *self)
{
gint i, j;
GtkMenu *menu = GTK_MENU(gtk_menu_new());
gtk_menu_set_accel_group(menu, self->accel_group);
GMenu *menu = g_menu_new();
GMenu *sectionitems = g_menu_new();
GMenuItem *section = g_menu_item_new_section(NULL, G_MENU_MODEL(sectionitems));
g_menu_append_item(menu, section);
for (i = 0 ; i < G_N_ELEMENTS(keyCombos); i++) {
gchar *label = NULL;
if (keyCombos[i].keys[0] != GDK_KEY_VoidSymbol) {
if (keyCombos[i].keys[0] == GDK_KEY_VoidSymbol) {
sectionitems = g_menu_new();
section = g_menu_item_new_section(NULL, G_MENU_MODEL(sectionitems));
g_menu_append_item(menu, section);
} else {
gchar *label = NULL;
guint key;
GdkModifierType mods;
gtk_accelerator_parse(keyCombos[i].accel_label, &key, &mods);
label = gtk_accelerator_get_label(key, mods);
virt_viewer_menu_add_combo(self, sectionitems, keyCombos[i].keys, label);
g_free(label);
}
virt_viewer_menu_add_combo(self, menu, keyCombos[i].keys, label);
g_free(label);
}
if (virt_viewer_app_get_enable_accel(self->app)) {
gchar **accelactions = gtk_application_list_action_descriptions(GTK_APPLICATION(self->app));
sectionitems = g_menu_new();
section = g_menu_item_new_section(NULL, G_MENU_MODEL(sectionitems));
g_menu_append_item(menu, section);
for (i = 0; accelactions[i] != NULL; i++) {
gchar **accels = gtk_application_get_accels_for_action(GTK_APPLICATION(self->app),
accelactions[i]);
@ -925,7 +883,7 @@ virt_viewer_window_get_keycombo_menu(VirtViewerWindow *self)
guint *keys = accel_key_to_keys(accel_key, accel_mods);
gchar *label = gtk_accelerator_get_label(accel_key, accel_mods);
virt_viewer_menu_add_combo(self, menu, keys, label);
virt_viewer_menu_add_combo(self, sectionitems, keys, label);
g_free(label);
g_free(keys);
}
@ -935,7 +893,6 @@ virt_viewer_window_get_keycombo_menu(VirtViewerWindow *self)
g_strfreev(accelactions);
}
gtk_widget_show_all(GTK_WIDGET(menu));
return menu;
}
@ -1034,7 +991,6 @@ virt_viewer_window_set_fullscreen(VirtViewerWindow *self,
}
}
static void keycombo_menu_location(GtkMenu *menu G_GNUC_UNUSED, gint *x, gint *y,
gboolean *push_in, gpointer user_data)
{
@ -1054,15 +1010,13 @@ static void
virt_viewer_window_toolbar_send_key(GtkWidget *button G_GNUC_UNUSED,
VirtViewerWindow *self)
{
GtkMenu *menu = virt_viewer_window_get_keycombo_menu(self);
gtk_menu_attach_to_widget(menu, self->window, NULL);
g_object_ref_sink(menu);
gtk_menu_popup(menu, NULL, NULL, keycombo_menu_location, self,
gtk_menu_attach_to_widget(GTK_MENU(self->toolbar_send_menu),
self->window, NULL);
gtk_menu_popup(GTK_MENU(self->toolbar_send_menu),
NULL, NULL, keycombo_menu_location, self,
0, gtk_get_current_event_time());
g_object_unref(menu);
}
static void add_if_writable (GdkPixbufFormat *data, GHashTable *formats)
{
if (gdk_pixbuf_format_is_writable(data)) {
@ -1345,7 +1299,6 @@ virt_viewer_window_toolbar_setup(VirtViewerWindow *self)
gtk_widget_show(button);
gtk_toolbar_insert(GTK_TOOLBAR(self->toolbar), GTK_TOOL_ITEM(button), 0);
g_signal_connect(button, "clicked", G_CALLBACK(virt_viewer_window_toolbar_send_key), self);
gtk_widget_set_sensitive(button, FALSE);
self->toolbar_send_key = button;
/* Leave fullscreen */
@ -1467,69 +1420,62 @@ virt_viewer_window_update_title(VirtViewerWindow *self)
g_free(title);
}
void
virt_viewer_window_set_menu_displays_sensitive(VirtViewerWindow *self, gboolean sensitive)
{
GtkWidget *menu;
g_return_if_fail(VIRT_VIEWER_IS_WINDOW(self));
menu = GTK_WIDGET(gtk_builder_get_object(self->builder, "menu-displays"));
gtk_widget_set_sensitive(menu, sensitive);
}
void
virt_viewer_window_set_usb_options_sensitive(VirtViewerWindow *self, gboolean sensitive)
{
GtkWidget *menu;
GAction *action;
GActionMap *map;
g_return_if_fail(VIRT_VIEWER_IS_WINDOW(self));
menu = GTK_WIDGET(gtk_builder_get_object(self->builder, "menu-file-usb-device-selection"));
gtk_widget_set_sensitive(menu, sensitive);
gtk_widget_set_visible(self->toolbar_usb_device_selection, sensitive);
map = G_ACTION_MAP(self->window);
action = g_action_map_lookup_action(map, "usb-device-select");
g_simple_action_set_enabled(G_SIMPLE_ACTION(action), sensitive);
}
void
virt_viewer_window_set_usb_reset_sensitive(VirtViewerWindow *self, gboolean sensitive)
{
GtkWidget *menu;
GAction *action;
GActionMap *map;
g_return_if_fail(VIRT_VIEWER_IS_WINDOW(self));
menu = GTK_WIDGET(gtk_builder_get_object(self->builder, "menu-file-usb-device-reset"));
gtk_widget_set_sensitive(menu, sensitive);
map = G_ACTION_MAP(self->window);
action = g_action_map_lookup_action(map, "usb-device-reset");
g_simple_action_set_enabled(G_SIMPLE_ACTION(action), sensitive);
}
void
virt_viewer_window_set_menus_sensitive(VirtViewerWindow *self, gboolean sensitive)
virt_viewer_window_set_actions_sensitive(VirtViewerWindow *self, gboolean sensitive)
{
GtkWidget *menu;
GAction *action;
GActionMap *map;
g_return_if_fail(VIRT_VIEWER_IS_WINDOW(self));
menu = GTK_WIDGET(gtk_builder_get_object(self->builder, "menu-preferences"));
gtk_widget_set_sensitive(menu, sensitive);
map = G_ACTION_MAP(self->window);
action = g_action_map_lookup_action(map, "preferences");
g_simple_action_set_enabled(G_SIMPLE_ACTION(action), sensitive);
menu = GTK_WIDGET(gtk_builder_get_object(self->builder, "menu-file-screenshot"));
gtk_widget_set_sensitive(menu, sensitive &&
VIRT_VIEWER_DISPLAY_CAN_SCREENSHOT(self->display));
action = g_action_map_lookup_action(map, "screenshot");
g_simple_action_set_enabled(G_SIMPLE_ACTION(action),
sensitive &&
VIRT_VIEWER_DISPLAY_CAN_SCREENSHOT(self->display));
menu = GTK_WIDGET(gtk_builder_get_object(self->builder, "menu-view-zoom"));
gtk_widget_set_sensitive(menu, sensitive);
action = g_action_map_lookup_action(map, "zoom-in");
g_simple_action_set_enabled(G_SIMPLE_ACTION(action), sensitive);
menu = GTK_WIDGET(gtk_builder_get_object(self->builder, "menu-machine"));
gtk_widget_set_sensitive(menu, sensitive);
action = g_action_map_lookup_action(map, "zoom-out");
g_simple_action_set_enabled(G_SIMPLE_ACTION(action), sensitive);
{
gboolean can_send = sensitive &&
VIRT_VIEWER_DISPLAY_CAN_SEND_KEYS(self->display);
action = g_action_map_lookup_action(map, "zoom-reset");
g_simple_action_set_enabled(G_SIMPLE_ACTION(action), sensitive);
menu = GTK_WIDGET(gtk_builder_get_object(self->builder, "menu-send"));
gtk_widget_set_sensitive(menu, can_send);
gtk_widget_set_sensitive(self->toolbar_send_key, can_send);
}
action = g_action_map_lookup_action(map, "send-key");
g_simple_action_set_enabled(G_SIMPLE_ACTION(action),
sensitive &&
VIRT_VIEWER_DISPLAY_CAN_SEND_KEYS(self->display));
}
static void
@ -1537,6 +1483,8 @@ display_show_hint(VirtViewerDisplay *display,
GParamSpec *pspec G_GNUC_UNUSED,
VirtViewerWindow *self)
{
GAction *action;
GActionMap *map;
guint hint;
g_object_get(display, "show-hint", &hint, NULL);
@ -1548,8 +1496,13 @@ display_show_hint(VirtViewerDisplay *display,
virt_viewer_window_set_zoom_level(self, self->zoomlevel);
}
gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object(self->builder, "menu-file-screenshot")), hint);
map = G_ACTION_MAP(self->window);
action = g_action_map_lookup_action(map, "screenshot");
g_simple_action_set_enabled(G_SIMPLE_ACTION(action),
hint);
}
static gboolean
window_key_pressed (GtkWidget *widget G_GNUC_UNUSED,
GdkEvent *ev,
@ -1642,8 +1595,6 @@ virt_viewer_window_set_display(VirtViewerWindow *self, VirtViewerDisplay *displa
if (virt_viewer_display_get_enabled(display))
virt_viewer_window_desktop_resize(display, self);
virt_viewer_window_set_menus_sensitive(self, TRUE);
}
}
@ -1733,12 +1684,17 @@ gint virt_viewer_window_get_zoom_level(VirtViewerWindow *self)
return self->zoomlevel;
}
GtkMenuItem*
GMenuModel *
virt_viewer_window_get_menu_displays(VirtViewerWindow *self)
{
GObject *menu;
GMenuModel *model;
g_return_val_if_fail(VIRT_VIEWER_IS_WINDOW(self), NULL);
return GTK_MENU_ITEM(gtk_builder_get_object(self->builder, "menu-displays"));
menu = gtk_builder_get_object(self->builder, "header-machine");
model = gtk_menu_button_get_menu_model(GTK_MENU_BUTTON(menu));
return g_menu_model_get_item_link(model, 0, G_MENU_LINK_SECTION);
}
GtkBuilder*
@ -1775,20 +1731,12 @@ virt_viewer_window_set_kiosk(VirtViewerWindow *self, gboolean enabled)
}
static void
virt_viewer_window_get_minimal_dimensions(VirtViewerWindow *self,
virt_viewer_window_get_minimal_dimensions(VirtViewerWindow *self G_GNUC_UNUSED,
guint *width,
guint *height)
{
GtkRequisition req;
GtkWidget *top_menu;
top_menu = GTK_WIDGET(gtk_builder_get_object(virt_viewer_window_get_builder(self), "top-menu"));
gtk_widget_get_preferred_size(top_menu, &req, NULL);
/* minimal dimensions of the window are the maximum of dimensions of the top-menu
* and minimal dimension of the display
*/
*height = MIN_DISPLAY_HEIGHT;
*width = MAX(MIN_DISPLAY_WIDTH, req.width);
*width = MIN_DISPLAY_WIDTH;
}
/**

View File

@ -46,10 +46,9 @@ GtkWindow* virt_viewer_window_get_window (VirtViewerWindow* window);
VirtViewerNotebook* virt_viewer_window_get_notebook (VirtViewerWindow* window);
void virt_viewer_window_set_display(VirtViewerWindow *self, VirtViewerDisplay *display);
VirtViewerDisplay* virt_viewer_window_get_display(VirtViewerWindow *self);
void virt_viewer_window_set_menu_displays_sensitive(VirtViewerWindow *self, gboolean sensitive);
void virt_viewer_window_set_usb_options_sensitive(VirtViewerWindow *self, gboolean sensitive);
void virt_viewer_window_set_usb_reset_sensitive(VirtViewerWindow *self, gboolean sensitive);
void virt_viewer_window_set_menus_sensitive(VirtViewerWindow *self, gboolean sensitive);
void virt_viewer_window_set_actions_sensitive(VirtViewerWindow *self, gboolean sensitive);
void virt_viewer_window_update_title(VirtViewerWindow *self);
void virt_viewer_window_show(VirtViewerWindow *self);
void virt_viewer_window_hide(VirtViewerWindow *self);
@ -60,7 +59,7 @@ void virt_viewer_window_zoom_reset(VirtViewerWindow *self);
gint virt_viewer_window_get_zoom_level(VirtViewerWindow *self);
void virt_viewer_window_leave_fullscreen(VirtViewerWindow *self);
void virt_viewer_window_enter_fullscreen(VirtViewerWindow *self, gint monitor);
GtkMenuItem *virt_viewer_window_get_menu_displays(VirtViewerWindow *self);
GMenuModel *virt_viewer_window_get_menu_displays(VirtViewerWindow *self);
GtkBuilder* virt_viewer_window_get_builder(VirtViewerWindow *window);
void virt_viewer_window_set_kiosk(VirtViewerWindow *self, gboolean enabled);
void virt_viewer_window_show_about(VirtViewerWindow *self);

View File

@ -294,7 +294,6 @@ virt_viewer_deactivated(VirtViewerApp *app, gboolean connect_error)
virt_viewer_app_show_status(app, _("Waiting for guest domain to re-start"));
virt_viewer_app_trace(app, "Guest %s display has disconnected, waiting to reconnect", self->domkey);
virt_viewer_app_set_menus_sensitive(app, FALSE);
} else {
VIRT_VIEWER_APP_CLASS(virt_viewer_parent_class)->deactivated(app, connect_error);
}