diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js index d6b0b7aeb..96270a479 100644 --- a/js/ui/windowManager.js +++ b/js/ui/windowManager.js @@ -6,12 +6,14 @@ const Gio = imports.gi.Gio; const Lang = imports.lang; const Mainloop = imports.mainloop; const Meta = imports.gi.Meta; +const Pango = imports.gi.Pango; const St = imports.gi.St; const Shell = imports.gi.Shell; const AltTab = imports.ui.altTab; const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup; const Main = imports.ui.main; +const ModalDialog = imports.ui.modalDialog; const Tweener = imports.ui.tweener; const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings'; @@ -20,6 +22,107 @@ const DIM_BRIGHTNESS = -0.3; const DIM_TIME = 0.500; const UNDIM_TIME = 0.250; +const DISPLAY_REVERT_TIMEOUT = 20; // in seconds - keep in sync with mutter +const ONE_SECOND = 1000; // in ms + +const DisplayChangeDialog = new Lang.Class({ + Name: 'DisplayChangeDialog', + Extends: ModalDialog.ModalDialog, + + _init: function(wm) { + this.parent({ styleClass: 'prompt-dialog' }); + + this._wm = wm; + + let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout', + vertical: false }); + this.contentLayout.add(mainContentBox, + { x_fill: true, + y_fill: true }); + + let icon = new St.Icon({ icon_name: 'preferences-desktop-display-symbolic' }); + mainContentBox.add(icon, + { x_fill: true, + y_fill: false, + x_align: St.Align.END, + y_align: St.Align.START }); + + let messageBox = new St.BoxLayout({ style_class: 'prompt-dialog-message-layout', + vertical: true }); + mainContentBox.add(messageBox, + { expand: true, y_align: St.Align.START }); + + let subjectLabel = new St.Label({ style_class: 'prompt-dialog-headline', + text: _("Do you want to keep these display settings?") }); + messageBox.add(subjectLabel, + { y_fill: false, + y_align: St.Align.START }); + + this._countDown = DISPLAY_REVERT_TIMEOUT; + let message = this._formatCountDown(); + this._descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description', + text: this._formatCountDown() }); + this._descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; + this._descriptionLabel.clutter_text.line_wrap = true; + + messageBox.add(this._descriptionLabel, + { y_fill: true, + y_align: St.Align.START }); + + /* Translators: this and the following message should be limited in lenght, + to avoid ellipsizing the labels. + */ + this._cancelButton = this.addButton({ label: _("Revert Settings"), + action: Lang.bind(this, this._onFailure), + key: Clutter.Escape }, + { expand: true, x_fill: false, x_align: St.Align.START }); + this._okButton = this.addButton({ label: _("Keep Changes"), + action: Lang.bind(this, this._onSuccess), + default: true }, + { expand: false, x_fill: false, x_align: St.Align.END }); + + this._timeoutId = Mainloop.timeout_add(ONE_SECOND, Lang.bind(this, this._tick)); + }, + + close: function(timestamp) { + if (this._timeoutId > 0) { + Mainloop.source_remove(this._timeoutId); + this._timeoutId = 0; + } + + this.parent(timestamp); + }, + + _formatCountDown: function() { + let fmt = ngettext("Settings changes will revert in %d second", + "Settings changes will revert in %d seconds"); + return fmt.format(this._countDown); + }, + + _tick: function() { + this._countDown--; + + if (this._countDown == 0) { + /* mutter already takes care of failing at timeout */ + this._timeoutId = 0; + this.close(); + return false; + } + + this._descriptionLabel.text = this._formatCountDown(); + return true; + }, + + _onFailure: function() { + this._wm.complete_display_change(false); + this.close(); + }, + + _onSuccess: function() { + this._wm.complete_display_change(true); + this.close(); + }, +}); const WindowDimmer = new Lang.Class({ Name: 'WindowDimmer', @@ -306,6 +409,7 @@ const WindowManager = new Lang.Class({ this._shellwm.connect('map', Lang.bind(this, this._mapWindow)); this._shellwm.connect('destroy', Lang.bind(this, this._destroyWindow)); this._shellwm.connect('filter-keybinding', Lang.bind(this, this._filterKeybinding)); + this._shellwm.connect('confirm-display-change', Lang.bind(this, this._confirmDisplayChange)); this._workspaceSwitcherPopup = null; this.setCustomKeybindingHandler('switch-to-workspace-left', @@ -1056,4 +1160,9 @@ const WindowManager = new Lang.Class({ workspace.activate_with_focus (window, global.get_current_time()); } }, + + _confirmDisplayChange: function() { + let dialog = new DisplayChangeDialog(this._shellwm); + dialog.open(); + }, }); diff --git a/src/gnome-shell-plugin.c b/src/gnome-shell-plugin.c index 3470637fa..6571b3923 100644 --- a/src/gnome-shell-plugin.c +++ b/src/gnome-shell-plugin.c @@ -75,6 +75,9 @@ static gboolean gnome_shell_plugin_xevent_filter (MetaPlugin *plugi static gboolean gnome_shell_plugin_keybinding_filter (MetaPlugin *plugin, MetaKeyBinding *binding); + +static void gnome_shell_plugin_confirm_display_change (MetaPlugin *plugin); + static const MetaPluginInfo *gnome_shell_plugin_plugin_info (MetaPlugin *plugin); @@ -132,6 +135,9 @@ gnome_shell_plugin_class_init (GnomeShellPluginClass *klass) plugin_class->xevent_filter = gnome_shell_plugin_xevent_filter; plugin_class->keybinding_filter = gnome_shell_plugin_keybinding_filter; + + plugin_class->confirm_display_change = gnome_shell_plugin_confirm_display_change; + plugin_class->plugin_info = gnome_shell_plugin_plugin_info; } @@ -399,6 +405,12 @@ gnome_shell_plugin_keybinding_filter (MetaPlugin *plugin, return _shell_wm_filter_keybinding (get_shell_wm (), binding); } +static void +gnome_shell_plugin_confirm_display_change (MetaPlugin *plugin) +{ + _shell_wm_confirm_display_change (get_shell_wm ()); +} + static const MetaPluginInfo *gnome_shell_plugin_plugin_info (MetaPlugin *plugin) { diff --git a/src/shell-wm-private.h b/src/shell-wm-private.h index 6fc9348ce..bc9d4c317 100644 --- a/src/shell-wm-private.h +++ b/src/shell-wm-private.h @@ -38,6 +38,8 @@ void _shell_wm_kill_switch_workspace (ShellWM *wm); gboolean _shell_wm_filter_keybinding (ShellWM *wm, MetaKeyBinding *binding); +void _shell_wm_confirm_display_change (ShellWM *wm); + G_END_DECLS #endif /* __SHELL_WM_PRIVATE_H__ */ diff --git a/src/shell-wm.c b/src/shell-wm.c index 08aceb7f2..20f101ed4 100644 --- a/src/shell-wm.c +++ b/src/shell-wm.c @@ -27,6 +27,7 @@ enum KILL_SWITCH_WORKSPACE, KILL_WINDOW_EFFECTS, FILTER_KEYBINDING, + CONFIRM_DISPLAY_CHANGE, LAST_SIGNAL }; @@ -124,6 +125,13 @@ shell_wm_class_init (ShellWMClass *klass) g_signal_accumulator_true_handled, NULL, NULL, G_TYPE_BOOLEAN, 1, META_TYPE_KEY_BINDING); + shell_wm_signals[CONFIRM_DISPLAY_CHANGE] = + g_signal_new ("confirm-display-change", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); } void @@ -219,6 +227,20 @@ shell_wm_completed_destroy (ShellWM *wm, meta_plugin_destroy_completed (wm->plugin, actor); } +/** + * shell_wm_complete_display_change: + * @wm: the ShellWM + * @ok: if the new configuration was OK + * + * The plugin must call this after the user responded to the confirmation dialog. + */ +void +shell_wm_complete_display_change (ShellWM *wm, + gboolean ok) +{ + meta_plugin_complete_display_change (wm->plugin, ok); +} + void _shell_wm_kill_switch_workspace (ShellWM *wm) { @@ -287,6 +309,12 @@ _shell_wm_filter_keybinding (ShellWM *wm, return rv; } +void +_shell_wm_confirm_display_change (ShellWM *wm) +{ + g_signal_emit (wm, shell_wm_signals[CONFIRM_DISPLAY_CHANGE], 0); +} + /** * shell_wm_new: * @plugin: the #MetaPlugin diff --git a/src/shell-wm.h b/src/shell-wm.h index 20b740a3b..778af523e 100644 --- a/src/shell-wm.h +++ b/src/shell-wm.h @@ -39,6 +39,9 @@ void shell_wm_completed_destroy (ShellWM *wm, MetaWindowActor *actor); void shell_wm_completed_switch_workspace (ShellWM *wm); +void shell_wm_complete_display_change (ShellWM *wm, + gboolean ok); + G_END_DECLS #endif /* __SHELL_WM_H__ */