From e8fd09e340d6db9949bd61c8c30db8d2edbc8a1d Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Aug 01 2017 03:45:27 +0000 Subject: import gnome-shell-extensions-3.22.2-10.el7 --- diff --git a/.gitignore b/.gitignore index 0455c8e..0e00f4e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/gnome-shell-extensions-3.14.4.tar.xz +SOURCES/gnome-shell-extensions-3.22.2.tar.xz diff --git a/.gnome-shell-extensions.metadata b/.gnome-shell-extensions.metadata index 8cb9112..7ede0b6 100644 --- a/.gnome-shell-extensions.metadata +++ b/.gnome-shell-extensions.metadata @@ -1 +1 @@ -2f07cff9dfa756a6e16c3ac58b9ceb2fb229c022 SOURCES/gnome-shell-extensions-3.14.4.tar.xz +ba47368620b445a98543de0461f05529a3b7a0b9 SOURCES/gnome-shell-extensions-3.22.2.tar.xz diff --git a/SOURCES/0001-Use-a-proper-arrows-instead-of-UTF8.patch b/SOURCES/0001-Use-a-proper-arrows-instead-of-UTF8.patch deleted file mode 100644 index fe08007..0000000 --- a/SOURCES/0001-Use-a-proper-arrows-instead-of-UTF8.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 271f4dee47e19e3e0f3f7c6a8cf49e41044899a1 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Florian=20M=C3=BCllner?= -Date: Mon, 9 Mar 2015 20:58:43 +0100 -Subject: [PATCH] Use a proper arrows instead of UTF8 - -All proper shell menus were updated to do that a while ago, we -should do the same for consistency. - -https://bugzilla.gnome.org/show_bug.cgi?id=745909 ---- - extensions/drive-menu/extension.js | 4 +--- - extensions/places-menu/extension.js | 4 +--- - 2 files changed, 2 insertions(+), 6 deletions(-) - -diff --git a/extensions/drive-menu/extension.js b/extensions/drive-menu/extension.js -index fe42a4c..8d22987 100644 ---- a/extensions/drive-menu/extension.js -+++ b/extensions/drive-menu/extension.js -@@ -128,9 +128,7 @@ const DriveMenu = new Lang.Class({ - style_class: 'system-status-icon' }); - - hbox.add_child(icon); -- hbox.add_child(new St.Label({ text: '\u25BE', -- y_expand: true, -- y_align: Clutter.ActorAlign.CENTER })); -+ hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM)); - this.actor.add_child(hbox); - - this._monitor = Gio.VolumeMonitor.get(); -diff --git a/extensions/places-menu/extension.js b/extensions/places-menu/extension.js -index a8660c8..767fb87 100644 ---- a/extensions/places-menu/extension.js -+++ b/extensions/places-menu/extension.js -@@ -82,9 +82,7 @@ const PlacesMenu = new Lang.Class({ - y_expand: true, - y_align: Clutter.ActorAlign.CENTER }); - hbox.add_child(label); -- hbox.add_child(new St.Label({ text: '\u25BE', -- y_expand: true, -- y_align: Clutter.ActorAlign.CENTER })); -+ hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM)); - this.actor.add_actor(hbox); - - this.placesManager = new PlaceDisplay.PlacesManager(); --- -2.3.6 - diff --git a/SOURCES/0001-alternateTab-Adjust-to-gnome-shell-changes.patch b/SOURCES/0001-alternateTab-Adjust-to-gnome-shell-changes.patch deleted file mode 100644 index 10b1689..0000000 --- a/SOURCES/0001-alternateTab-Adjust-to-gnome-shell-changes.patch +++ /dev/null @@ -1,71 +0,0 @@ -From e98e5d0d6232da905bfec8023fc8ef557893b77d Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Florian=20M=C3=BCllner?= -Date: Tue, 31 May 2016 17:12:20 +0200 -Subject: [PATCH] alternateTab: Adjust to gnome-shell changes - -The code de-duplication in commit bf8d30603e57b broke the extension, -fix by duplicating the code here now :-( - -(It's not really that bad though ...) - -https://bugzilla.gnome.org/show_bug.cgi?id=767077 ---- - extensions/alternate-tab/extension.js | 29 +++++++++++++++++++++-------- - 1 file changed, 21 insertions(+), 8 deletions(-) - -diff --git a/extensions/alternate-tab/extension.js b/extensions/alternate-tab/extension.js -index 79fde0a..7a5ee17 100644 ---- a/extensions/alternate-tab/extension.js -+++ b/extensions/alternate-tab/extension.js -@@ -7,6 +7,7 @@ const Shell = imports.gi.Shell; - - const AltTab = imports.ui.altTab; - const Main = imports.ui.main; -+const WindowManager = imports.ui.windowManager; - - let injections = {}; - -@@ -33,20 +34,32 @@ function enable() { - return injections['_keyPressHandler'].call(this, keysym, action); - }; - -- setKeybinding('switch-applications', Lang.bind(Main.wm, Main.wm._startWindowSwitcher)); -- setKeybinding('switch-group', Lang.bind(Main.wm, Main.wm._startWindowSwitcher)); -- setKeybinding('switch-applications-backward', Lang.bind(Main.wm, Main.wm._startWindowSwitcher)); -- setKeybinding('switch-group-backward', Lang.bind(Main.wm, Main.wm._startWindowSwitcher)); -+ Main.wm._forcedWindowSwitcher = function(display, screen, window, binding) { -+ /* prevent a corner case where both popups show up at once */ -+ if (this._workspaceSwitcherPopup != null) -+ this._workspaceSwitcherPopup.destroy(); -+ -+ let tabPopup = new AltTab.WindowSwitcherPopup(); -+ -+ if (!tabPopup.show(binding.is_reversed(), binding.get_name(), binding.get_mask())) -+ tabPopup.destroy(); -+ }; -+ -+ setKeybinding('switch-applications', Lang.bind(Main.wm, Main.wm._forcedWindowSwitcher)); -+ setKeybinding('switch-group', Lang.bind(Main.wm, Main.wm._forcedWindowSwitcher)); -+ setKeybinding('switch-applications-backward', Lang.bind(Main.wm, Main.wm._forcedWindowSwitcher)); -+ setKeybinding('switch-group-backward', Lang.bind(Main.wm, Main.wm._forcedWindowSwitcher)); - } - - function disable() { - var prop; - -- setKeybinding('switch-applications', Lang.bind(Main.wm, Main.wm._startAppSwitcher)); -- setKeybinding('switch-group', Lang.bind(Main.wm, Main.wm._startAppSwitcher)); -- setKeybinding('switch-applications-backward', Lang.bind(Main.wm, Main.wm._startAppSwitcher)); -- setKeybinding('switch-group-backward', Lang.bind(Main.wm, Main.wm._startAppSwitcher)); -+ setKeybinding('switch-applications', Lang.bind(Main.wm, Main.wm._startSwitcher)); -+ setKeybinding('switch-group', Lang.bind(Main.wm, Main.wm._startSwitcher)); -+ setKeybinding('switch-applications-backward', Lang.bind(Main.wm, Main.wm._startSwitcher)); -+ setKeybinding('switch-group-backward', Lang.bind(Main.wm, Main.wm._startSwitcher)); - - for (prop in injections) - AltTab.WindowSwitcherPopup.prototype[prop] = injections[prop]; -+ delete Main.wm._forcedWindowSwitcher; - } --- -2.7.4 - diff --git a/SOURCES/0001-apps-menu-Explicitly-set-label_actor.patch b/SOURCES/0001-apps-menu-Explicitly-set-label_actor.patch index d471ece..cc9c24c 100644 --- a/SOURCES/0001-apps-menu-Explicitly-set-label_actor.patch +++ b/SOURCES/0001-apps-menu-Explicitly-set-label_actor.patch @@ -1,4 +1,4 @@ -From 211feb53560ddae28d4f1b782fb97566e38d9d6e Mon Sep 17 00:00:00 2001 +From 674ad0b6469e6797c83fd5932a670b76def884d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 17 Mar 2016 17:15:38 +0100 Subject: [PATCH] apps-menu: Explicitly set label_actor @@ -10,10 +10,10 @@ so set the label_actor explicitly as workaround. 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js -index 719f298..1f68ea4 100644 +index 8e7afef..c840a06 100644 --- a/extensions/apps-menu/extension.js +++ b/extensions/apps-menu/extension.js -@@ -37,7 +37,9 @@ const ActivitiesMenuItem = new Lang.Class({ +@@ -36,7 +36,9 @@ const ActivitiesMenuItem = new Lang.Class({ _init: function(button) { this.parent(); this._button = button; @@ -24,7 +24,7 @@ index 719f298..1f68ea4 100644 }, activate: function(event) { -@@ -114,7 +116,9 @@ const CategoryMenuItem = new Lang.Class({ +@@ -109,7 +111,9 @@ const CategoryMenuItem = new Lang.Class({ else name = _("Favorites"); @@ -36,5 +36,5 @@ index 719f298..1f68ea4 100644 }, -- -2.9.3 +2.12.0 diff --git a/SOURCES/0001-apps-menu-Fix-call-to-open_new_window.patch b/SOURCES/0001-apps-menu-Fix-call-to-open_new_window.patch deleted file mode 100644 index fd00f7f..0000000 --- a/SOURCES/0001-apps-menu-Fix-call-to-open_new_window.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 3051fbc5dd204cc6dade56b9d395a4cfc3bcd698 Mon Sep 17 00:00:00 2001 -From: Siteshwar Vashisht -Date: Tue, 9 Jun 2015 17:43:20 +0530 -Subject: [PATCH] apps-menu: Fix call to open_new_window() - -The function never had a timestamp parameter, the parameter that was -added at some point is a workspace index. Ouch, this has gone unspotted -since the original AxeMenu extension was adapted for the apps-menu ... ---- - extensions/apps-menu/extension.js | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js -index f8438fd..5ac4e8e 100644 ---- a/extensions/apps-menu/extension.js -+++ b/extensions/apps-menu/extension.js -@@ -74,7 +74,7 @@ const ApplicationMenuItem = new Lang.Class({ - }, - - activate: function(event) { -- this._app.open_new_window(event.get_time()); -+ this._app.open_new_window(-1); - this._button.selectCategory(null, null); - this._button.menu.toggle(); - this.parent(event); --- -2.4.3 - diff --git a/SOURCES/0001-apps-menu-Handle-.desktop-files-from-non-standard-di.patch b/SOURCES/0001-apps-menu-Handle-.desktop-files-from-non-standard-di.patch deleted file mode 100644 index 49ecbea..0000000 --- a/SOURCES/0001-apps-menu-Handle-.desktop-files-from-non-standard-di.patch +++ /dev/null @@ -1,31 +0,0 @@ -From e23708f94570806457ab13d682989e23331fb951 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Florian=20M=C3=BCllner?= -Date: Wed, 6 Jul 2016 20:41:25 +0200 -Subject: [PATCH] apps-menu: Handle .desktop files from non-standard - directories - -.desktop files in non-standard locations are not handled by GIO, -so looking up apps for entries for such locations (e.g. a -directory added via the AppsDir directive) will fail. We can -still handle this case in the menu by creating the app directly -from the entry's AppInfo. ---- - extensions/apps-menu/extension.js | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js -index 5221a41..5a41890 100644 ---- a/extensions/apps-menu/extension.js -+++ b/extensions/apps-menu/extension.js -@@ -424,6 +424,8 @@ const ApplicationsButton = new Lang.Class({ - let entry = iter.get_entry(); - let appInfo = entry.get_app_info(); - let app = appSys.lookup_app(entry.get_desktop_file_id()); -+ if (!app) -+ app = new Shell.App({ app_info: appInfo }); - if (appInfo.should_show()) { - let menu_id = dir.get_menu_id(); - this.applicationsByCategory[categoryId].push(app); --- -2.7.4 - diff --git a/SOURCES/0001-apps-menu-add-logo-icon-to-Applications-menu.patch b/SOURCES/0001-apps-menu-add-logo-icon-to-Applications-menu.patch index 83d3ccf..98728ad 100644 --- a/SOURCES/0001-apps-menu-add-logo-icon-to-Applications-menu.patch +++ b/SOURCES/0001-apps-menu-add-logo-icon-to-Applications-menu.patch @@ -1,15 +1,30 @@ -From 6d8c4482fe9f423dceb9671958ed388778ff62a8 Mon Sep 17 00:00:00 2001 +From f1a607062817a981099349139055618542ea1f05 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Tue, 21 Jan 2014 16:48:17 -0500 Subject: [PATCH] apps-menu: add logo icon to Applications menu Brand requested it. --- + data/gnome-classic.css | 3 +++ extensions/apps-menu/extension.js | 6 ++++++ - 1 file changed, 6 insertions(+) + 2 files changed, 9 insertions(+) +diff --git a/data/gnome-classic.css b/data/gnome-classic.css +index 29a4132..f1a004d 100644 +--- a/data/gnome-classic.css ++++ b/data/gnome-classic.css +@@ -686,6 +686,9 @@ StScrollBar { + -st-icon-style: symbolic; + margin-left: 4px; + margin-right: 4px; } ++ #panel .panel-button .panel-logo-icon { ++ padding-right: .4em; ++ icon-size: 1em; } + #panel .panel-button:hover { + color: #454f52; } + #panel .panel-button:active, #panel .panel-button:overview, #panel .panel-button:focus, #panel .panel-button:checked { diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js -index f8438fd..2d198c1 100644 +index 4792854..8e7afef 100644 --- a/extensions/apps-menu/extension.js +++ b/extensions/apps-menu/extension.js @@ -1,6 +1,7 @@ @@ -20,7 +35,7 @@ index f8438fd..2d198c1 100644 const GMenu = imports.gi.GMenu; const Lang = imports.lang; const Shell = imports.gi.Shell; -@@ -280,6 +281,11 @@ const ApplicationsButton = new Lang.Class({ +@@ -263,6 +264,11 @@ const ApplicationsButton = new Lang.Class({ let hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' }); @@ -33,5 +48,5 @@ index f8438fd..2d198c1 100644 y_expand: true, y_align: Clutter.ActorAlign.CENTER }); -- -2.3.3 +2.12.0 diff --git a/SOURCES/0001-classic-shade-panel-in-overview.patch b/SOURCES/0001-classic-shade-panel-in-overview.patch index 7a3b282..4256b72 100644 --- a/SOURCES/0001-classic-shade-panel-in-overview.patch +++ b/SOURCES/0001-classic-shade-panel-in-overview.patch @@ -1,4 +1,4 @@ -From c1e6ea231d866f7e23740a5264defef13af8003a Mon Sep 17 00:00:00 2001 +From c391bd5c645398f2978a9dc2755fd076a97ea340 Mon Sep 17 00:00:00 2001 From: Jakub Steiner Date: Tue, 14 Jan 2014 17:00:23 +0100 Subject: [PATCH] classic: shade panel in overview @@ -7,52 +7,29 @@ Subject: [PATCH] classic: shade panel in overview base the overview panel on the classic grey and "darken" for overview --- - data/gnome-classic.css | 14 ++++++++------ - 1 file changed, 8 insertions(+), 6 deletions(-) + data/gnome-classic.css | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/data/gnome-classic.css b/data/gnome-classic.css -index d77fe30..5ff2125 100644 +index ee4ec52..29a4132 100644 --- a/data/gnome-classic.css +++ b/data/gnome-classic.css -@@ -6,7 +6,7 @@ - */ - - #panel { -- background-color: #e9e9e9 !important; -+ background-color: #e9e9e9; - background-gradient-direction: vertical; - background-gradient-end: #d0d0d0; - border-top-color: #666; /* we don't support non-uniform border-colors and -@@ -21,10 +21,8 @@ - } - +@@ -1865,12 +1865,10 @@ StScrollBar { + border-bottom: 1px solid #666; + app-icon-bottom-clip: 0px; } #panel:overview { -- background-color: #000 !important; -- background-gradient-end: #000 !important; +- background-color: #000; +- background-gradient-end: #000; - border-top-color: #000; -- border-bottom: 1px solid #000 !important; -+ background-color: #a9a9a9; -+ background-gradient-end: #797979; - } - - #panel.lock-screen { -@@ -50,11 +48,15 @@ - } - - .panel-button { -- color: #555 !important; -+ color: #555; - -natural-hpadding: 6px !important; - -minimum-hpadding: 3px !important; - } - -+ #panel:overview .panel-button { -+ color: #333; -+ } -+ - #panel:overview .panel-button, - #panel.lock-screen .panel-button, - #panel.unlock-screen .panel-button { +- border-bottom: 1px solid #000; } ++ background-color: #e0e0e0; ++ background-gradient-end: #d4d4d4; } + #panel:overview .panel-button { +- color: #fff; } ++ color: #222728; } + #panel .panel-button { + -natural-hpadding: 8px; + -minimum-hpadding: 4px; -- -1.8.4.2 +2.12.0 diff --git a/SOURCES/0001-loginDialog-make-info-messages-themed.patch b/SOURCES/0001-loginDialog-make-info-messages-themed.patch new file mode 100644 index 0000000..6e54c8f --- /dev/null +++ b/SOURCES/0001-loginDialog-make-info-messages-themed.patch @@ -0,0 +1,86 @@ +From a382e21acd24a1002fbcc160aa7591ff99b191e6 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 26 Jun 2017 14:44:06 -0400 +Subject: [PATCH] loginDialog: make info messages themed + +They were lacking a definition before leading them to +show up invisible. +--- + data/gnome-classic.css | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/data/gnome-classic.css b/data/gnome-classic.css +index f1a004d..30c7fca 100644 +--- a/data/gnome-classic.css ++++ b/data/gnome-classic.css +@@ -1599,63 +1599,65 @@ StScrollBar { + box-shadow: inset 0 1px #5d696d; + text-shadow: 0 1px black; + icon-shadow: 0 1px black; } + .login-dialog .modal-dialog-button:default:active { + color: white; + border-color: rgba(0, 0, 0, 0.7); + background-color: #3583d5; + box-shadow: inset 0 0 black; + text-shadow: none; + icon-shadow: none; } + .login-dialog .modal-dialog-button:default:insensitive { + color: #8e9192; + border-color: rgba(0, 0, 0, 0.7); + background-color: rgba(65, 71, 72, 0.7); + box-shadow: none; + text-shadow: none; + icon-shadow: none; } + + .login-dialog-logo-bin { + padding: 24px 0px; } + + .login-dialog-banner { + color: #d6d6d1; } + + .login-dialog-button-box { + spacing: 5px; } + + .login-dialog-message-warning { + color: #f57900; } + +-.login-dialog-message-hint { ++.login-dialog-message-hint, .login-dialog-message { ++ color: #bebeb6; + padding-top: 0; +- padding-bottom: 20px; } ++ padding-bottom: 20px; ++ min-height: 2.75em; } + + .login-dialog-user-selection-box { + padding: 100px 0px; } + .login-dialog-user-selection-box .login-dialog-not-listed-label { + padding-left: 2px; } + .login-dialog-not-listed-button:focus .login-dialog-user-selection-box .login-dialog-not-listed-label, .login-dialog-not-listed-button:hover .login-dialog-user-selection-box .login-dialog-not-listed-label { + color: #eeeeec; } + + .login-dialog-not-listed-label { + font-size: 90%; + font-weight: bold; + color: #a6a69b; + padding-top: 1em; } + + .login-dialog-user-list-view { + -st-vfade-offset: 1em; } + + .login-dialog-user-list { + spacing: 12px; + padding: .2em; + width: 23em; } + .login-dialog-user-list:expanded .login-dialog-user-list-item:focus { + background-color: #4a90d9; + color: #ffffff; } + .login-dialog-user-list:expanded .login-dialog-user-list-item:logged-in { + border-right: 2px solid #4a90d9; } + + .login-dialog-user-list-item { + border-radius: 5px; + padding: .2em; +-- +2.13.0 + diff --git a/SOURCES/0001-window-list-Don-t-consider-skip-taskbar-windows-for-.patch b/SOURCES/0001-window-list-Don-t-consider-skip-taskbar-windows-for-.patch deleted file mode 100644 index 9a6c4db..0000000 --- a/SOURCES/0001-window-list-Don-t-consider-skip-taskbar-windows-for-.patch +++ /dev/null @@ -1,33 +0,0 @@ -From cda4c58cfa094c44fe939f050ef1113fdd88c8ba Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Florian=20M=C3=BCllner?= -Date: Thu, 30 Jul 2015 14:45:58 +0200 -Subject: [PATCH] window-list: Don't consider skip-taskbar windows for app - sorting - -It is odd to consider windows that are not shown in the window list -for app sorting, in particular when switching between grouped and -ungrouped mode, and when a long-lived window like the DESKTOP is -present. - -https://bugzilla.gnome.org/show_bug.cgi?id=753055 ---- - extensions/window-list/extension.js | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js -index 28feda6..f37c850 100644 ---- a/extensions/window-list/extension.js -+++ b/extensions/window-list/extension.js -@@ -58,7 +58,8 @@ function _onMenuStateChanged(menu, isOpen) { - } - - function _getAppStableSequence(app) { -- return app.get_windows().reduce(function(prev, cur) { -+ let windows = app.get_windows().filter(function(w) { return !w.skip_taskbar; }); -+ return windows.reduce(function(prev, cur) { - return Math.min(prev, cur.get_stable_sequence()); - }, Infinity); - } --- -2.4.3 - diff --git a/SOURCES/0001-window-list-Hide-workspace-indicator-when-there-s-1-.patch b/SOURCES/0001-window-list-Hide-workspace-indicator-when-there-s-1-.patch new file mode 100644 index 0000000..c5c6bbb --- /dev/null +++ b/SOURCES/0001-window-list-Hide-workspace-indicator-when-there-s-1-.patch @@ -0,0 +1,91 @@ +From 8cb224b951f7271bcc73ae96a5d397ddc888a49c Mon Sep 17 00:00:00 2001 +From: Felipe Borges +Date: Fri, 20 Jan 2017 15:10:11 +0100 +Subject: [PATCH] window-list: Hide workspace indicator when there's 1 + workspace + +There's no need to show the workspace indicator at the right +corner of the window-list if there's just a single workspace +AND the workspace creation is static. This saves us a bit more +of space. + +https://bugzilla.gnome.org/show_bug.cgi?id=777504 +--- + extensions/window-list/extension.js | 25 +++++++++++++++++++++---- + 1 file changed, 21 insertions(+), 4 deletions(-) + +diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js +index 15db297..4d1f83c 100644 +--- a/extensions/window-list/extension.js ++++ b/extensions/window-list/extension.js +@@ -813,10 +813,15 @@ const WindowList = new Lang.Class({ + this._workspaceIndicator = new WorkspaceIndicator(); + indicatorsBox.add(this._workspaceIndicator.container, { expand: false, y_fill: true }); + ++ this._mutterSettings = new Gio.Settings({ schema_id: 'org.gnome.mutter' }); + this._workspaceSettings = this._getWorkspaceSettings(); + this._workspacesOnlyOnPrimaryChangedId = + this._workspaceSettings.connect('changed::workspaces-only-on-primary', + Lang.bind(this, this._updateWorkspaceIndicatorVisibility)); ++ this._dynamicWorkspacesSettings = this._getDynamicWorkspacesSettings(); ++ this._dynamicWorkspacesChangedId = ++ this._dynamicWorkspacesSettings.connect('changed::dynamic-workspaces', ++ Lang.bind(this, this._updateWorkspaceIndicatorVisibility)); + this._updateWorkspaceIndicatorVisibility(); + + this._menuManager = new PopupMenu.PopupMenuManager(this); +@@ -898,11 +903,17 @@ const WindowList = new Lang.Class({ + this._groupingModeChanged(); + }, + ++ _getDynamicWorkspacesSettings: function() { ++ if (this._workspaceSettings.list_keys().indexOf('dynamic-workspaces') > -1) ++ return this._workspaceSettings; ++ return this._mutterSettings; ++ }, ++ + _getWorkspaceSettings: function() { + let settings = global.get_overrides_settings(); + if (settings.list_keys().indexOf('workspaces-only-on-primary') > -1) + return settings; +- return new Gio.Settings({ schema_id: 'org.gnome.mutter' }); ++ return this._mutterSettings; + }, + + _onScrollEvent: function(actor, event) { +@@ -936,9 +947,12 @@ const WindowList = new Lang.Class({ + }, + + _updateWorkspaceIndicatorVisibility: function() { +- this._workspaceIndicator.actor.visible = +- this._monitor == Main.layoutManager.primaryMonitor || +- !this._workspaceSettings.get_boolean('workspaces-only-on-primary'); ++ let hasWorkspaces = this._dynamicWorkspacesSettings.get_boolean('dynamic-workspaces') || ++ global.screen.n_workspaces > 1; ++ let workspacesOnMonitor = this._monitor == Main.layoutManager.primaryMonitor || ++ !this._workspaceSettings.get_boolean('workspaces-only-on-primary'); ++ ++ this._workspaceIndicator.actor.visible = hasWorkspaces && workspacesOnMonitor; + }, + + _getPreferredUngroupedWindowListWidth: function() { +@@ -1109,6 +1123,8 @@ const WindowList = new Lang.Class({ + Lang.bind(this, this._onWindowRemoved)); + this._workspaceSignals.set(workspace, signals); + } ++ ++ this._updateWorkspaceIndicatorVisibility(); + }, + + _disconnectWorkspaceSignals: function() { +@@ -1177,6 +1193,7 @@ const WindowList = new Lang.Class({ + + _onDestroy: function() { + this._workspaceSettings.disconnect(this._workspacesOnlyOnPrimaryChangedId); ++ this._dynamicWorkspacesSettings.disconnect(this._dynamicWorkspacesChangedId); + + this._workspaceIndicator.destroy(); + +-- +2.12.0 + diff --git a/SOURCES/0001-workspace-indicator-Use-consistent-workspace-numberi.patch b/SOURCES/0001-workspace-indicator-Use-consistent-workspace-numberi.patch deleted file mode 100644 index 91bc42a..0000000 --- a/SOURCES/0001-workspace-indicator-Use-consistent-workspace-numberi.patch +++ /dev/null @@ -1,30 +0,0 @@ -From d44d9983cc506df397baeb595109e215578bebbc Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Florian=20M=C3=BCllner?= -Date: Fri, 31 Jul 2015 16:37:48 +0200 -Subject: [PATCH] workspace-indicator: Use consistent workspace numbering - -The indicator numbers workspaces starting from 1, while newly added -workspace names in the preference dialog start counting at 0. -Change the latter to be consistent with the indicator. - -https://bugzilla.gnome.org/show_bug.cgi?id=753105 ---- - extensions/workspace-indicator/prefs.js | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/extensions/workspace-indicator/prefs.js b/extensions/workspace-indicator/prefs.js -index 49378c4..e5dbebe 100644 ---- a/extensions/workspace-indicator/prefs.js -+++ b/extensions/workspace-indicator/prefs.js -@@ -195,7 +195,7 @@ const WorkspaceSettingsWidget = new GObject.Class({ - let iter = this._store.append(); - let index = this._store.get_path(iter).get_indices()[0]; - -- let label = _("Workspace %d").format(index); -+ let label = _("Workspace %d").format(index + 1); - this._store.set(iter, [this._store.Columns.LABEL], [label]); - }, - --- -2.4.3 - diff --git a/SOURCES/add-extra-extensions.patch b/SOURCES/add-extra-extensions.patch index 05c1d8e..c5d0617 100644 --- a/SOURCES/add-extra-extensions.patch +++ b/SOURCES/add-extra-extensions.patch @@ -1,4 +1,4 @@ -From 3743256a1975c911f07043f20e1ff861ad14b7b3 Mon Sep 17 00:00:00 2001 +From 2c81d3faaff49f5e4595869cffeccfaa3d5eb980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Wed, 20 May 2015 17:44:50 +0200 Subject: [PATCH 1/4] Add top-icons extension @@ -16,31 +16,31 @@ Subject: [PATCH 1/4] Add top-icons extension create mode 100644 extensions/top-icons/stylesheet.css diff --git a/configure.ac b/configure.ac -index 6b95674..90b54da 100644 +index c5a8c45..3b7aca9 100644 --- a/configure.ac +++ b/configure.ac -@@ -29,7 +29,7 @@ AC_SUBST([SHELL_VERSION]) +@@ -31,7 +31,7 @@ AC_SUBST([SHELL_VERSION]) dnl keep this in alphabetic order CLASSIC_EXTENSIONS="apps-menu places-menu alternate-tab launch-new-instance window-list" DEFAULT_EXTENSIONS="$CLASSIC_EXTENSIONS drive-menu screenshot-window-sizer windowsNavigator workspace-indicator" --ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows example native-window-placement systemMonitor user-theme" -+ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows example native-window-placement systemMonitor top-icons user-theme" +-ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows example native-window-placement user-theme" ++ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows example native-window-placement top-icons user-theme" AC_SUBST(CLASSIC_EXTENSIONS, [$CLASSIC_EXTENSIONS]) AC_SUBST(ALL_EXTENSIONS, [$ALL_EXTENSIONS]) AC_ARG_ENABLE([extensions], -@@ -66,7 +66,7 @@ for e in $enable_extensions; do - [AC_MSG_WARN([libgtop-2.0 not found, disabling systemMonitor])]) - ;; +@@ -63,7 +63,7 @@ ENABLED_EXTENSIONS= + for e in $enable_extensions; do + case $e in dnl keep this in alphabetic order - alternate-tab|apps-menu|auto-move-windows|drive-menu|example|launch-new-instance|native-window-placement|places-menu|screenshot-window-sizer|user-theme|window-list|windowsNavigator|workspace-indicator) + alternate-tab|apps-menu|auto-move-windows|drive-menu|example|launch-new-instance|native-window-placement|places-menu|screenshot-window-sizer|top-icons|user-theme|window-list|windowsNavigator|workspace-indicator) ENABLED_EXTENSIONS="$ENABLED_EXTENSIONS $e" ;; *) -@@ -89,6 +89,7 @@ AC_CONFIG_FILES([ +@@ -87,6 +87,7 @@ AC_CONFIG_FILES([ + extensions/native-window-placement/Makefile extensions/places-menu/Makefile extensions/screenshot-window-sizer/Makefile - extensions/systemMonitor/Makefile + extensions/top-icons/Makefile extensions/user-theme/Makefile extensions/window-list/Makefile @@ -276,97 +276,75 @@ index 0000000..25134b6 @@ -0,0 +1 @@ +/* This extensions requires no special styling */ -- -2.7.2 +2.13.0 -From 5ae70e47ef5417e556b1c5ec5ecc456f3e3d8e38 Mon Sep 17 00:00:00 2001 +From 774167b7d8a0f413c8ffce2979bc69e9e1e28466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Wed, 20 May 2015 18:05:41 +0200 Subject: [PATCH 2/4] Add dash-to-dock extension --- configure.ac | 5 +- - extensions/dash-to-dock/Makefile.am | 32 + - extensions/dash-to-dock/Settings.ui | 1782 ++++++++++++++++++++ - extensions/dash-to-dock/dockedDash.js | 1684 ++++++++++++++++++ - extensions/dash-to-dock/extension.js | 52 + - extensions/dash-to-dock/intellihide.js | 278 +++ - extensions/dash-to-dock/media/four.svg | 132 ++ - extensions/dash-to-dock/media/four_bottom.svg | 133 ++ - extensions/dash-to-dock/media/four_rtl.svg | 133 ++ - extensions/dash-to-dock/media/four_top.svg | 133 ++ - extensions/dash-to-dock/media/logo.svg | 561 ++++++ - extensions/dash-to-dock/media/one.svg | 107 ++ - extensions/dash-to-dock/media/one_bottom.svg | 108 ++ - extensions/dash-to-dock/media/one_rtl.svg | 108 ++ - extensions/dash-to-dock/media/one_top.svg | 108 ++ - extensions/dash-to-dock/media/three.svg | 121 ++ - extensions/dash-to-dock/media/three_bottom.svg | 122 ++ - extensions/dash-to-dock/media/three_rtl.svg | 122 ++ - extensions/dash-to-dock/media/three_top.svg | 122 ++ - extensions/dash-to-dock/media/two.svg | 118 ++ - extensions/dash-to-dock/media/two_bottom.svg | 119 ++ - extensions/dash-to-dock/media/two_rtl.svg | 119 ++ - extensions/dash-to-dock/media/two_top.svg | 119 ++ + extensions/dash-to-dock/Makefile.am | 21 + + extensions/dash-to-dock/Settings.ui | 2998 ++++++++++++++++++++ + extensions/dash-to-dock/appIcons.js | 1256 ++++++++ + extensions/dash-to-dock/convenience.js | 74 + + extensions/dash-to-dock/dash.js | 1180 ++++++++ + extensions/dash-to-dock/dockedDash.js | 1684 +++++++++++ + extensions/dash-to-dock/docking.js | 1909 +++++++++++++ + extensions/dash-to-dock/extension.js | 21 + + extensions/dash-to-dock/intellihide.js | 323 +++ + extensions/dash-to-dock/media/logo.svg | 528 ++++ extensions/dash-to-dock/metadata.json.in | 12 + - extensions/dash-to-dock/myConvenience.js | 103 ++ - extensions/dash-to-dock/myDash.js | 1672 ++++++++++++++++++ - ...me.shell.extensions.dash-to-dock.gschema.xml.in | 159 ++ - extensions/dash-to-dock/prefs.js | 367 ++++ - extensions/dash-to-dock/stylesheet.css | 181 ++ - 29 files changed, 8810 insertions(+), 2 deletions(-) + ...gnome.shell.extensions.dash-to-dock.gschema.xml | 713 +++++ + extensions/dash-to-dock/prefs.js | 705 +++++ + extensions/dash-to-dock/stylesheet.css | 109 + + extensions/dash-to-dock/theming.js | 293 ++ + extensions/dash-to-dock/utils.js | 123 + + extensions/dash-to-dock/windowPreview.js | 595 ++++ + 18 files changed, 12547 insertions(+), 2 deletions(-) create mode 100644 extensions/dash-to-dock/Makefile.am create mode 100644 extensions/dash-to-dock/Settings.ui + create mode 100644 extensions/dash-to-dock/appIcons.js + create mode 100644 extensions/dash-to-dock/convenience.js + create mode 100644 extensions/dash-to-dock/dash.js create mode 100644 extensions/dash-to-dock/dockedDash.js + create mode 100644 extensions/dash-to-dock/docking.js create mode 100644 extensions/dash-to-dock/extension.js create mode 100644 extensions/dash-to-dock/intellihide.js - create mode 100644 extensions/dash-to-dock/media/four.svg - create mode 100644 extensions/dash-to-dock/media/four_bottom.svg - create mode 100644 extensions/dash-to-dock/media/four_rtl.svg - create mode 100644 extensions/dash-to-dock/media/four_top.svg create mode 100644 extensions/dash-to-dock/media/logo.svg - create mode 100644 extensions/dash-to-dock/media/one.svg - create mode 100644 extensions/dash-to-dock/media/one_bottom.svg - create mode 100644 extensions/dash-to-dock/media/one_rtl.svg - create mode 100644 extensions/dash-to-dock/media/one_top.svg - create mode 100644 extensions/dash-to-dock/media/three.svg - create mode 100644 extensions/dash-to-dock/media/three_bottom.svg - create mode 100644 extensions/dash-to-dock/media/three_rtl.svg - create mode 100644 extensions/dash-to-dock/media/three_top.svg - create mode 100644 extensions/dash-to-dock/media/two.svg - create mode 100644 extensions/dash-to-dock/media/two_bottom.svg - create mode 100644 extensions/dash-to-dock/media/two_rtl.svg - create mode 100644 extensions/dash-to-dock/media/two_top.svg create mode 100644 extensions/dash-to-dock/metadata.json.in - create mode 100644 extensions/dash-to-dock/myConvenience.js - create mode 100644 extensions/dash-to-dock/myDash.js - create mode 100644 extensions/dash-to-dock/org.gnome.shell.extensions.dash-to-dock.gschema.xml.in + create mode 100644 extensions/dash-to-dock/org.gnome.shell.extensions.dash-to-dock.gschema.xml create mode 100644 extensions/dash-to-dock/prefs.js create mode 100644 extensions/dash-to-dock/stylesheet.css + create mode 100644 extensions/dash-to-dock/theming.js + create mode 100644 extensions/dash-to-dock/utils.js + create mode 100644 extensions/dash-to-dock/windowPreview.js diff --git a/configure.ac b/configure.ac -index 90b54da..d40b9e0 100644 +index 3b7aca9..cc7cd29 100644 --- a/configure.ac +++ b/configure.ac -@@ -29,7 +29,7 @@ AC_SUBST([SHELL_VERSION]) +@@ -31,7 +31,7 @@ AC_SUBST([SHELL_VERSION]) dnl keep this in alphabetic order CLASSIC_EXTENSIONS="apps-menu places-menu alternate-tab launch-new-instance window-list" DEFAULT_EXTENSIONS="$CLASSIC_EXTENSIONS drive-menu screenshot-window-sizer windowsNavigator workspace-indicator" --ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows example native-window-placement systemMonitor top-icons user-theme" -+ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows dash-to-dock example native-window-placement systemMonitor top-icons user-theme" +-ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows example native-window-placement top-icons user-theme" ++ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows dash-to-dock example native-window-placement top-icons user-theme" AC_SUBST(CLASSIC_EXTENSIONS, [$CLASSIC_EXTENSIONS]) AC_SUBST(ALL_EXTENSIONS, [$ALL_EXTENSIONS]) AC_ARG_ENABLE([extensions], -@@ -66,7 +66,7 @@ for e in $enable_extensions; do - [AC_MSG_WARN([libgtop-2.0 not found, disabling systemMonitor])]) - ;; +@@ -63,7 +63,7 @@ ENABLED_EXTENSIONS= + for e in $enable_extensions; do + case $e in dnl keep this in alphabetic order - alternate-tab|apps-menu|auto-move-windows|drive-menu|example|launch-new-instance|native-window-placement|places-menu|screenshot-window-sizer|top-icons|user-theme|window-list|windowsNavigator|workspace-indicator) + alternate-tab|apps-menu|auto-move-windows|dash-to-dock|drive-menu|example|launch-new-instance|native-window-placement|places-menu|screenshot-window-sizer|top-icons|user-theme|window-list|windowsNavigator|workspace-indicator) ENABLED_EXTENSIONS="$ENABLED_EXTENSIONS $e" ;; *) -@@ -82,6 +82,7 @@ AC_CONFIG_FILES([ +@@ -81,6 +81,7 @@ AC_CONFIG_FILES([ extensions/alternate-tab/Makefile extensions/apps-menu/Makefile extensions/auto-move-windows/Makefile @@ -376,48 +354,37 @@ index 90b54da..d40b9e0 100644 extensions/launch-new-instance/Makefile diff --git a/extensions/dash-to-dock/Makefile.am b/extensions/dash-to-dock/Makefile.am new file mode 100644 -index 0000000..5e08acf +index 0000000..0189a7b --- /dev/null +++ b/extensions/dash-to-dock/Makefile.am -@@ -0,0 +1,32 @@ +@@ -0,0 +1,21 @@ +EXTENSION_ID = dash-to-dock + +EXTRA_MODULES = \ ++ appIcons.js \ ++ convenience.js \ ++ dash.js \ + dockedDash.js \ ++ docking.js \ + intellihide.js \ -+ myConvenience.js \ -+ myDash.js \ + prefs.js \ -+ Settings.ui ++ Settings.ui \ ++ theming.js \ ++ utils.js \ ++ windowPreview.js + +include ../../extension.mk +include ../../settings.mk + +mediadir = $(extensiondir)/media +dist_media_DATA = \ -+ media/four_bottom.svg \ -+ media/four_rtl.svg \ -+ media/four.svg \ -+ media/four_top.svg \ -+ media/logo.svg \ -+ media/one_bottom.svg \ -+ media/one_rtl.svg \ -+ media/one.svg \ -+ media/one_top.svg \ -+ media/three_bottom.svg \ -+ media/three_rtl.svg \ -+ media/three.svg \ -+ media/three_top.svg \ -+ media/two_bottom.svg \ -+ media/two_rtl.svg \ -+ media/two.svg \ -+ media/two_top.svg ++ media/logo.svg diff --git a/extensions/dash-to-dock/Settings.ui b/extensions/dash-to-dock/Settings.ui new file mode 100644 -index 0000000..e283588 +index 0000000..d8d498f --- /dev/null +++ b/extensions/dash-to-dock/Settings.ui -@@ -0,0 +1,1782 @@ +@@ -0,0 +1,2998 @@ + + + @@ -427,6 +394,11 @@ index 0000000..e283588 + 0.050000000000000003 + 0.25 + ++ ++ 10 ++ 0.250000000000000003 ++ 1 ++ + + 1 + 0.01 @@ -438,108 +410,119 @@ index 0000000..e283588 + 0.01 + 0.10000000000000001 + -+ -+ 1 -+ 0.050000000000000003 -+ 0.25 -+ -+ -+ 16 -+ 128 ++ ++ 10 + 1 -+ 10 ++ 5 + -+ ++ + True -+ True -+ 6 -+ 6 -+ 6 -+ 6 ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ vertical + -+ ++ + True + False -+ 24 -+ 24 -+ 24 -+ 24 -+ vertical -+ 24 ++ 0 ++ in + -+ ++ + True + False -+ 0 -+ in ++ none + -+ ++ ++ 100 ++ 80 + True -+ False -+ none ++ True + -+ ++ + True -+ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ vertical ++ 12 + -+ ++ + True + False -+ 12 -+ 12 -+ 12 -+ 12 + 32 + -+ ++ + True -+ False -+ True -+ 0 -+ Show the dock on ++ True + + -+ 0 ++ 1 + 0 + + + -+ ++ + True + False -+ center -+ ++ True ++ Customize indicator style ++ fill ++ 0 + + -+ 1 ++ 0 + 0 + + + ++ ++ False ++ True ++ 0 ++ + -+ -+ -+ -+ -+ 100 -+ True -+ True + -+ ++ + True + False -+ 12 -+ 12 -+ 12 -+ 12 -+ 32 ++ 1 ++ vertical ++ 12 + -+ ++ + True + False -+ True -+ 0 -+ Position on screen ++ 32 ++ ++ ++ True ++ False ++ Color ++ 0 ++ ++ ++ True ++ True ++ 0 ++ ++ ++ ++ ++ True ++ True ++ True ++ ++ ++ False ++ True ++ 1 ++ ++ + + + False @@ -548,39 +531,28 @@ index 0000000..e283588 + + + -+ ++ + True + False + 32 + -+ -+ Left ++ + True -+ True -+ False -+ end -+ center ++ False ++ Border color + 0 -+ True -+ + + -+ False ++ True + True + 0 + + + -+ -+ Bottom ++ + True + True -+ False -+ center -+ 0 -+ True -+ position_left_button -+ ++ True + + + False @@ -588,520 +560,624 @@ index 0000000..e283588 + 1 + + ++ ++ ++ False ++ True ++ 1 ++ ++ ++ ++ ++ True ++ False ++ 32 + -+ -+ Top ++ + True -+ True -+ False -+ center ++ False ++ Border width + 0 -+ bottom -+ True -+ position_left_button -+ + + -+ False ++ True + True -+ 2 ++ 0 + + + -+ -+ Right ++ + True + True -+ False -+ center -+ 0 -+ True -+ position_left_button -+ ++ dot_border_width_adjustment + + + False + True -+ 3 ++ 1 + + + + + False + True -+ 1 ++ 2 + + + ++ ++ False ++ True ++ 1 ++ + + + + + -+ -+ -+ + -+ -+ False -+ True -+ 0 -+ + ++ ++ ++ ++ ++ ++ False ++ True ++ 0 ++ ++ ++ ++ ++ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ vertical ++ ++ ++ True ++ False ++ 0 ++ in + -+ ++ + True + False -+ 0 -+ in ++ none + -+ ++ ++ 100 ++ 80 + True -+ False -+ none ++ True + -+ -+ 100 -+ 80 ++ + True -+ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ 32 + -+ ++ ++ True ++ True ++ end ++ center ++ ++ ++ 1 ++ 0 ++ 2 ++ ++ ++ ++ + True + False -+ 12 -+ 12 -+ 12 -+ 12 -+ 32 -+ -+ -+ True -+ False -+ True -+ 0 -+ Hide the dock when it obstructs a window of the the current application. More refined settings are available. -+ True -+ -+ -+ -+ 0 -+ 1 -+ -+ -+ -+ -+ True -+ False -+ True -+ 0 -+ Intelligent autohide -+ -+ -+ 0 -+ 0 -+ -+ -+ -+ -+ True -+ False -+ 6 -+ -+ -+ True -+ True -+ True -+ center -+ center -+ 0.46000000834465027 -+ -+ -+ True -+ False -+ emblem-system-symbolic -+ -+ -+ -+ -+ -+ False -+ True -+ 0 -+ -+ -+ -+ -+ True -+ True -+ end -+ center -+ -+ -+ False -+ True -+ 1 -+ -+ -+ -+ -+ 1 -+ 0 -+ 2 -+ -+ -+ -+ -+ Insensitive Message Tray -+ True -+ True -+ False -+ 12 -+ 0 -+ True -+ -+ -+ 0 -+ 2 -+ 2 -+ -+ ++ True ++ 0 ++ Number overlay ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ True ++ False ++ 0 ++ Temporarily show the application numbers over the icons, corresponding to the shortcut. ++ True ++ 40 ++ + ++ ++ 0 ++ 1 ++ + + + + + -+ -+ -+ -+ -+ -+ False -+ True -+ 1 -+ -+ -+ -+ -+ True -+ False -+ 0 -+ in + -+ ++ ++ 100 ++ 80 + True -+ False -+ none ++ True + -+ -+ 100 -+ 80 ++ + True -+ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ 32 + -+ ++ ++ True ++ True ++ end ++ center ++ ++ ++ 1 ++ 0 ++ 2 ++ ++ ++ ++ + True + False -+ 12 -+ 12 -+ 12 -+ 12 -+ 32 -+ -+ -+ True -+ False -+ 0 -+ Dock size limit -+ -+ -+ 0 -+ 0 -+ -+ -+ -+ -+ True -+ True -+ baseline -+ True -+ dock_size_adjustment -+ 0 -+ 2 -+ right -+ -+ -+ -+ -+ 1 -+ 0 -+ -+ -+ -+ -+ Panel mode: extend to the screen edge -+ True -+ True -+ False -+ 12 -+ 0 -+ True -+ -+ -+ 0 -+ 1 -+ 2 -+ -+ ++ True ++ 0 ++ Show the dock if it is hidden + ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ True ++ False ++ 0 ++ If using autohide, the dock will appear for a short time when triggering the shortcut. ++ True ++ 40 ++ ++ ++ ++ 0 ++ 1 ++ + + + ++ ++ ++ ++ ++ 100 ++ 80 ++ True ++ True + -+ ++ + True -+ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ 32 + -+ ++ ++ 12 ++ center ++ ++ ++ 1 ++ 0 ++ ++ ++ ++ + True + False -+ 12 -+ 12 -+ 12 -+ 12 -+ 32 -+ -+ -+ True -+ False -+ 0 -+ Icon size limit -+ -+ -+ 0 -+ 0 -+ -+ -+ -+ -+ True -+ True -+ baseline -+ True -+ icon_size_adjustment -+ 1 -+ 0 -+ right -+ -+ -+ -+ -+ 1 -+ 0 -+ -+ -+ -+ -+ Fixed icon size: scroll to reveal other icons -+ True -+ True -+ False -+ 12 -+ 0 -+ True -+ -+ -+ 0 -+ 1 -+ 2 -+ -+ ++ True ++ 0 ++ Shortcut for the options above ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ True ++ False ++ 0 ++ 40 ++ Syntax: <Shift>, <Ctrl>, <Alt>, <Super> ++ True ++ + ++ ++ 0 ++ 1 ++ + + + + + -+ -+ ++ ++ ++ True ++ True ++ ++ ++ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ True ++ 6 ++ 32 ++ ++ ++ True ++ True ++ end ++ shortcut_time_adjustment ++ 3 ++ ++ ++ 1 ++ 0 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Hide timeout (s) ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ + + -+ -+ False -+ True -+ 2 -+ + -+ -+ -+ -+ -+ True -+ False -+ Position and size ++ ++ ++ + + -+ False ++ False ++ True ++ 0 + + ++ ++ ++ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ vertical + -+ ++ + True + False -+ 24 -+ 24 -+ 24 -+ 24 -+ vertical -+ 24 ++ 0 ++ in + -+ ++ + True + False -+ 0 -+ in ++ none + -+ ++ ++ 100 ++ 80 + True -+ False -+ none ++ True + -+ ++ + True -+ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ 32 + -+ ++ + True + False -+ 12 -+ 12 -+ 12 -+ 12 -+ 32 -+ -+ -+ True -+ True -+ end -+ center -+ -+ -+ 1 -+ 0 -+ -+ -+ -+ -+ True -+ False -+ True -+ 0 -+ Show running applications -+ -+ -+ 0 -+ 0 -+ -+ ++ True ++ 0 ++ 40 ++ When set to minimize, double clicking minimizes all the windows of the application. ++ True ++ + ++ ++ 0 ++ 1 ++ + -+ -+ -+ -+ -+ True -+ True + -+ ++ + True + False -+ 12 -+ 12 -+ 12 -+ 12 -+ 32 -+ -+ -+ True -+ True -+ end -+ center -+ -+ -+ 1 -+ 0 -+ 2 -+ -+ -+ -+ -+ True -+ False -+ True -+ 0 -+ Move the applications button at the beginning of the dock. -+ True -+ -+ -+ -+ 0 -+ 1 -+ -+ -+ -+ -+ True -+ False -+ True -+ 0 -+ <i>Show Applications</i> icon first -+ True -+ -+ -+ 0 -+ 0 -+ -+ ++ True ++ 0 ++ Shift+Click action ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ True ++ False ++ center ++ ++ Raise window ++ Minimize window ++ Launch new instance ++ Cycle through windows ++ Minimize or overview ++ Show window previews ++ Quit ++ + ++ ++ 1 ++ 0 ++ 2 ++ + + + + + -+ -+ -+ -+ -+ -+ False -+ True -+ 0 -+ ++ ++ ++ 100 ++ 80 ++ True ++ True ++ ++ ++ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ 32 ++ ++ ++ True ++ False ++ True ++ 0 ++ 40 ++ Behavior for Middle-Click. ++ True ++ ++ ++ ++ 0 ++ 1 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Middle-Click action ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ True ++ False ++ center ++ ++ Raise window ++ Minimize window ++ Launch new instance ++ Cycle through windows ++ Minimize or overview ++ Show window previews ++ Quit ++ ++ ++ ++ 1 ++ 0 ++ 2 ++ ++ ++ ++ ++ ++ ++ ++ ++ 100 ++ 80 ++ True ++ True ++ ++ ++ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ 32 ++ ++ ++ True ++ False ++ True ++ 0 ++ 40 ++ Behavior for Shift+Middle-Click. ++ True ++ ++ ++ ++ 0 ++ 1 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Shift+Middle-Click action ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ True ++ False ++ center ++ ++ Raise window ++ Minimize window ++ Launch new instance ++ Cycle through windows ++ Minimize or overview ++ Show window previews ++ Quit ++ ++ ++ ++ 1 ++ 0 ++ 2 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + ++ ++ ++ False ++ True ++ 0 ++ ++ ++ ++ ++ 1 ++ 0.050000000000000003 ++ 0.25 ++ ++ ++ 16 ++ 128 ++ 1 ++ 10 ++ ++ ++ True ++ True ++ 6 ++ 6 ++ 6 ++ 6 ++ ++ ++ True ++ False ++ 24 ++ 24 ++ 24 ++ 24 ++ vertical ++ 24 + -+ ++ + True + False + 0 + in + -+ ++ + True + False + none + -+ ++ + True + True + -+ ++ + True + False + 12 @@ -1110,51 +1186,44 @@ index 0000000..e283588 + 12 + 32 + -+ ++ + True + False + True + 0 -+ Behaviour when clicking on the icon of a running application. -+ True -+ ++ Show the dock on + + + 0 -+ 1 ++ 0 + + + -+ ++ + True + False -+ True -+ 0 -+ Click action ++ center ++ + + -+ 0 ++ 1 + 0 + + + -+ ++ ++ Show on all monitors. + True -+ False -+ center -+ -+ Do nothing -+ Minimize -+ Launch new instance -+ Cycle through windows -+ ++ True ++ False ++ 12 ++ 0 ++ True + + -+ 1 -+ 0 -+ 2 ++ 0 ++ 2 ++ 2 + + + @@ -1162,148 +1231,116 @@ index 0000000..e283588 + + + -+ ++ + 100 -+ 80 + True + True + -+ ++ + True + False + 12 + 12 + 12 + 12 -+ 32 -+ -+ -+ True -+ False -+ True -+ 0 -+ When set to minimize, double clicking minimizes all the windows of the application. -+ True -+ -+ -+ -+ 0 -+ 1 -+ -+ ++ 32 + -+ ++ + True + False + True + 0 -+ Shift+Click action ++ Position on screen + + -+ 0 -+ 0 ++ False ++ True ++ 0 + + + -+ ++ + True + False -+ center -+ -+ Do nothing -+ Minimize window -+ -+ -+ -+ 1 -+ 0 -+ 2 -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ False -+ True -+ 1 -+ -+ -+ -+ -+ True -+ False -+ 0 -+ in -+ -+ -+ True -+ False -+ none -+ -+ -+ True -+ True -+ -+ -+ True -+ False -+ 12 -+ 12 -+ 12 -+ 12 -+ 32 -+ -+ -+ True -+ False -+ True -+ 0 -+ The area of the dock close to the screen edge and the <i>Show Applications</i> icon are active. -+ True -+ True -+ -+ -+ -+ 0 -+ 1 -+ -+ -+ -+ -+ True -+ False -+ True -+ 0 -+ Switch workspace by scrolling on the dock -+ -+ -+ 0 -+ 0 -+ -+ -+ -+ -+ True -+ True -+ end -+ center ++ 32 ++ ++ ++ Left ++ True ++ True ++ False ++ end ++ center ++ 0 ++ True ++ ++ ++ ++ False ++ True ++ 0 ++ ++ ++ ++ ++ Bottom ++ True ++ True ++ False ++ center ++ 0 ++ True ++ position_left_button ++ ++ ++ ++ False ++ True ++ 1 ++ ++ ++ ++ ++ Top ++ True ++ True ++ False ++ center ++ 0 ++ bottom ++ True ++ position_left_button ++ ++ ++ ++ False ++ True ++ 2 ++ ++ ++ ++ ++ Right ++ True ++ True ++ False ++ center ++ 0 ++ True ++ position_left_button ++ ++ ++ ++ False ++ True ++ 3 ++ ++ + + -+ 1 -+ 0 -+ 2 ++ False ++ True ++ 1 + + + @@ -1319,52 +1356,28 @@ index 0000000..e283588 + + False + True -+ 2 ++ 0 + + -+ -+ -+ 1 -+ -+ -+ -+ -+ True -+ False -+ Behavior -+ -+ -+ 1 -+ False -+ -+ -+ -+ -+ True -+ False -+ 24 -+ 24 -+ 24 -+ 24 -+ vertical -+ 24 + -+ ++ + True + False + 0 + in + -+ ++ + True -+ True ++ False + none + -+ ++ ++ 100 ++ 80 + True + True + -+ ++ + True + False + 12 @@ -1373,12 +1386,12 @@ index 0000000..e283588 + 12 + 32 + -+ ++ + True + False + True + 0 -+ Few customizations meant to integrate the dock wih the default GNOME theme. Alternatively, specific options can be enabled below. ++ Hide the dock when it obstructs a window of the the current application. More refined settings are available. + True + ++ ++ ++ False ++ True ++ 0 ++ ++ ++ ++ ++ True ++ True ++ end ++ center ++ ++ ++ False ++ True ++ 1 ++ ++ + + + 1 @@ -1428,26 +1478,28 @@ index 0000000..e283588 + + False + True -+ 0 ++ 1 + + + -+ ++ + True + False + 0 + in + -+ ++ + True + False + none + -+ ++ ++ 100 ++ 80 + True + True + -+ ++ + True + False + 12 @@ -1456,45 +1508,49 @@ index 0000000..e283588 + 12 + 32 + -+ ++ + True -+ True -+ end -+ center ++ False ++ 0 ++ Dock size limit + + -+ 1 ++ 0 + 0 -+ 2 + + + -+ ++ + True -+ False ++ True ++ baseline + True -+ 0 -+ Save space reducing padding and border radius. -+ ++ dock_size_adjustment ++ 0 ++ 2 ++ right ++ ++ + + -+ 0 -+ 1 ++ 1 ++ 0 + + + -+ ++ ++ Panel mode: extend to the screen edge + True -+ False -+ True ++ True ++ False ++ 12 + 0 -+ Shrink the dash ++ True + + + 0 -+ 0 ++ 1 ++ 2 + + + @@ -1502,11 +1558,11 @@ index 0000000..e283588 + + + -+ ++ + True + True + -+ ++ + True + False + 12 @@ -1515,164 +1571,49 @@ index 0000000..e283588 + 12 + 32 + -+ -+ True -+ True -+ end -+ center -+ -+ -+ 1 -+ 0 -+ 2 -+ -+ -+ -+ ++ + True + False -+ True + 0 -+ Show a dot for each windows of the application. -+ ++ Icon size limit + + + 0 -+ 1 ++ 0 + + + -+ ++ + True -+ False ++ True ++ baseline + True -+ 0 -+ Show windows counter indicators ++ icon_size_adjustment ++ 1 ++ 0 ++ right ++ ++ + + -+ 0 ++ 1 + 0 + + -+ -+ -+ -+ -+ -+ -+ True -+ True -+ -+ -+ True -+ False -+ vertical -+ -+ -+ True -+ False -+ 12 -+ 12 -+ 12 -+ 12 -+ 32 -+ -+ -+ True -+ True -+ end -+ center -+ -+ -+ 1 -+ 0 -+ 2 -+ -+ -+ -+ -+ True -+ False -+ True -+ 0 -+ Tune the dash background opacity. -+ -+ -+ -+ 0 -+ 1 -+ -+ -+ -+ -+ True -+ False -+ True -+ 0 -+ Customize opacity -+ -+ -+ 0 -+ 0 -+ -+ -+ -+ -+ False -+ True -+ 0 -+ -+ + -+ ++ ++ Fixed icon size: scroll to reveal other icons + True -+ False -+ 12 -+ 12 ++ True ++ False + 12 -+ 12 -+ 32 -+ -+ -+ True -+ False -+ Opacity -+ -+ -+ False -+ True -+ 0 -+ -+ -+ -+ -+ True -+ True -+ custom_opacity_adjustement -+ on -+ False -+ 0 -+ 0 -+ 2 -+ right -+ -+ -+ -+ -+ True -+ True -+ 1 -+ -+ ++ 0 ++ True + + -+ False -+ True -+ 1 ++ 0 ++ 1 ++ 2 + + + @@ -1688,7672 +1629,11380 @@ index 0000000..e283588 + + False + True -+ 1 ++ 2 + + + -+ -+ 2 -+ + + -+ ++ + True + False -+ Appearance ++ Position and size + + -+ 2 + False + + + -+ ++ ++ True + False ++ 24 ++ 24 + 24 + 24 -+ True -+ True + vertical -+ 5 -+ -+ -+ -+ False -+ True -+ 10 -+ 0 -+ -+ -+ -+ -+ True -+ False -+ <b>Dash to Dock</b> -+ True -+ -+ -+ False -+ True -+ 1 -+ -+ ++ 24 + -+ ++ + True + False -+ center -+ -+ -+ True -+ False -+ end -+ version: -+ -+ -+ False -+ True -+ 0 -+ -+ ++ 0 ++ in + -+ ++ + True + False -+ start -+ ... -+ -+ -+ False -+ True -+ 1 -+ -+ -+ -+ -+ False -+ True -+ 2 -+ -+ -+ -+ -+ True -+ False -+ Moves the dash out of the overview transforming it in a dock -+ center -+ True -+ -+ -+ False -+ True -+ 3 -+ -+ -+ -+ -+ True -+ False -+ center -+ 5 -+ -+ -+ True -+ False -+ Created by -+ -+ -+ False -+ True -+ 0 -+ -+ -+ -+ -+ True -+ True -+ Michele (<a href="mailto:micxgx@gmail.com">micxgx@gmail.com</a>) -+ True -+ -+ -+ False -+ True -+ 1 -+ -+ -+ -+ -+ False -+ True -+ 4 -+ -+ -+ -+ -+ Webpage -+ True -+ True -+ True -+ -+ center -+ none -+ https://micheleg.github.io/dash-to-dock/ -+ -+ -+ False -+ True -+ 5 -+ -+ -+ -+ -+ True -+ True -+ end -+ <span size="small">This program comes with ABSOLUTELY NO WARRANTY. -+See the <a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GNU General Public License, version 2 or later</a> for details.</span> -+ True -+ center -+ True -+ -+ -+ True -+ True -+ 6 -+ -+ -+ -+ -+ 3 -+ -+ -+ -+ -+ True -+ False -+ About -+ -+ -+ 3 -+ False -+ -+ -+ -+ -+ 1000 -+ 50 -+ 250 -+ -+ -+ 1 -+ 0.050000000000000003 -+ 0.25 -+ -+ -+ True -+ False -+ 12 -+ 12 -+ 12 -+ 12 -+ vertical -+ -+ -+ True -+ False -+ 0 -+ in -+ -+ -+ True -+ False -+ none -+ -+ -+ True -+ True ++ none + -+ ++ + True -+ False -+ 12 -+ 12 -+ 12 -+ 12 -+ 32 -+ -+ -+ True -+ False -+ True -+ 0 -+ Show the dock by mouse hover on the screen edge. -+ True -+ -+ -+ -+ 0 -+ 1 -+ -+ ++ True + -+ ++ + True + False -+ True -+ 0 -+ Autohide -+ -+ -+ 0 -+ 0 -+ -+ -+ -+ -+ True -+ True -+ end -+ center -+ -+ -+ 1 -+ 0 -+ 2 -+ -+ -+ -+ -+ Push to show: require pressure to show the dock -+ True -+ True -+ False ++ 12 ++ 12 + 12 -+ 0 -+ True ++ 12 ++ 32 ++ ++ ++ True ++ True ++ end ++ center ++ ++ ++ 1 ++ 0 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Show favorite applications ++ ++ ++ 0 ++ 0 ++ ++ + -+ -+ 0 -+ 2 -+ 2 -+ + + + -+ -+ -+ -+ -+ True -+ True + -+ ++ + True -+ False -+ 12 -+ 12 -+ 12 -+ 12 -+ 32 -+ -+ -+ True -+ False -+ True -+ 0 -+ Show the dock when it doesn't obstruct application windows. -+ True -+ -+ -+ -+ 0 -+ 1 -+ -+ ++ True + -+ ++ + True + False -+ True -+ 0 -+ Dodge windows -+ -+ -+ 0 -+ 0 -+ -+ -+ -+ -+ True -+ True -+ end -+ center ++ 12 ++ 12 ++ 12 ++ 12 ++ 32 ++ ++ ++ True ++ True ++ end ++ center ++ ++ ++ 1 ++ 0 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Show running applications ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ Isolate workspaces. ++ True ++ True ++ False ++ 12 ++ 0 ++ True ++ ++ ++ 0 ++ 2 ++ 2 ++ ++ ++ ++ ++ Isolate monitors. ++ True ++ True ++ False ++ 12 ++ 0 ++ True ++ ++ ++ 0 ++ 3 ++ 2 ++ ++ ++ ++ ++ True ++ True ++ False ++ 3 ++ 0 ++ True ++ ++ ++ True ++ False ++ Show open windows previews. ++ True ++ ++ ++ ++ ++ 0 ++ 1 ++ 2 ++ ++ + -+ -+ 1 -+ 0 -+ 2 -+ + ++ ++ ++ ++ ++ True ++ True + -+ -+ Only consider windows of the focused application ++ + True -+ True -+ False ++ False ++ 12 ++ 12 + 12 -+ 0 -+ True ++ 12 ++ 32 ++ ++ ++ True ++ True ++ end ++ center ++ ++ ++ 1 ++ 0 ++ 2 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ If disabled, these settings are accessible from gnome-tweak-tool or the extension website. ++ True ++ ++ ++ ++ 0 ++ 1 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Show <i>Applications</i> icon ++ True ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ Move the applications button at the beginning of the dock. ++ True ++ True ++ False ++ 12 ++ 0 ++ True ++ ++ ++ 0 ++ 2 ++ 2 ++ ++ ++ ++ ++ True ++ True ++ False ++ 3 ++ 0 ++ 0.43000000715255737 ++ True ++ ++ ++ True ++ False ++ Animate <i>Show Applications</i>. ++ True ++ ++ ++ ++ ++ 0 ++ 3 ++ 2 ++ ++ + -+ -+ 0 -+ 2 -+ 2 -+ + + + + + ++ ++ ++ ++ ++ ++ False ++ True ++ 0 ++ ++ ++ ++ ++ 1 ++ ++ ++ ++ ++ True ++ False ++ Launchers ++ ++ ++ 1 ++ False ++ ++ ++ ++ ++ True ++ False ++ 24 ++ 24 ++ 24 ++ 24 ++ vertical ++ 24 ++ ++ ++ True ++ False ++ 0 ++ in + -+ ++ + True -+ True ++ False ++ none + -+ ++ + True -+ False -+ 12 -+ 12 -+ 12 -+ 12 -+ True -+ 6 -+ 32 -+ -+ -+ True -+ True -+ end -+ animation_time_adjustment -+ 3 -+ -+ -+ 1 -+ 0 -+ -+ ++ True + -+ ++ + True + False -+ True -+ 0 -+ Animation duration (s) -+ -+ -+ 0 -+ 0 -+ -+ -+ -+ -+ True -+ True -+ end -+ hide_timeout_adjustment -+ 3 -+ -+ -+ 1 -+ 1 -+ -+ -+ -+ -+ True -+ True -+ end -+ show_timeout_adjustment -+ 3 -+ -+ -+ 1 -+ 2 -+ -+ -+ -+ -+ True -+ True -+ 0.000 -+ pressure_threshold_adjustment -+ -+ -+ 1 -+ 3 -+ -+ -+ -+ -+ True -+ False -+ True -+ 0 -+ Hide timeout (s) -+ -+ -+ 0 -+ 1 -+ -+ -+ -+ -+ True -+ False -+ True -+ 0 -+ Show timeout (s) ++ 12 ++ 12 ++ 12 ++ 12 ++ 32 ++ ++ ++ True ++ False ++ True ++ 0 ++ Enable Super+(0-9) as shortcuts to activate apps. It can also be used together with Shift and Ctrl. ++ True ++ True ++ ++ ++ ++ 0 ++ 1 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Use keyboard shortcuts to activate apps ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ True ++ False ++ 6 ++ ++ ++ True ++ True ++ True ++ center ++ center ++ 0.46000000834465027 ++ ++ ++ True ++ False ++ emblem-system-symbolic ++ ++ ++ ++ ++ ++ False ++ True ++ 0 ++ ++ ++ ++ ++ True ++ True ++ end ++ center ++ ++ ++ ++ ++ 1 ++ 0 ++ 2 ++ ++ + -+ -+ 0 -+ 2 -+ + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ False ++ True ++ 1 ++ ++ ++ ++ ++ True ++ False ++ 0 ++ in ++ ++ ++ True ++ False ++ none ++ ++ ++ True ++ True + -+ ++ + True + False -+ True -+ 0 -+ Pressure threshold ++ 12 ++ 12 ++ 12 ++ 12 ++ 32 ++ ++ ++ True ++ False ++ True ++ 0 ++ Behaviour when clicking on the icon of a running application. ++ True ++ ++ ++ ++ 0 ++ 1 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Click action ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ True ++ False ++ 6 ++ ++ ++ True ++ True ++ True ++ center ++ ++ ++ True ++ False ++ emblem-system-symbolic ++ ++ ++ ++ ++ ++ ++ ++ True ++ False ++ center ++ ++ Raise window ++ Minimize ++ Launch new instance ++ Cycle through windows ++ Minimize or overview ++ Show window previews ++ ++ ++ ++ ++ ++ 2 ++ ++ + -+ -+ 0 -+ 3 -+ + + + + + ++ ++ ++ + ++ ++ False ++ True ++ 2 ++ + -+ -+ -+ -+ -+ -+ False -+ True -+ 0 -+ -+ -+ -+ -diff --git a/extensions/dash-to-dock/dockedDash.js b/extensions/dash-to-dock/dockedDash.js -new file mode 100644 -index 0000000..1e145c6 ---- /dev/null -+++ b/extensions/dash-to-dock/dockedDash.js -@@ -0,0 +1,1684 @@ -+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- -+ -+const Clutter = imports.gi.Clutter; -+const GLib = imports.gi.GLib; -+const Gtk = imports.gi.Gtk; -+const Lang = imports.lang; -+const Meta = imports.gi.Meta; -+const Shell = imports.gi.Shell; -+const St = imports.gi.St; -+const Mainloop = imports.mainloop; -+const Params = imports.misc.params; -+ -+const Main = imports.ui.main; -+const Dash = imports.ui.dash; -+const IconGrid = imports.ui.iconGrid; -+const MessageTray = imports.ui.messageTray; -+const Overview = imports.ui.overview; -+const OverviewControls = imports.ui.overviewControls; -+const PointerWatcher = imports.ui.pointerWatcher; -+const Tweener = imports.ui.tweener; -+const Signals = imports.signals; -+const ViewSelector = imports.ui.viewSelector; -+const WorkspaceSwitcherPopup= imports.ui.workspaceSwitcherPopup; -+const Layout = imports.ui.layout; -+const LayoutManager = imports.ui.main.layoutManager; -+ -+const Me = imports.misc.extensionUtils.getCurrentExtension(); -+const Convenience = Me.imports.myConvenience; -+const Intellihide = Me.imports.intellihide; -+const MyDash = Me.imports.myDash; -+ -+const DOCK_DWELL_CHECK_INTERVAL = 100; //TODO -+ -+const State = { -+ HIDDEN: 0, -+ SHOWING: 1, -+ SHOWN: 2, -+ HIDING: 3 -+}; -+ -+/* Return the actual position reverseing left and right in rtl */ -+function getPosition(settings) { -+ let position = settings.get_enum('dock-position'); -+ if(Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) { -+ if (position == St.Side.LEFT) -+ position = St.Side.RIGHT; -+ else if (position == St.Side.RIGHT) -+ position = St.Side.LEFT; -+ } -+ return position; -+} -+ -+/* -+ * A simple St.Widget with one child whose allocation takes into account the -+ * slide out of its child via the _slidex parameter ([0:1]). -+ * -+ * Required since I want to track the input region of this container which is -+ * based on its allocation even if the child overlows the parent actor. By doing -+ * this the region of the dash that is slideout is not steling anymore the input -+ * regions making the extesion usable when the primary monitor is the right one. -+ * -+ * The slidex parameter can be used to directly animate the sliding. The parent -+ * must have a WEST (SOUTH) anchor_point to achieve the sliding to the RIGHT (BOTTOM) -+ * side. -+ * -+ * It can't be an extended object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973. -+ * thus use the Shell.GenericContainer pattern. -+*/ -+ -+const DashSlideContainer = new Lang.Class({ -+ Name: 'DashSlideContainer', -+ -+ _init: function(params) { -+ -+ /* Default local params */ -+ let localDefaults = { -+ side: St.Side.LEFT, -+ initialSlideValue: 1 -+ } -+ -+ let localParams = Params.parse(params, localDefaults, true); -+ -+ if (params){ -+ /* Remove local params before passing the params to the parent -+ constructor to avoid errors. */ -+ let prop; -+ for (prop in localDefaults) { -+ if ((prop in params)) -+ delete params[prop]; -+ } -+ } -+ -+ this.actor = new Shell.GenericContainer(params); -+ this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); -+ this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); -+ this.actor.connect('allocate', Lang.bind(this, this._allocate)); -+ -+ this.actor._delegate = this; -+ -+ this._child = null; -+ -+ // slide parameter: 1 = visible, 0 = hidden. -+ this._slidex = localParams.initialSlideValue; -+ this._side = localParams.side; -+ this._slideoutSize = 0; // minimum size when slided out -+ }, -+ -+ -+ _allocate: function(actor, box, flags) { -+ -+ if (this._child == null) -+ return; -+ -+ let availWidth = box.x2 - box.x1; -+ let availHeight = box.y2 - box.y1; -+ let [minChildWidth, minChildHeight, natChildWidth, natChildHeight] = -+ this._child.get_preferred_size(); -+ -+ let childWidth = natChildWidth; -+ let childHeight = natChildHeight; -+ -+ let childBox = new Clutter.ActorBox(); -+ -+ let slideoutSize = this._slideoutSize; -+ -+ if (this._side == St.Side.LEFT) { -+ childBox.x1 = (this._slidex -1)*(childWidth - slideoutSize); -+ childBox.x2 = slideoutSize + this._slidex*(childWidth - slideoutSize); -+ childBox.y1 = 0; -+ childBox.y2 = childBox.y1 + childHeight; -+ } else if (this._side == St.Side.RIGHT -+ || this._side == St.Side.BOTTOM) { -+ childBox.x1 = 0; -+ childBox.x2 = childWidth; -+ childBox.y1 = 0; -+ childBox.y2 = childBox.y1 + childHeight; -+ } else if (this._side == St.Side.TOP) { -+ childBox.x1 = 0; -+ childBox.x2 = childWidth; -+ childBox.y1 = (this._slidex -1)*(childHeight - slideoutSize); -+ childBox.y2 = slideoutSize + this._slidex*(childHeight - slideoutSize); -+ } -+ -+ this._child.allocate(childBox, flags); -+ this._child.set_clip(-childBox.x1, -childBox.y1, -+ -childBox.x1+availWidth,-childBox.y1 + availHeight); -+ }, -+ -+ /* Just the child width but taking into account the slided out part */ -+ _getPreferredWidth: function(actor, forHeight, alloc) { -+ let [minWidth, natWidth ] = this._child.get_preferred_width(forHeight); -+ if (this._side == St.Side.LEFT -+ || this._side == St.Side.RIGHT) { -+ minWidth = (minWidth - this._slideoutSize)*this._slidex + this._slideoutSize; -+ natWidth = (natWidth - this._slideoutSize)*this._slidex + this._slideoutSize; -+ } -+ -+ alloc.min_size = minWidth; -+ alloc.natural_size = natWidth; -+ }, -+ -+ /* Just the child height but taking into account the slided out part */ -+ _getPreferredHeight: function(actor, forWidth, alloc) { -+ let [minHeight, natHeight] = this._child.get_preferred_height(forWidth); -+ if (this._side == St.Side.TOP -+ || this._side == St.Side.BOTTOM) { -+ minHeight = (minHeight - this._slideoutSize)*this._slidex + this._slideoutSize; -+ natHeight = (natHeight - this._slideoutSize)*this._slidex + this._slideoutSize; -+ } -+ alloc.min_size = minHeight; -+ alloc.natural_size = natHeight; -+ }, -+ -+ /* I was expecting it to be a virtual function... stil I don't understand -+ how things work. -+ */ -+ add_child: function(actor) { -+ -+ /* I'm supposed to have only on child */ -+ if(this._child !== null) { -+ this.actor.remove_child(actor); -+ } -+ -+ this._child = actor; -+ this.actor.add_child(actor); -+ }, -+ -+ set slidex(value) { -+ this._slidex = value; -+ this._child.queue_relayout(); -+ }, -+ -+ get slidex() { -+ return this._slidex; -+ } -+ -+}); -+ -+const dockedDash = new Lang.Class({ -+ Name: 'dockedDash', -+ -+ _init: function(settings) { -+ -+ this._rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL; -+ -+ // Load settings -+ this._settings = settings; -+ this._bindSettingsChanges(); -+ -+ this._position = getPosition(settings); -+ this._isHorizontal = ( this._position == St.Side.TOP || -+ this._position == St.Side.BOTTOM ); -+ -+ // Temporary ignore hover events linked to autohide for whatever reason -+ this._ignoreHover = false; -+ this._oldignoreHover = null; -+ // This variables are linked to the settings regardles of autohide or intellihide -+ // being temporary disable. Get set by _updateVisibilityMode; -+ this._autohideIsEnabled = null; -+ this._intellihideIsEnabled = null; -+ this._fixedIsEnabled = null; -+ -+ // Create intellihide object to monitor windows overlapping -+ this._intellihide = new Intellihide.intellihide(this._settings); -+ -+ // initialize dock state -+ this._dockState = State.HIDDEN; -+ -+ /* status variable: true when the overview is shown through the dash -+ * applications button. -+ */ -+ this.forcedOverview = false; -+ -+ // Put dock on the primary monitor -+ this._monitor = Main.layoutManager.primaryMonitor; -+ -+ // this store size and the position where the dash is shown; -+ // used by intellihide module to check window overlap. -+ this.staticBox = new Clutter.ActorBox(); -+ -+ // Initialize pressure barrier variables -+ this._canUsePressure = false; -+ this._pressureBarrier = null; -+ this._barrier = null; -+ this._messageTrayShowing = false; -+ this._removeBarrierTimeoutId = 0; -+ -+ // Initialize dwelling system variables -+ this._dockDwelling = false; -+ this._dockWatch = null; -+ this._dockDwellUserTime = 0; -+ this._dockDwellTimeoutId = 0 -+ -+ // Create a new dash object -+ this.dash = new MyDash.myDash(this._settings); -+ -+ // set stored icon size to the new dash -+ Main.overview.dashIconSize = this.dash.iconSize; -+ -+ // connect app icon into the view selector -+ this.dash.showAppsButton.connect('notify::checked', Lang.bind(this, this._onShowAppsButtonToggled)); -+ -+ // Create the main actor and the containers for sliding in and out and -+ // centering, turn on track hover -+ -+ let positionStyleClass = ['top', 'right', 'bottom', 'left']; -+ // This is the centering actor -+ this.actor = new St.Bin({ name: 'dashtodockContainer',reactive: false, -+ style_class:positionStyleClass[this._position], -+ x_align: this._isHorizontal?St.Align.MIDDLE:St.Align.START, -+ y_align: this._isHorizontal?St.Align.START:St.Align.MIDDLE}); -+ this.actor._delegate = this; -+ -+ // This is the sliding actor whose allocation is to be tracked for input regions -+ this._slider = new DashSlideContainer({side: this._position, initialSlideValue: 0}); -+ -+ // This is the actor whose hover status us tracked for autohide -+ this._box = new St.BoxLayout({ name: 'dashtodockBox', reactive: true, track_hover:true } ); -+ this._box.connect("notify::hover", Lang.bind(this, this._hoverChanged)); -+ -+ // Create and apply height constraint to the dash. It's controlled by this.actor height -+ this.constrainSize = new Clutter.BindConstraint({ source: this.actor, -+ coordinate: this._isHorizontal?Clutter.BindCoordinate.WIDTH:Clutter.BindCoordinate.HEIGHT }); -+ this.dash.actor.add_constraint(this.constrainSize); -+ -+ // Connect global signals -+ this._signalsHandler = new Convenience.GlobalSignalsHandler(); -+ this._signalsHandler.add( -+ [ -+ Main.overview, -+ 'item-drag-begin', -+ Lang.bind(this, this._onDragStart) -+ ], -+ [ -+ Main.overview, -+ 'item-drag-end', -+ Lang.bind(this, this._onDragEnd) -+ ], -+ [ -+ Main.overview, -+ 'item-drag-cancelled', -+ Lang.bind(this, this._onDragEnd) -+ ], -+ // update wne monitor changes, for instance in multimonitor when monitor are attached -+ [ -+ global.screen, -+ 'monitors-changed', -+ Lang.bind(this, this._resetPosition ) -+ ], -+ [ -+ Main.overview, -+ 'showing', -+ Lang.bind(this, this._onOverviewShowing) -+ ], -+ [ -+ Main.overview, -+ 'hiding', -+ Lang.bind(this, this._onOverviewHiding) -+ ], -+ // Hide on appview -+ [ -+ Main.overview.viewSelector, -+ 'page-changed', -+ Lang.bind(this, this._pageChanged) -+ ], -+ [ -+ Main.overview.viewSelector, -+ 'page-empty', -+ Lang.bind(this, this._onPageEmpty) -+ ], -+ // Ensure the ShowAppsButton status is kept in sync -+ [ -+ Main.overview.viewSelector._showAppsButton, -+ 'notify::checked', -+ Lang.bind(this, this._syncShowAppsButtonToggled) -+ ], -+ [ -+ Main.messageTray, -+ 'showing', -+ Lang.bind(this, this._onMessageTrayShowing) -+ ], -+ [ -+ Main.messageTray, -+ 'hiding', -+ Lang.bind(this, this._onMessageTrayHiding) -+ ], -+ // Monitor windows overlapping -+ [ -+ this._intellihide, -+ 'status-changed', -+ Lang.bind(this, this._updateDashVisibility) -+ ], -+ // Keep dragged icon consistent in size with this dash -+ [ -+ this.dash, -+ 'icon-size-changed', -+ Lang.bind(this, function() { -+ Main.overview.dashIconSize = this.dash.iconSize; -+ }) -+ ], -+ // This duplicate the similar signal which is in owerview.js. -+ // Being connected and thus executed later this effectively -+ // overwrite any attempt to use the size of the default dash -+ //which given the customization is usually much smaller. -+ // I can't easily disconnect the original signal -+ [ -+ Main.overview._controls.dash, -+ 'icon-size-changed', -+ Lang.bind(this, function() { -+ Main.overview.dashIconSize = this.dash.iconSize; -+ }) -+ ] -+ ); -+ -+ this._injectionsHandler = new Convenience.InjectionsHandler(); -+ this._themeManager = new themeManager(this._settings, this.actor, this.dash); -+ -+ // Since the actor is not a topLevel child and its parent is now not added to the Chrome, -+ // the allocation change of the parent container (slide in and slideout) doesn't trigger -+ // anymore an update of the input regions. Force the update manually. -+ this.actor.connect('notify::allocation', -+ Lang.bind(Main.layoutManager, Main.layoutManager._queueUpdateRegions)); -+ -+ this.dash._container.connect('allocation-changed', Lang.bind(this, this._updateStaticBox)); -+ this._slider.actor.connect(this._isHorizontal?'notify::x':'notify::y', Lang.bind(this, this._updateStaticBox)); -+ -+ // sync hover after a popupmenu is closed -+ this.dash.connect('menu-closed', Lang.bind(this, function(){this._box.sync_hover();})); -+ -+ // Restore dash accessibility -+ Main.ctrlAltTabManager.addGroup( -+ this.dash.actor, _("Dash"),'user-bookmarks-symbolic', -+ {focusCallback: Lang.bind(this, this._onAccessibilityFocus)}); -+ -+ // Load optional features -+ this._optionalScrollWorkspaceSwitch(); -+ -+ // Delay operations that require the shell to be fully loaded and with -+ // user theme applied. -+ -+ this._paintId = this.actor.connect("paint", Lang.bind(this, this._initialize)); -+ -+ // Hide usual Dash -+ Main.overview._controls.dash.actor.hide(); -+ -+ // Also set dash width to 1, so it's almost not taken into account by code -+ // calculaing the reserved space in the overview. The reason to keep it at 1 is -+ // to allow its visibility change to trigger an allocaion of the appGrid which -+ // in turn is triggergin the appsIcon spring animation, required when no other -+ // actors has this effect, i.e in horizontal mode and without the workspaceThumnails -+ // 1 static workspace only) -+ Main.overview._controls.dash.actor.set_width(1); -+ -+ // Manage the which is used to reserve space in the overview for the dock -+ // Add and additional dashSpacer positioned according to the dash positioning. -+ // It gets restored on extension unload. -+ this._dashSpacer = new OverviewControls.DashSpacer(); -+ this._dashSpacer.setDashActor(this._box); -+ -+ // shift overview messageIndicator for bottom dock -+ this._oldMessageIndicatorPosition = Main.overview._controls._indicator.actor.get_first_child().y; -+ if (this._position == St.Side.BOTTOM) { -+ this._dashSpacer.connect('notify::height', Lang.bind(this, function(){ -+ Main.overview._controls._indicator.actor.get_first_child().y = -this._dashSpacer.height; -+ })); -+ } -+ -+ if (this._position == St.Side.LEFT) -+ Main.overview._controls._group.insert_child_at_index(this._dashSpacer, this._rtl?-1:0); // insert on first -+ else if (this._position == St.Side.RIGHT) -+ Main.overview._controls._group.insert_child_at_index(this._dashSpacer, this._rtl?0:-1); // insert on last -+ else if (this._position == St.Side.TOP) -+ Main.overview._overview.insert_child_at_index(this._dashSpacer, 0); -+ else if (this._position == St.Side.BOTTOM) -+ Main.overview._overview.insert_child_at_index(this._dashSpacer, -1); -+ -+ // Add dash container actor and the container to the Chrome. -+ this.actor.set_child(this._slider.actor); -+ this._slider.add_child(this._box); -+ this._box.add_actor(this.dash.actor); -+ -+ // Add aligning container without tracking it for input region (old affectsinputRegion: false that was removed). -+ // The public method trackChrome requires the actor to be child of a tracked actor. Since I don't want the parent -+ // to be tracked I use the private internal _trackActor instead. -+ Main.uiGroup.add_child(this.actor); -+ Main.layoutManager._trackActor(this._slider.actor, {trackFullscreen: true}); -+ -+ // Keep the dash below the modalDialogGroup -+ Main.layoutManager.uiGroup.set_child_below_sibling(this.actor,Main.layoutManager.modalDialogGroup); -+ -+ if ( this._settings.get_boolean('dock-fixed') ) -+ Main.layoutManager._trackActor(this.dash.actor, {affectsStruts: true}); -+ -+ // pretend this._slider is isToplevel child so that fullscreen is actually tracked -+ let index = Main.layoutManager._findActor(this._slider.actor); -+ Main.layoutManager._trackedActors[index].isToplevel = true ; -+ -+ // Set initial position -+ this._resetPosition(); -+ -+ }, -+ -+ _initialize: function(){ -+ -+ if(this._paintId>0){ -+ this.actor.disconnect(this._paintId); -+ this._paintId=0; -+ } -+ -+ this.dash.setIconSize(this._settings.get_int('dash-max-icon-size'), true); -+ -+ // Apply custome css class according to the settings -+ this._themeManager.updateCustomTheme(); -+ -+ // Since Gnome 3.8 dragging an app without having opened the overview before cause the attemp to -+ //animate a null target since some variables are not initialized when the viewSelector is created -+ if(Main.overview.viewSelector._activePage == null) -+ Main.overview.viewSelector._activePage = Main.overview.viewSelector._workspacesPage; -+ -+ this._updateVisibilityMode(); -+ -+ // Setup pressure barrier (GS38+ only) -+ this._updatePressureBarrier(); -+ this._updateBarrier(); -+ -+ // setup dwelling system if pressure barriers are not available -+ this._setupDockDwellIfNeeded(); -+ -+ // Insensitive Message Tray -+ this._updateInsensitiveTray(); -+ }, -+ -+ destroy: function(){ -+ -+ // Disconnect global signals -+ this._signalsHandler.destroy(); -+ // The dash and intellihide have global signals as well internally -+ this.dash.destroy(); -+ this._intellihide.destroy(); -+ -+ this._injectionsHandler.destroy(); -+ -+ // Destroy main clutter actor: this should be sufficient removing it and -+ // destroying all its children -+ this.actor.destroy(); -+ -+ // Remove barrier timeout -+ if (this._removeBarrierTimeoutId > 0) -+ Mainloop.source_remove(this._removeBarrierTimeoutId); -+ -+ // Remove existing barrier -+ this._removeBarrier(); -+ -+ // Remove pointer watcher -+ if(this._dockWatch){ -+ PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch); -+ this._dockWatch = null; -+ } -+ -+ // Remove the dashSpacer -+ this._dashSpacer.destroy(); -+ -+ // restore messageIndicator position -+ Main.overview._controls._indicator.actor.get_first_child().y = this._oldMessageIndicatorPosition; -+ -+ // Reshow normal dash previously hidden, restore panel position if changed. -+ Main.overview._controls.dash.actor.show(); -+ Main.overview._controls.dash.actor.set_width(-1); //reset default dash size -+ // This force the recalculation of the icon size -+ Main.overview._controls.dash._maxHeight = -1; -+ -+ // reset stored icon size to the default dash -+ Main.overview.dashIconSize = Main.overview._controls.dash.iconSize; -+ // Reshow panel corners -+ this._revertPanelCorners(); -+ }, -+ -+ _bindSettingsChanges: function() { -+ -+ this._settings.connect('changed::scroll-switch-workspace', Lang.bind(this, function(){ -+ this._optionalScrollWorkspaceSwitch(this._settings.get_boolean('scroll-switch-workspace')); -+ })); -+ -+ this._settings.connect('changed::dash-max-icon-size', Lang.bind(this, function(){ -+ this.dash.setIconSize(this._settings.get_int('dash-max-icon-size')); -+ })); -+ -+ this._settings.connect('changed::icon-size-fixed', Lang.bind(this, function(){ -+ this.dash.setIconSize(this._settings.get_int('dash-max-icon-size')); -+ })); -+ -+ this._settings.connect('changed::show-running', Lang.bind(this, function(){ -+ this.dash.resetAppIcons(); -+ })); -+ -+ this._settings.connect('changed::show-apps-at-top', Lang.bind(this, function(){ -+ this.dash.resetAppIcons(); -+ })); -+ -+ this._settings.connect('changed::dock-fixed', Lang.bind(this, function(){ -+ -+ if(this._settings.get_boolean('dock-fixed')) { -+ Main.layoutManager._trackActor(this.dash.actor, {affectsStruts: true}); -+ } else { -+ Main.layoutManager._untrackActor(this.dash.actor); -+ } -+ -+ this._resetPosition(); -+ -+ // Add or remove barrier depending on if dock-fixed -+ this._updateBarrier(); -+ -+ this._updateVisibilityMode(); -+ })); -+ -+ this._settings.connect('changed::intellihide', Lang.bind(this, this._updateVisibilityMode)); -+ -+ this._settings.connect('changed::intellihide-perapp', Lang.bind(this, function(){ -+ this._intellihide.forceUpdate(); -+ })); -+ -+ this._settings.connect('changed::autohide', Lang.bind(this, function(){ -+ this._updateVisibilityMode(); -+ this._updateBarrier(); -+ })); -+ this._settings.connect('changed::extend-height', Lang.bind(this,this._resetPosition)); -+ this._settings.connect('changed::preferred-monitor', Lang.bind(this,this._resetPosition)); -+ this._settings.connect('changed::height-fraction', Lang.bind(this,this._resetPosition)); -+ this._settings.connect('changed::insensitive-message-tray', Lang.bind(this,this._updateInsensitiveTray)); -+ this._settings.connect('changed::require-pressure-to-show', Lang.bind(this,function(){ -+ // Remove pointer watcher -+ if(this._dockWatch){ -+ PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch); -+ this._dockWatch = null; -+ } -+ this._setupDockDwellIfNeeded(); -+ this._updateBarrier(); -+ })); -+ this._settings.connect('changed::pressure-threshold', Lang.bind(this,function() { -+ this._updatePressureBarrier(); -+ this._updateBarrier(); -+ })); -+ -+ }, -+ -+ // This is call when visibility settings change -+ _updateVisibilityMode: function() { -+ -+ if (this._settings.get_boolean('dock-fixed')) { -+ this._fixedIsEnabled = true; -+ this._autohideIsEnabled = false; -+ this._intellihideIsEnabled = false; -+ } else { -+ this._fixedIsEnabled = false; -+ this._autohideIsEnabled = this._settings.get_boolean('autohide') -+ this._intellihideIsEnabled = this._settings.get_boolean('intellihide') -+ } -+ -+ if (this._intellihideIsEnabled) -+ this._intellihide.enable(); -+ else -+ this._intellihide.disable(); -+ -+ this._updateDashVisibility(); -+ }, -+ -+ /* Show/hide dash based on, in order of priority: -+ * overview visibility -+ * fixed mode -+ * intellihide -+ * autohide -+ * overview visibility -+ */ -+ _updateDashVisibility: function() { -+ -+ if (Main.overview.visibleTarget) -+ return; -+ -+ if ( this._fixedIsEnabled ) { -+ this._removeAnimations(); -+ this._animateIn(this._settings.get_double('animation-time'), 0); -+ } else if (this._intellihideIsEnabled) { -+ if ( this._intellihide.getOverlapStatus() ) { -+ this._ignoreHover = false; -+ // Do not hide if autohide is enabled and mouse is hover -+ if (!this._box.hover || !this._autohideIsEnabled) { -+ this._animateOut(this._settings.get_double('animation-time'), 0); -+ } -+ } else { -+ this._ignoreHover = true; -+ this._removeAnimations(); -+ this._animateIn(this._settings.get_double('animation-time'), 0); -+ } -+ } else { -+ if (this._autohideIsEnabled) { -+ this._ignoreHover = false; -+ global.sync_pointer(); -+ -+ if( this._box.hover ) { -+ this._animateIn(this._settings.get_double('animation-time'), 0); -+ } else { -+ this._animateOut(this._settings.get_double('animation-time'), 0); -+ } -+ -+ } else { -+ this._animateOut(this._settings.get_double('animation-time'), 0); -+ } -+ } -+ }, -+ -+ _onOverviewShowing: function() { -+ this._ignoreHover = true; -+ this._intellihide.disable(); -+ this._removeAnimations(); -+ this._animateIn(this._settings.get_double('animation-time'), 0); -+ }, -+ -+ _onOverviewHiding: function() { -+ this._ignoreHover = false; -+ this._intellihide.enable(); -+ this._updateDashVisibility(); -+ }, -+ -+ _hoverChanged: function() { -+ -+ if (!this._ignoreHover) { -+ -+ // Skip if dock is not in autohide mode for instance because it is shown -+ // by intellihide. -+ if(this._autohideIsEnabled) { -+ if( this._box.hover ) { -+ this._show(); -+ } else { -+ this._hide(); -+ } -+ } -+ } -+ }, -+ -+ _show: function() { -+ -+ if ( this._dockState == State.HIDDEN || this._dockState == State.HIDING ) { -+ -+ if(this._dockState == State.HIDING){ -+ // suppress all potential queued hiding animations - i.e. added to Tweener but not started, -+ // always give priority to show -+ this._removeAnimations(); -+ } -+ -+ this.emit("showing"); -+ this._animateIn(this._settings.get_double('animation-time'), 0); -+ } -+ }, -+ -+ _hide: function() { -+ -+ // If no hiding animation is running or queued -+ if ( this._dockState == State.SHOWN || this._dockState == State.SHOWING ) { -+ -+ let delay; -+ -+ if (this._dockState == State.SHOWING) { -+ //if a show already started, let it finish; queue hide without removing the show. -+ // to obtain this I increase the delay to avoid the overlap and interference -+ // between the animations -+ delay = this._settings.get_double('hide-delay') + this._settings.get_double('animation-time'); -+ } else { -+ delay = this._settings.get_double('hide-delay'); -+ } -+ -+ this.emit("hiding"); -+ this._animateOut(this._settings.get_double('animation-time'), delay); -+ -+ } -+ }, -+ -+ _animateIn: function(time, delay) { -+ -+ this._dockState = State.SHOWING; -+ -+ Tweener.addTween(this._slider,{ -+ slidex: 1, -+ time: time, -+ delay: delay, -+ transition: 'easeOutQuad', -+ onComplete: Lang.bind(this, function() { -+ this._dockState = State.SHOWN; -+ // Remove barrier so that mouse pointer is released and can access monitors on other side of dock -+ // NOTE: Delay needed to keep mouse from moving past dock and re-hiding dock immediately. This -+ // gives users an opportunity to hover over the dock -+ if (this._removeBarrierTimeoutId > 0) { -+ Mainloop.source_remove(this._removeBarrierTimeoutId); -+ } -+ this._removeBarrierTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, this._removeBarrier)); -+ }) -+ }); -+ }, -+ -+ _animateOut: function(time, delay){ -+ -+ this._dockState = State.HIDING; -+ Tweener.addTween(this._slider,{ -+ slidex: 0, -+ time: time, -+ delay: delay , -+ transition: 'easeOutQuad', -+ onComplete: Lang.bind(this, function() { -+ this._dockState = State.HIDDEN; -+ this._updateBarrier(); -+ }) -+ }); -+ }, -+ -+ // Dwelling system based on the GNOME Shell 3.14 messageTray code. -+ _setupDockDwellIfNeeded: function() { -+ // If we don't have extended barrier features, then we need -+ // to support the old tray dwelling mechanism. -+ if (!global.display.supports_extended_barriers() || !this._settings.get_boolean('require-pressure-to-show')) { -+ let pointerWatcher = PointerWatcher.getPointerWatcher(); -+ this._dockWatch = pointerWatcher.addWatch(DOCK_DWELL_CHECK_INTERVAL, Lang.bind(this, this._checkDockDwell)); -+ this._dockDwelling = false; -+ this._dockDwellUserTime = 0; -+ } -+ }, -+ -+ _checkDockDwell: function(x, y) { -+ let monitor = this._monitor; -+ -+ // Check for the dock area -+ let shouldDwell = (x >= this.staticBox.x1 && x <= this.staticBox.x2 && -+ y >= this.staticBox.y1 && y <= this.staticBox.y2); -+ -+ // Check for the correct screen edge -+ // Position is approximated to the lower integer -+ if(this._position==St.Side.LEFT){ -+ shouldDwell = shouldDwell && x == this._monitor.x; -+ } else if(this._position==St.Side.RIGHT) { -+ shouldDwell = shouldDwell && x == this._monitor.x + this._monitor.width - 1; -+ } else if(this._position==St.Side.TOP) { -+ shouldDwell = shouldDwell && y == this._monitor.y; -+ } else if (this._position==St.Side.BOTTOM) { -+ shouldDwell = shouldDwell && y == this._monitor.y + this._monitor.height - 1; -+ } -+ -+ if (shouldDwell) { -+ // We only set up dwell timeout when the user is not hovering over the dock -+ // already (!this._box._hover). -+ // The _dockDwelling variable is used so that we only try to -+ // fire off one dock dwell - if it fails (because, say, the user has the mouse down), -+ // we don't try again until the user moves the mouse up and down again. -+ if (!this._dockDwelling && !this._box._hover && this._dockDwellTimeoutId == 0) { -+ // Save the interaction timestamp so we can detect user input -+ let focusWindow = global.display.focus_window; -+ this._dockDwellUserTime = focusWindow ? focusWindow.user_time : 0; -+ -+ this._dockDwellTimeoutId = Mainloop.timeout_add(this._settings.get_double('show-delay')*1000, -+ Lang.bind(this, this._dockDwellTimeout)); -+ GLib.Source.set_name_by_id(this._dockDwellTimeoutId, '[dash-to-dock] this._dockDwellTimeout'); -+ } -+ this._dockDwelling = true; -+ } else { -+ this._cancelDockDwell(); -+ this._dockDwelling = false; -+ } -+ }, -+ -+ _cancelDockDwell: function() { -+ if (this._dockDwellTimeoutId != 0) { -+ Mainloop.source_remove(this._dockDwellTimeoutId); -+ this._dockDwellTimeoutId = 0; -+ } -+ }, -+ -+ _dockDwellTimeout: function() { -+ this._dockDwellTimeoutId = 0; -+ -+ if (this._monitor.inFullscreen) -+ return GLib.SOURCE_REMOVE; -+ -+ // We don't want to open the tray when a modal dialog -+ // is up, so we check the modal count for that. When we are in the -+ // overview we have to take the overview's modal push into account -+ if (Main.modalCount > (Main.overview.visible ? 1 : 0)) -+ return GLib.SOURCE_REMOVE; -+ -+ // If the user interacted with the focus window since we started the tray -+ // dwell (by clicking or typing), don't activate the message tray -+ let focusWindow = global.display.focus_window; -+ let currentUserTime = focusWindow ? focusWindow.user_time : 0; -+ if (currentUserTime != this._dockDwellUserTime) -+ return GLib.SOURCE_REMOVE; -+ -+ // Reuse the pressure version function, the logic is the same -+ this._onPressureSensed(); -+ return GLib.SOURCE_REMOVE; -+ }, -+ -+ _updatePressureBarrier: function() { -+ this._canUsePressure = global.display.supports_extended_barriers(); -+ let pressureThreshold = this._settings.get_double('pressure-threshold'); -+ -+ // Remove existing pressure barrier -+ if (this._pressureBarrier) { -+ this._pressureBarrier.destroy(); -+ this._pressureBarrier = null; -+ } -+ -+ // Create new pressure barrier based on pressure threshold setting -+ if (this._canUsePressure) { -+ this._pressureBarrier = new Layout.PressureBarrier(pressureThreshold, this._settings.get_double('show-delay')*1000, -+ Shell.KeyBindingMode.NORMAL | Shell.KeyBindingMode.OVERVIEW); -+ this._pressureBarrier.connect('trigger', Lang.bind(this, function(barrier){ -+ if (this._monitor.inFullscreen) -+ return; -+ this._onPressureSensed(); -+ })); -+ } -+ }, -+ -+ // handler for mouse pressure sensed -+ _onPressureSensed: function() { -+ -+ if (Main.overview.visibleTarget) -+ return; -+ -+ // In case the mouse move away from the dock area before hovering it, in such case the leave event -+ // would never be triggered and the dock would stay visible forever. -+ let triggerTimeoutId = Mainloop.timeout_add(250, -+ Lang.bind(this, function() { -+ triggerTimeoutId = 0; -+ this._hoverChanged(); -+ return GLib.SOURCE_REMOVE; -+ })); -+ -+ this._show(); -+ }, -+ -+ _onMessageTrayShowing: function() { -+ -+ // Temporary move the dash below the top panel so that it slide below it. -+ Main.layoutManager.uiGroup.set_child_below_sibling(this.actor, Main.layoutManager.panelBox); -+ -+ // Remove other tweens that could mess with the state machine -+ Tweener.removeTweens(this.actor); -+ this._oldignoreHover = this._ignoreHover; -+ this._ignoreHover = true; -+ this.dash.cleanUpLabels(); -+ Tweener.addTween(this.actor, { -+ y: this._y0 - Main.messageTray.actor.height, -+ time: MessageTray.ANIMATION_TIME, -+ transition: 'easeOutQuad' -+ }); -+ this._messageTrayShowing = true; -+ this._updateBarrier(); -+ }, -+ -+ _onMessageTrayHiding: function() { -+ -+ // Remove other tweens that could mess with the state machine -+ Tweener.removeTweens(this.actor); -+ Tweener.addTween(this.actor, { -+ y: this._y0, -+ time: MessageTray.ANIMATION_TIME, -+ transition: 'easeOutQuad', -+ onComplete: Lang.bind(this, function(){ -+ // Reset desired dash stack order (on top to accept dnd of app icons) -+ Main.layoutManager.uiGroup.set_child_below_sibling(this.actor, Main.layoutManager.modalDialogGroup); -+ // restore previous ignoreHover. If it was not set, set it to false -+ if (this._oldignoreHover !== null) -+ this._ignoreHover = this._oldignoreHover; -+ this._oldignoreHover == null; -+ if (!isMouseHover(this._box)) -+ this._box.hover = false; -+ }) -+ }); -+ -+ this._messageTrayShowing = false; -+ this._updateBarrier(); -+ }, -+ -+ // Remove pressure barrier -+ _removeBarrier: function() { -+ if (this._barrier) { -+ if (this._pressureBarrier) { -+ this._pressureBarrier.removeBarrier(this._barrier); -+ } -+ this._barrier.destroy(); -+ this._barrier = null; -+ } -+ this._removeBarrierTimeoutId = 0; -+ return false; -+ }, -+ -+ // Update pressure barrier size -+ _updateBarrier: function() { -+ // Remove existing barrier -+ this._removeBarrier(); -+ -+ // Manually reset pressure barrier -+ // This is necessary because we remove the pressure barrier when it is triggered to show the dock -+ if (this._pressureBarrier) { -+ this._pressureBarrier._reset(); -+ this._pressureBarrier._isTriggered = false; -+ } -+ -+ // Create new barrier -+ // Note: dash in fixed position doesn't use pressure barrier -+ if (this._slider.actor.visible && this._canUsePressure && this._autohideIsEnabled && this._settings.get_boolean('require-pressure-to-show') && !this._messageTrayShowing) { -+ let x1, x2, y1, y2, direction; -+ -+ if(this._position==St.Side.LEFT){ -+ x1 = this.staticBox.x1; -+ x2 = this.staticBox.x1; -+ y1 = this.staticBox.y1; -+ y2 = this.staticBox.y2; -+ direction = Meta.BarrierDirection.POSITIVE_X; -+ } else if(this._position==St.Side.RIGHT) { -+ x1 = this.staticBox.x2; -+ x2 = this.staticBox.x2; -+ y1 = this.staticBox.y1; -+ y2 = this.staticBox.y2; -+ direction = Meta.BarrierDirection.NEGATIVE_X; -+ } else if(this._position==St.Side.TOP) { -+ x1 = this.staticBox.x1; -+ x2 = this.staticBox.x2; -+ y1 = this.staticBox.y1; -+ y2 = this.staticBox.y1; -+ direction = Meta.BarrierDirection.POSITIVE_Y; -+ } else if (this._position==St.Side.BOTTOM) { -+ x1 = this.staticBox.x1; -+ x2 = this.staticBox.x2; -+ y1 = this.staticBox.y2; -+ y2 = this.staticBox.y2; -+ direction = Meta.BarrierDirection.NEGATIVE_Y; -+ } -+ -+ this._barrier = new Meta.Barrier({display: global.display, -+ x1: x1, x2: x2, -+ y1: y1, y2: y2, -+ directions: direction}); -+ if (this._pressureBarrier) { -+ this._pressureBarrier.addBarrier(this._barrier); -+ } -+ } -+ -+ }, -+ -+ _isPrimaryMonitor: function() { -+ return (this._monitor.x == Main.layoutManager.primaryMonitor.x && -+ this._monitor.y == Main.layoutManager.primaryMonitor.y); -+ }, -+ -+ _resetPosition: function() { -+ -+ // Ensure variables linked to settings are updated. -+ this._updateVisibilityMode(); -+ -+ this._monitor = this._getMonitor(); -+ -+ let unavailableTopSpace = 0; -+ let unavailableBottomSpace = 0; -+ -+ let extendHeight = this._settings.get_boolean('extend-height'); -+ -+ // Reserve space for the dash on the overview -+ // if the dock is on the primary monitor -+ if (this._isPrimaryMonitor()){ -+ unavailableTopSpace = Main.panel.actor.height; -+ this._dashSpacer.show(); -+ } else { -+ // No space is required in the overview of the dash -+ this._dashSpacer.hide(); -+ } -+ -+ let fraction = this._settings.get_double('height-fraction'); -+ -+ if(extendHeight) -+ fraction = 1; -+ else if(fraction<0 || fraction >1) -+ fraction = 0.95; -+ -+ let anchor_point; -+ -+ if(this._isHorizontal){ -+ -+ let availableWidth = this._monitor.width; -+ this.actor.width = Math.round( fraction * availableWidth); -+ -+ let pos_y; -+ if( this._position == St.Side.BOTTOM) { -+ pos_y = this._monitor.y + this._monitor.height; -+ anchor_point = Clutter.Gravity.SOUTH_WEST; -+ } else { -+ pos_y = this._monitor.y + unavailableTopSpace; -+ anchor_point = Clutter.Gravity.NORTH_WEST; -+ } -+ -+ this.actor.move_anchor_point_from_gravity(anchor_point); -+ this.actor.x = this._monitor.x + Math.round( (1-fraction)/2 * availableWidth); -+ this.actor.y = pos_y; -+ -+ if(extendHeight){ -+ this.dash._container.set_width(this.actor.width); -+ this.actor.add_style_class_name('extended'); -+ } else { -+ this.dash._container.set_width(-1); -+ this.actor.remove_style_class_name('extended'); -+ } -+ -+ } else { -+ -+ let availableHeight = this._monitor.height - unavailableTopSpace - unavailableBottomSpace; -+ this.actor.height = Math.round( fraction * availableHeight); -+ -+ let pos_x; -+ if( this._position == St.Side.RIGHT) { -+ pos_x = this._monitor.x + this._monitor.width; -+ anchor_point = Clutter.Gravity.NORTH_EAST; -+ } else { -+ pos_x = this._monitor.x; -+ anchor_point = Clutter.Gravity.NORTH_WEST; -+ } -+ -+ this.actor.move_anchor_point_from_gravity(anchor_point); -+ this.actor.x = pos_x; -+ this.actor.y = this._monitor.y + unavailableTopSpace + Math.round( (1-fraction)/2 * availableHeight); -+ -+ if(extendHeight){ -+ this.dash._container.set_height(this.actor.height); -+ this.actor.add_style_class_name('extended'); -+ } else { -+ this.dash._container.set_height(-1); -+ this.actor.remove_style_class_name('extended'); -+ } -+ } -+ -+ this._y0 = this.actor.y; -+ this._adjustPanelCorners(); -+ -+ this._updateStaticBox(); -+ }, -+ -+ _updateStaticBox: function() { -+ -+ this.staticBox.init_rect( -+ this.actor.x + this._slider.actor.x - (this._position==St.Side.RIGHT?this._box.width:0), -+ this.actor.y + this._slider.actor.y - (this._position==St.Side.BOTTOM?this._box.height:0), -+ this._box.width, -+ this._box.height -+ ); -+ -+ this._intellihide.updateTargetBox(this.staticBox); -+ }, -+ -+ // Adjust Panel corners -+ _adjustPanelCorners: function() { -+ let extendHeight = this._settings.get_boolean('extend-height'); -+ if (!this._isHorizontal && this._isPrimaryMonitor() && extendHeight && this._fixedIsEnabled) { -+ Main.panel._rightCorner.actor.hide(); -+ Main.panel._leftCorner.actor.hide(); -+ } else { -+ this._revertPanelCorners(); -+ } -+ }, -+ -+ _revertPanelCorners: function() { -+ Main.panel._leftCorner.actor.show(); -+ Main.panel._rightCorner.actor.show(); -+ }, -+ -+ _getMonitor: function(){ -+ -+ let monitorIndex = this._settings.get_int('preferred-monitor'); -+ let monitor; -+ -+ if (monitorIndex >0 && monitorIndex< Main.layoutManager.monitors.length) -+ monitor = Main.layoutManager.monitors[monitorIndex]; -+ else -+ monitor = Main.layoutManager.primaryMonitor; -+ -+ return monitor; -+ }, -+ -+ _removeAnimations: function() { -+ Tweener.removeTweens(this._slider); -+ }, -+ -+ _onDragStart: function(){ -+ // The dash need to be above the top_window_group, otherwise it doesn't -+ // accept dnd of app icons when not in overiew mode. -+ Main.layoutManager.uiGroup.set_child_above_sibling(this.actor, global.top_window_group); -+ this._oldignoreHover = this._ignoreHover; -+ this._ignoreHover = true; -+ this._animateIn(this._settings.get_double('animation-time'), 0); -+ }, -+ -+ _onDragEnd: function(){ -+ // Restore drag default dash stack order -+ Main.layoutManager.uiGroup.set_child_below_sibling(this.actor, Main.layoutManager.modalDialogGroup); -+ if (this._oldignoreHover !== null) -+ this._ignoreHover = this._oldignoreHover; -+ this._oldignoreHover = null; -+ this._box.sync_hover(); -+ if(Main.overview._shown) -+ this._pageChanged(); -+ }, -+ -+ _pageChanged: function() { -+ -+ let activePage = Main.overview.viewSelector.getActivePage(); -+ let dashVisible = (activePage == ViewSelector.ViewPage.WINDOWS || -+ activePage == ViewSelector.ViewPage.APPS); -+ -+ if(dashVisible){ -+ this._animateIn(this._settings.get_double('animation-time'), 0); -+ } else { -+ this._animateOut(this._settings.get_double('animation-time'), 0); -+ } -+ }, -+ -+ _onPageEmpty: function() { -+ /* The dash spacer is required only in the WINDOWS view if in the default position. -+ * The 'page-empty' signal is emitted in between a change of view, -+ * signalling the spacer can be added and removed without visible effect, -+ * as it's done for the upstream dashSpacer. -+ * -+ * Moreover, hiding the spacer ensure the appGrid allocaton is triggered. -+ * This matter as the appview spring animation is triggered by to first reallocaton of the appGrid, -+ * (See appDisplay.js, line 202 on GNOME Shell 3.14: -+ * this._grid.actor.connect('notify::allocation', ...) -+ * which in turn seems to be triggered by changes in the other actors in the overview. -+ * Normally, as far as I could understand, either the dashSpacer being hidden or the workspacesThumbnails -+ * sliding out would trigger the allocation. However, with no stock dash -+ * and no thumbnails, which happen if the user configured only 1 and static workspace, -+ * the animation out of icons is not played. -+ */ -+ -+ let activePage = Main.overview.viewSelector.getActivePage(); -+ this._dashSpacer.visible = (this._isHorizontal || activePage == ViewSelector.ViewPage.WINDOWS); -+ }, -+ -+ // Show dock and give key focus to it -+ _onAccessibilityFocus: function(){ -+ this._box.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); -+ this._animateIn(this._settings.get_double('animation-time'), 0); -+ }, -+ -+ _onShowAppsButtonToggled: function() { -+ -+ // Sync the status of the default appButtons. Only if the two statuses are -+ // different, that means the user interacted with the extension provided -+ // application button, cutomize the behaviour. Otherwise the shell has changed the -+ // status (due to the _syncShowAppsButtonToggled function below) and it -+ // has already performed the desired action. -+ -+ let selector = Main.overview.viewSelector; -+ -+ if(selector._showAppsButton.checked !== this.dash.showAppsButton.checked){ -+ -+ // find visible view -+ let visibleView; -+ Main.overview.viewSelector.appDisplay._views.every(function(v, index) { -+ if (v.view.actor.visible) { -+ visibleView = index; -+ return false; -+ } else { -+ return true; -+ } -+ }); -+ -+ if(this.dash.showAppsButton.checked){ -+ // force entering overview if needed -+ if (!Main.overview._shown) { -+ -+ let view = Main.overview.viewSelector.appDisplay._views[visibleView].view; -+ let grid = view._grid; -+ -+ // Animate in the the appview, hide the appGrid to avoiud flashing -+ // Go to the appView before entering the overview, skipping the workspaces. -+ // Do this manually avoiding opacity in transitions so that the setting of the opacity -+ // to 0 doesn't get overwritten. -+ Main.overview.viewSelector._activePage.opacity = 0; -+ Main.overview.viewSelector._activePage.hide(); -+ Main.overview.viewSelector._activePage = Main.overview.viewSelector._appsPage; -+ Main.overview.viewSelector._activePage.show(); -+ grid.actor.opacity = 0; -+ selector._showAppsButton.checked = true; -+ -+ // The animation has to be trigered manually because the AppDisplay.animate -+ // method is waiting for an allocation not happening, as we skip the workspace view -+ // and the appgrid could already be allocated from previous shown. -+ // It has to be triggered after the overview is shown as wrong coordinates are obtained -+ // otherwise. -+ let overviewShownId = Main.overview.connect('shown', Lang.bind(this, function(){ -+ Main.overview.disconnect(overviewShownId); -+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { -+ grid.actor.opacity = 255; -+ grid.animateSpring(IconGrid.AnimationDirection.IN, this.dash.showAppsButton); -+ })); -+ })); -+ -+ // Finally show the overview -+ Main.overview.show(); -+ this.forcedOverview = true; -+ } else { -+ selector._showAppsButton.checked = true; -+ } -+ } else { -+ if (this.forcedOverview) { -+ // force exiting overview if needed -+ -+ // Manually trigger springout animation without activating the -+ // workspaceView to avoid the zoomout animation. Hide the appPage -+ // onComplete to avoid ugly flashing of original icons. -+ let view = Main.overview.viewSelector.appDisplay._views[visibleView].view; -+ let grid = view._grid; -+ view.animate(IconGrid.AnimationDirection.OUT, Lang.bind(this, function(){ -+ Main.overview.viewSelector._appsPage.hide(); -+ Main.overview.hide(); -+ selector._showAppsButton.checked = false; -+ this.forcedOverview = false; -+ })); -+ -+ } else { -+ selector._showAppsButton.checked = false; -+ } -+ -+ } -+ } -+ -+ // whenever the button is unactivated even if not by the user still reset the -+ // forcedOverview flag -+ if( this.dash.showAppsButton.checked==false) -+ this.forcedOverview = false; -+ }, -+ -+ // Keep ShowAppsButton status in sync with the overview status -+ _syncShowAppsButtonToggled: function() { -+ let status = Main.overview.viewSelector._showAppsButton.checked; -+ if(this.dash.showAppsButton.checked !== status) -+ this.dash.showAppsButton.checked = status; -+ }, -+ -+ // Optional features enable/disable -+ -+ // Switch workspace by scrolling over the dock -+ _optionalScrollWorkspaceSwitch: function() { -+ -+ let label = 'optionalScrollWorkspaceSwitch'; -+ -+ this._settings.connect('changed::scroll-switch-workspace',Lang.bind(this, function(){ -+ if(this._settings.get_boolean('scroll-switch-workspace')) -+ Lang.bind(this, enable)(); -+ else -+ Lang.bind(this, disable)(); -+ })); -+ -+ if(this._settings.get_boolean('scroll-switch-workspace')) -+ Lang.bind(this, enable)(); -+ -+ function enable(){ -+ -+ this._signalsHandler.removeWithLabel(label); -+ -+ this._signalsHandler.addWithLabel(label, -+ [ -+ this._box, -+ 'scroll-event', -+ Lang.bind(this, onScrollEvent) -+ ] -+ ); -+ -+ this._optionalScrollWorkspaceSwitchDeadTimeId=0; -+ } -+ -+ function disable() { -+ this._signalsHandler.removeWithLabel(label); -+ -+ if(this._optionalScrollWorkspaceSwitchDeadTimeId>0){ -+ Mainloop.source_remove(this._optionalScrollWorkspaceSwitchDeadTimeId); -+ this._optionalScrollWorkspaceSwitchDeadTimeId=0; -+ } -+ } -+ -+ // This was inspired to desktop-scroller@obsidien.github.com -+ function onScrollEvent(actor, event) { -+ -+ // When in overview change workscape only in windows view -+ if (Main.overview.visible && Main.overview.viewSelector.getActivePage() !== ViewSelector.ViewPage.WINDOWS) -+ return false; -+ -+ let activeWs = global.screen.get_active_workspace(); -+ let direction = null; -+ -+ switch ( event.get_scroll_direction() ) { -+ case Clutter.ScrollDirection.UP: -+ direction = Meta.MotionDirection.UP; -+ break; -+ case Clutter.ScrollDirection.DOWN: -+ direction = Meta.MotionDirection.DOWN; -+ break; -+ case Clutter.ScrollDirection.SMOOTH: -+ let [dx, dy] = event.get_scroll_delta(); -+ if(dy < 0){ -+ direction = Meta.MotionDirection.UP; -+ } else if(dy > 0) { -+ direction = Meta.MotionDirection.DOWN; -+ } -+ break; -+ } -+ -+ if(direction !==null ){ -+ -+ // Prevent scroll events from triggering too many workspace switches -+ // by adding a 250ms deadtime between each scroll event. -+ // Usefull on laptops when using a touchpad. -+ -+ // During the deadtime do nothing -+ if(this._optionalScrollWorkspaceSwitchDeadTimeId>0) -+ return false; -+ else { -+ this._optionalScrollWorkspaceSwitchDeadTimeId = -+ Mainloop.timeout_add(250, -+ Lang.bind(this, function() { -+ this._optionalScrollWorkspaceSwitchDeadTimeId=0; -+ } -+ )); -+ } -+ -+ -+ let ws; -+ -+ ws = activeWs.get_neighbor(direction) -+ -+ if (Main.wm._workspaceSwitcherPopup == null) -+ Main.wm._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup(); -+ // Set the actor non reactive, so that it doesn't prevent the -+ // clicks events from reaching the dash actor. I can't see a reason -+ // why it should be reactive. -+ Main.wm._workspaceSwitcherPopup.actor.reactive = false; -+ Main.wm._workspaceSwitcherPopup.connect('destroy', function() { -+ Main.wm._workspaceSwitcherPopup = null; -+ }); -+ -+ // Do not show wokspaceSwithcer in overview -+ if(!Main.overview.visible) -+ Main.wm._workspaceSwitcherPopup.display(direction, ws.index()); -+ Main.wm.actionMoveWorkspace(ws); -+ -+ return true; -+ -+ } else { -+ return false; -+ } -+ } -+ -+ }, -+ -+ // Makes the message not being triggered by mouse. SOURCE: insensitive-tray extension. -+ _updateInsensitiveTray: function() { -+ -+ let insensitive = this._settings.get_boolean('insensitive-message-tray'); -+ -+ if("_trayPressure" in LayoutManager) { -+ // Systems supporting pressure -+ if (insensitive) { -+ LayoutManager._trayPressure._keybindingMode = null; -+ } else { -+ LayoutManager._trayPressure._keybindingMode = Shell.KeyBindingMode.NORMAL -+ | Shell.KeyBindingMode.OVERVIEW; -+ } -+ } else { -+ //systems using the old dwell mechanism -+ if (insensitive) { -+ this._injectionsHandler.addWithLabel('insensitive-message-tray', -+ [ -+ Main.messageTray, -+ '_trayDwellTimeout', -+ function() { return false; } -+ ] -+ ); -+ } else { -+ this._injectionsHandler.removeWithLabel('insensitive-message-tray') -+ } -+ } -+ } -+}); -+Signals.addSignalMethods(dockedDash.prototype); -+ -+/* -+ * Manage theme customization and custom theme support -+*/ -+const themeManager = new Lang.Class({ -+ Name: 'ThemeManager', -+ -+ _init: function(settings, actor, dash) { -+ -+ this._settings = settings; -+ this._bindSettingsChanges(); -+ this._actor = actor; -+ this._dash = dash; -+ -+ // initialize colors with generic values -+ this._defaultBackground = {red: 0, green:0, blue: 0, alpha:0}; -+ this._defaultBackgroundColor = {red: 0, green:0, blue: 0, alpha:0}; -+ this._customizedBackground = {red: 0, green:0, blue: 0, alpha:0}; -+ -+ this._signalsHandler = new Convenience.GlobalSignalsHandler(); -+ this._signalsHandler.add( -+ // When theme changes re-obtain default background color -+ [ -+ St.ThemeContext.get_for_stage (global.stage), -+ 'changed', -+ Lang.bind(this, this.updateCustomTheme) -+ ], -+ // update :overview pseudoclass -+ [ -+ Main.overview, -+ 'showing', -+ Lang.bind(this, this._onOverviewShowing) -+ ], -+ [ -+ Main.overview, -+ 'hiding', -+ Lang.bind(this, this._onOverviewHiding) -+ ] -+ ); -+ -+ this._updateCustomStyleClasses(); -+ -+ }, -+ -+ destroy: function() { -+ this._signalsHandler.destroy(); -+ }, -+ -+ _onOverviewShowing: function() { -+ this._actor.add_style_pseudo_class('overview'); -+ }, -+ -+ _onOverviewHiding: function() { -+ this._actor.remove_style_pseudo_class('overview'); -+ }, -+ -+ _updateBackgroundOpacity: function() { -+ -+ let newAlpha = this._settings.get_double('background-opacity'); -+ -+ this._defaultBackground = 'rgba('+ -+ this._defaultBackgroundColor.red + ','+ -+ this._defaultBackgroundColor.green + ','+ -+ this._defaultBackgroundColor.blue + ','+ -+ Math.round(this._defaultBackgroundColor.alpha/2.55)/100 + ')'; -+ -+ this._customizedBackground = 'rgba('+ -+ this._defaultBackgroundColor.red + ','+ -+ this._defaultBackgroundColor.green + ','+ -+ this._defaultBackgroundColor.blue + ','+ -+ newAlpha + ')'; -+ }, -+ -+ _getBackgroundColor: function() { -+ -+ // Prevent shell crash if the actor is not on the stage. -+ // It happens enabling/disabling repeatedly the extension -+ if(!this._dash._container.get_stage()) -+ return; -+ -+ // Remove custom style -+ let oldStyle = this._dash._container.get_style(); -+ this._dash._container.set_style(null); -+ -+ let themeNode = this._dash._container.get_theme_node(); -+ this._dash._container.set_style(oldStyle); -+ -+ this._defaultBackgroundColor = themeNode.get_background_color(); -+ }, -+ -+ _updateCustomStyleClasses: function(){ -+ -+ if (this._settings.get_boolean('apply-custom-theme')) -+ this._actor.add_style_class_name('dashtodock'); -+ else { -+ this._actor.remove_style_class_name('dashtodock'); -+ } -+ -+ if (this._settings.get_boolean('custom-theme-shrink')) -+ this._actor.add_style_class_name('shrink'); -+ else { -+ this._actor.remove_style_class_name('shrink'); -+ } -+ -+ if (this._settings.get_boolean('custom-theme-running-dots')) -+ this._actor.add_style_class_name('running-dots'); -+ else { -+ this._actor.remove_style_class_name('running-dots'); -+ } -+ -+ }, -+ -+ updateCustomTheme: function() { -+ this._updateCustomStyleClasses(); -+ this._getBackgroundColor(); -+ this._updateBackgroundOpacity(); -+ this._adjustTheme(); -+ this._dash._redisplay(); -+ }, -+ -+ /* Reimported back and adapted from atomdock */ -+ _adjustTheme: function() { -+ // Prevent shell crash if the actor is not on the stage. -+ // It happens enabling/disabling repeatedly the extension -+ if (!this._dash._container.get_stage()) { -+ return; -+ } -+ -+ // Remove prior style edits -+ this._dash._container.set_style(null); -+ -+ /* If built-in theme is enabled do nothing else */ -+ if( this._settings.get_boolean('apply-custom-theme') ) -+ return; -+ -+ let newStyle = ''; -+ let position = getPosition(this._settings); -+ -+ if ( ! this._settings.get_boolean('custom-theme-shrink') ) { -+ -+ // obtain theme border settings -+ let themeNode = this._dash._container.get_theme_node(); -+ let borderColor = themeNode.get_border_color(St.Side.TOP); -+ let borderWidth = themeNode.get_border_width(St.Side.TOP); -+ let borderRadius = themeNode.get_border_radius(St.Corner.TOPRIGHT); -+ -+ /* We're copying border and corner styles to left border and top-left -+ * corner, also removing bottom border and bottom-right corner styles -+ */ -+ let borderInner = ''; -+ let borderRadiusValue = ''; -+ let borderMissingStyle = ''; -+ -+ if (this._rtl && position != St.Side.RIGHT) { -+ borderMissingStyle = 'border-right: ' + borderWidth + 'px solid ' + -+ borderColor.to_string() + ';'; -+ } else if (!this._rtl && position != St.Side.LEFT){ -+ borderMissingStyle = 'border-left: ' + borderWidth + 'px solid ' + -+ borderColor.to_string() + ';'; -+ } -+ -+ switch(position) { -+ case St.Side.LEFT: -+ borderInner = 'border-left'; -+ borderRadiusValue = '0 ' + borderRadius + 'px ' + borderRadius + 'px 0;'; -+ break; -+ case St.Side.RIGHT: -+ borderInner = 'border-right'; -+ borderRadiusValue = borderRadius + 'px 0 0 ' + borderRadius + 'px;'; -+ break; -+ case St.Side.TOP: -+ borderInner = 'border-top'; -+ borderRadiusValue = '0 0 ' + borderRadius + 'px ' + borderRadius + 'px;'; -+ break; -+ case St.Side.BOTTOM: -+ borderInner = 'border-bottom'; -+ borderRadiusValue = borderRadius + 'px ' + borderRadius + 'px 0 0;'; -+ break; -+ } -+ -+ newStyle = borderInner + ': none;' + -+ 'border-radius: ' + borderRadiusValue + -+ borderMissingStyle ; -+ -+ /* I do call set_style possibly twice so that only the background gets the transition. -+ * The transition-property css rules seems to be unsupported -+ */ -+ this._dash._container.set_style(newStyle); -+ } -+ -+ /* Customize background */ -+ if ( this._settings.get_boolean('opaque-background') ) { -+ newStyle = newStyle + 'background-color:'+ this._customizedBackground + '; ' + -+ 'transition-delay: 0s; transition-duration: 0.250s;'; -+ this._dash._container.set_style(newStyle); -+ } -+ }, -+ -+ _bindSettingsChanges: function() { -+ -+ let keys = ['opaque-background', -+ 'background-opacity', -+ 'apply-custom-theme', -+ 'custom-theme-shrink', -+ 'custom-theme-running-dots', -+ 'extend-height']; -+ -+ keys.forEach(function(key){ -+ this._settings.connect('changed::'+key, -+ Lang.bind(this, this.updateCustomTheme) -+ ); -+ }, this ); -+ -+ } -+}); -+ -+ -+/* -+ * Manually check if mouse "can be" hover from the mouse position. The hover porperty -+ * is not reliable when focus move from the clutter actors to the the windows, giving a false -+ * positive hover status. If the mouse pointer is not in the right position I can be sure -+ * that the hover has to be false. -+*/ -+function isMouseHover(actor) { -+ let [pointerX, pointerY, mods] = global.get_pointer(); -+ -+ let [x, y] = actor.get_transformed_position(); -+ let [width, height] =actor.get_transformed_size(); -+ -+ let test = (pointerX < x + width) && -+ (pointerX > x) && -+ (pointerY < y + height) && -+ (pointerY > y); -+ -+ return test; -+} -diff --git a/extensions/dash-to-dock/extension.js b/extensions/dash-to-dock/extension.js -new file mode 100644 -index 0000000..4bd19fa ---- /dev/null -+++ b/extensions/dash-to-dock/extension.js -@@ -0,0 +1,52 @@ -+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- -+ -+const Me = imports.misc.extensionUtils.getCurrentExtension(); -+const Convenience = Me.imports.convenience; -+const DockedDash = Me.imports.dockedDash; -+ -+const Main = imports.ui.main; -+ -+let settings; -+let dock; -+ -+let oldDash; -+ -+function init() { -+ -+} -+ -+function enable() { -+ -+ settings = Convenience.getSettings('org.gnome.shell.extensions.dash-to-dock'); -+ dock = new DockedDash.dockedDash(settings); -+ -+ /* Pretend I'm the dash: meant to make appgrd swarm animation come from the -+ * right position of the appShowButton. -+ */ -+ oldDash = Main.overview._dash; -+ Main.overview._dash = dock.dash; -+ bindSettingsChanges(); -+} -+ -+function disable() { -+ dock.destroy(); -+ settings.run_dispose(); -+ Main.overview._dash = oldDash; -+ -+ dock=null; -+ settings = null; -+ oldDash=null; -+} -+ -+ -+function bindSettingsChanges() { -+ // This settings change require a full reload. -+ -+ /* It's easier to just reload the extension when the dock position changes -+ * rather than working out all changes to the differen containers. -+ */ -+ settings.connect('changed::dock-position', function(){ -+ disable(); -+ enable(); -+ }); -+} -diff --git a/extensions/dash-to-dock/intellihide.js b/extensions/dash-to-dock/intellihide.js -new file mode 100644 -index 0000000..0c79cee ---- /dev/null -+++ b/extensions/dash-to-dock/intellihide.js -@@ -0,0 +1,278 @@ -+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- -+ -+const Lang = imports.lang; -+const Mainloop = imports.mainloop; -+const Meta = imports.gi.Meta; -+const Shell = imports.gi.Shell; -+ -+const Main = imports.ui.main; -+const Signals = imports.signals; -+ -+const Me = imports.misc.extensionUtils.getCurrentExtension(); -+const Convenience = Me.imports.myConvenience; -+ -+// A good compromise between reactivity and efficiency; to be tuned. -+const INTELLIHIDE_CHECK_INTERVAL = 100; -+ -+const OverlapStatus = { -+ UNDEFINED: -1, -+ FALSE: 0, -+ TRUE: 1 -+}; -+ -+// List of windows type taken into account. Order is important (keep the original -+// enum order). -+const handledWindowTypes = [ -+ Meta.WindowType.NORMAL, -+ Meta.WindowType.DIALOG, -+ Meta.WindowType.MODAL_DIALOG, -+ Meta.WindowType.TOOLBAR, -+ Meta.WindowType.MENU, -+ Meta.WindowType.UTILITY, -+ Meta.WindowType.SPLASHSCREEN -+]; -+ -+/* -+ * A rough and ugly implementation of the intellihide behaviour. -+ * Intallihide object: emit 'status-changed' signal when the overlap of windows -+ * with the provided targetBoxClutter.ActorBox changes; -+ * -+*/ -+ -+const intellihide = new Lang.Class({ -+ Name: 'Intellihide', -+ -+ _init: function(settings) { -+ -+ // Load settings -+ this._settings = settings; -+ -+ this._signalsHandler = new Convenience.GlobalSignalsHandler(); -+ this._tracker = Shell.WindowTracker.get_default(); -+ this._focusApp = null; -+ -+ this._isEnabled = false; -+ this.status = OverlapStatus.UNDEFINED; -+ this._targetBox = null; -+ -+ // Main id of the timeout controlling timeout for updateDockVisibility function -+ // when windows are dragged around (move and resize) -+ this._windowChangedTimeout = 0; -+ -+ // Connect global signals -+ this._signalsHandler.add ( -+ // Add timeout when window grab-operation begins and remove it when it ends. -+ [ -+ global.display, -+ 'grab-op-begin', -+ Lang.bind(this, this._grabOpBegin) -+ ], -+ [ -+ global.display, -+ 'grab-op-end', -+ Lang.bind(this, this._grabOpEnd) -+ ], -+ // direct maximize/unmazimize are not included in grab-operations -+ [ -+ global.window_manager, -+ 'maximize', -+ Lang.bind(this, this._checkOverlap ) -+ ], -+ [ -+ global.window_manager, -+ 'unmaximize', -+ Lang.bind(this, this._checkOverlap ) -+ ], -+ // triggered for instance when the window list order changes, -+ // included when the workspace is switched -+ [ -+ global.screen, -+ 'restacked', -+ Lang.bind(this, this._checkOverlap) -+ ], -+ // update wne monitor changes, for instance in multimonitor when monitor are attached -+ [ -+ global.screen, -+ 'monitors-changed', -+ Lang.bind(this, this._checkOverlap ) -+ ] -+ ); -+ -+ }, -+ -+ destroy: function() { -+ -+ // Disconnect global signals -+ this._signalsHandler.destroy(); -+ -+ if(this._windowChangedTimeout>0) -+ Mainloop.source_remove(this._windowChangedTimeout); // Just to be sure -+ this._windowChangedTimeout=0; -+ }, -+ -+ enable: function() { -+ -+ this._isEnabled = true; -+ this._status = OverlapStatus.UNDEFINED; -+ this._checkOverlap(); -+ }, -+ -+ disable: function() { -+ this._isEnabled = false; -+ if(this._windowChangedTimeout>0) -+ Mainloop.source_remove(this._windowChangedTimeout); -+ this._windowChangedTimeout = 0; -+ }, -+ -+ updateTargetBox: function(box) { -+ this._targetBox = box; -+ this._checkOverlap(); -+ }, -+ -+ forceUpdate: function() { -+ this._status = OverlapStatus.UNDEFINED; -+ this._checkOverlap(); -+ }, -+ -+ getOverlapStatus: function(){ -+ if(this._status == OverlapStatus.TRUE) -+ return true; -+ else -+ return false; -+ }, -+ -+ _grabOpBegin: function() { -+ if(this._isEnabled){ -+ if(this._windowChangedTimeout>0) -+ Mainloop.source_remove(this._windowChangedTimeout); // Just to be sure -+ -+ this._windowChangedTimeout = Mainloop.timeout_add(INTELLIHIDE_CHECK_INTERVAL, -+ Lang.bind(this, function(){ -+ this._checkOverlap(); -+ return true; // to make the loop continue -+ }) -+ ); -+ } -+ }, -+ -+ _grabOpEnd: function() { -+ -+ if(this._windowChangedTimeout>0) -+ Mainloop.source_remove(this._windowChangedTimeout); -+ -+ this._windowChangedTimeout=0; -+ this._checkOverlap(); -+ }, -+ -+ _checkOverlap: function() { -+ -+ if( !this._isEnabled || this._targetBox == null) -+ return; -+ -+ let overlaps = OverlapStatus.FALSE; -+ let windows = global.get_window_actors(); -+ -+ if (windows.length>0){ -+ -+ // This is the window on top of all others in the current workspace -+ let topWindow = windows[windows.length-1].get_meta_window(); -+ // If there isn't a focused app, use that of the window on top -+ this._focusApp = this._tracker.focus_app || this._tracker.get_window_app(topWindow); -+ -+ windows = windows.filter(this._intellihideFilterInteresting, this); -+ -+ for(let i=0; i< windows.length; i++){ -+ -+ let win = windows[i].get_meta_window(); -+ if(win){ -+ let rect = win.get_outer_rect(); -+ -+ let test = ( rect.x < this._targetBox.x2) && -+ ( rect.x +rect.width > this._targetBox.x1 ) && -+ ( rect.y < this._targetBox.y2 ) && -+ ( rect.y +rect.height > this._targetBox.y1 ); -+ -+ if(test){ -+ overlaps = OverlapStatus.TRUE; -+ break; -+ } -+ } -+ } -+ } -+ -+ if ( this._status !== overlaps ) { -+ this._status = overlaps; -+ this.emit('status-changed', this._status); -+ } -+ -+ }, -+ -+ // Filter interesting windows to be considered for intellihide. -+ // Consider all windows visible on the current workspace. -+ // Optionally skip windows of other applications -+ _intellihideFilterInteresting: function(wa){ -+ -+ var currentWorkspace = global.screen.get_active_workspace_index(); -+ -+ var meta_win = wa.get_meta_window(); -+ if (!meta_win) { -+ return false; -+ } -+ -+ if ( !this._handledWindow(meta_win) ) -+ return false; -+ -+ var wksp = meta_win.get_workspace(); -+ var wksp_index = wksp.index(); -+ -+ // Skip windows of other apps -+ if(this._focusApp && this._settings.get_boolean('intellihide-perapp')) { -+ // The DropDownTerminal extension is not an application per se -+ // so we match its window by wm class instead -+ if (meta_win.get_wm_class() == 'DropDownTerminalWindow') -+ return true; -+ -+ let currentApp = this._tracker.get_window_app(meta_win); -+ -+ // But consider half maximized windows ( Useful if one is using -+ // two apps side by side and windows which are alwayson top -+ if( this._focusApp != currentApp -+ && !(meta_win.maximized_vertically && !meta_win.maximized_horizontally) -+ && !meta_win.is_above() -+ ) { -+ return false; -+ } -+ } -+ -+ if ( wksp_index == currentWorkspace && meta_win.showing_on_its_workspace() ) { -+ return true; -+ } else { -+ return false; -+ } -+ -+ }, -+ -+ // Filter windows by type -+ // inspired by Opacify@gnome-shell.localdomain.pl -+ _handledWindow: function(metaWindow) { -+ // The DropDownTerminal extension uses the POPUP_MENU window type hint -+ // so we match its window by wm class instead -+ if (metaWindow.get_wm_class() == 'DropDownTerminalWindow') -+ return true; -+ -+ var wtype = metaWindow.get_window_type(); -+ for (var i = 0; i < handledWindowTypes.length; i++) { -+ var hwtype = handledWindowTypes[i]; -+ if (hwtype == wtype) { -+ return true; -+ } else if (hwtype > wtype) { -+ return false; -+ } -+ } -+ return false; -+ -+ } -+ -+}); -+ -+Signals.addSignalMethods(intellihide.prototype); -diff --git a/extensions/dash-to-dock/media/four.svg b/extensions/dash-to-dock/media/four.svg -new file mode 100644 -index 0000000..8653206 ---- /dev/null -+++ b/extensions/dash-to-dock/media/four.svg -@@ -0,0 +1,132 @@ -+ -+ -+ -+ -+ -+ -+ -+ image/svg+xml -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/extensions/dash-to-dock/media/four_bottom.svg b/extensions/dash-to-dock/media/four_bottom.svg -new file mode 100644 -index 0000000..3fc4cb2 ---- /dev/null -+++ b/extensions/dash-to-dock/media/four_bottom.svg -@@ -0,0 +1,133 @@ -+ -+ -+ -+ -+ -+ -+ -+ image/svg+xml -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/extensions/dash-to-dock/media/four_rtl.svg b/extensions/dash-to-dock/media/four_rtl.svg -new file mode 100644 -index 0000000..67dcf28 ---- /dev/null -+++ b/extensions/dash-to-dock/media/four_rtl.svg -@@ -0,0 +1,133 @@ -+ -+ -+ -+ -+ -+ -+ -+ image/svg+xml -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/extensions/dash-to-dock/media/four_top.svg b/extensions/dash-to-dock/media/four_top.svg -new file mode 100644 -index 0000000..1b3dfcb ---- /dev/null -+++ b/extensions/dash-to-dock/media/four_top.svg -@@ -0,0 +1,133 @@ -+ -+ -+ -+ -+ -+ -+ -+ image/svg+xml -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/extensions/dash-to-dock/media/logo.svg b/extensions/dash-to-dock/media/logo.svg -new file mode 100644 -index 0000000..509d2e5 ---- /dev/null -+++ b/extensions/dash-to-dock/media/logo.svg -@@ -0,0 +1,561 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ image/svg+xml -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ Dash to Dock -+ Michele -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ ++ ++ ++ True ++ False ++ 0 ++ in ++ ++ ++ True ++ False ++ none ++ ++ ++ True ++ True ++ ++ ++ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ 32 ++ ++ ++ True ++ False ++ True ++ Behaviour when scrolling on the icon of an application. ++ True ++ 0 ++ ++ ++ ++ 0 ++ 1 ++ ++ ++ ++ ++ True ++ False ++ True ++ Scroll action ++ 0 ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ True ++ False ++ 6 ++ ++ ++ True ++ False ++ center ++ ++ Do nothing ++ Cycle through windows ++ Switch workspace ++ ++ ++ ++ False ++ True ++ 1 ++ ++ ++ ++ ++ 1 ++ 0 ++ 2 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ False ++ True ++ 3 ++ ++ ++ ++ ++ 2 ++ ++ ++ ++ ++ True ++ False ++ Behavior ++ ++ ++ 2 ++ False ++ ++ ++ ++ ++ True ++ False ++ 24 ++ 24 ++ 24 ++ 24 ++ vertical ++ 24 ++ ++ ++ True ++ False ++ 0 ++ in ++ ++ ++ True ++ True ++ none ++ ++ ++ True ++ True ++ ++ ++ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ 32 ++ ++ ++ True ++ False ++ True ++ 0 ++ Few customizations meant to integrate the dock with the default GNOME theme. Alternatively, specific options can be enabled below. ++ True ++ ++ ++ ++ 0 ++ 1 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Use built-in theme ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ True ++ True ++ end ++ center ++ ++ ++ 1 ++ 0 ++ 2 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ False ++ True ++ 0 ++ ++ ++ ++ ++ True ++ False ++ 0 ++ in ++ ++ ++ True ++ False ++ none ++ ++ ++ True ++ True ++ ++ ++ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ 32 ++ ++ ++ True ++ True ++ end ++ center ++ ++ ++ 1 ++ 0 ++ 2 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Save space reducing padding and border radius. ++ ++ ++ ++ 0 ++ 1 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Shrink the dash ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ ++ ++ ++ ++ True ++ True ++ ++ ++ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ 32 ++ ++ ++ True ++ False ++ True ++ 0 ++ Show a dot for each windows of the application. ++ ++ ++ ++ 0 ++ 1 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Show windows counter indicators ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ True ++ False ++ 6 ++ ++ ++ True ++ True ++ True ++ center ++ center ++ 0.46000000834465027 ++ ++ ++ True ++ False ++ emblem-system-symbolic ++ ++ ++ ++ ++ ++ False ++ True ++ 0 ++ ++ ++ ++ ++ True ++ True ++ end ++ center ++ ++ ++ False ++ True ++ 1 ++ ++ ++ ++ ++ 1 ++ 0 ++ 2 ++ ++ ++ ++ ++ ++ ++ ++ ++ True ++ True ++ ++ ++ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ 32 ++ ++ ++ True ++ False ++ True ++ 0 ++ Set the background color for the dash. ++ True ++ ++ ++ ++ 0 ++ 1 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Customize the dash color ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ True ++ False ++ 6 ++ ++ ++ True ++ True ++ True ++ center ++ center ++ 0.46000000834465027 ++ ++ ++ False ++ True ++ 1 ++ ++ ++ ++ ++ True ++ True ++ end ++ center ++ ++ ++ False ++ True ++ 1 ++ ++ ++ ++ ++ 1 ++ 0 ++ 2 ++ ++ ++ ++ ++ ++ ++ ++ ++ True ++ True ++ ++ ++ True ++ False ++ vertical ++ ++ ++ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ 32 ++ ++ ++ True ++ True ++ end ++ center ++ ++ ++ 1 ++ 0 ++ 2 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Tune the dash background opacity. ++ ++ ++ ++ 0 ++ 1 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Customize opacity ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ False ++ True ++ 0 ++ ++ ++ ++ ++ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ 32 ++ ++ ++ True ++ False ++ Opacity ++ ++ ++ False ++ True ++ 0 ++ ++ ++ ++ ++ True ++ True ++ custom_opacity_adjustement ++ on ++ False ++ 0 ++ 0 ++ 2 ++ right ++ ++ ++ ++ ++ True ++ True ++ 1 ++ ++ ++ ++ ++ False ++ True ++ 1 ++ ++ ++ ++ ++ ++ ++ ++ ++ 100 ++ 80 ++ True ++ True ++ ++ ++ True ++ False ++ 32 ++ ++ ++ True ++ False ++ center ++ 12 ++ Force straight corner ++ ++ 0 ++ ++ ++ True ++ True ++ 0 ++ ++ ++ ++ ++ True ++ True ++ center ++ 3 ++ ++ ++ False ++ True ++ 12 ++ 1 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ False ++ True ++ 1 ++ ++ ++ ++ ++ 3 ++ ++ ++ ++ ++ True ++ False ++ Appearance ++ ++ ++ 3 ++ False ++ ++ ++ ++ ++ False ++ 24 ++ 24 ++ True ++ True ++ vertical ++ 5 ++ ++ ++ ++ False ++ True ++ 10 ++ 0 ++ ++ ++ ++ ++ True ++ False ++ <b>Dash to Dock</b> ++ True ++ ++ ++ False ++ True ++ 1 ++ ++ ++ ++ ++ True ++ False ++ center ++ ++ ++ True ++ False ++ end ++ version: ++ ++ ++ False ++ True ++ 0 ++ ++ ++ ++ ++ True ++ False ++ start ++ ... ++ ++ ++ False ++ True ++ 1 ++ ++ ++ ++ ++ False ++ True ++ 2 ++ ++ ++ ++ ++ True ++ False ++ Moves the dash out of the overview transforming it in a dock ++ center ++ True ++ ++ ++ False ++ True ++ 3 ++ ++ ++ ++ ++ True ++ False ++ center ++ 5 ++ ++ ++ True ++ False ++ Created by ++ ++ ++ False ++ True ++ 0 ++ ++ ++ ++ ++ True ++ True ++ Michele (<a href="mailto:micxgx@gmail.com">micxgx@gmail.com</a>) ++ True ++ ++ ++ False ++ True ++ 1 ++ ++ ++ ++ ++ False ++ True ++ 4 ++ ++ ++ ++ ++ Webpage ++ True ++ True ++ True ++ ++ center ++ none ++ https://micheleg.github.io/dash-to-dock/ ++ ++ ++ False ++ True ++ 5 ++ ++ ++ ++ ++ True ++ True ++ end ++ <span size="small">This program comes with ABSOLUTELY NO WARRANTY. ++See the <a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GNU General Public License, version 2 or later</a> for details.</span> ++ True ++ center ++ True ++ ++ ++ True ++ True ++ 6 ++ ++ ++ ++ ++ 4 ++ ++ ++ ++ ++ True ++ False ++ About ++ ++ ++ 4 ++ False ++ ++ ++ ++ ++ 1000 ++ 50 ++ 250 ++ ++ ++ 1 ++ 0.050000000000000003 ++ 0.25 ++ ++ ++ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ vertical ++ ++ ++ True ++ False ++ 0 ++ in ++ ++ ++ True ++ False ++ none ++ ++ ++ True ++ True ++ ++ ++ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ 32 ++ ++ ++ True ++ False ++ True ++ 0 ++ Show the dock by mouse hover on the screen edge. ++ True ++ ++ ++ ++ 0 ++ 1 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Autohide ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ True ++ True ++ end ++ center ++ ++ ++ 1 ++ 0 ++ 2 ++ ++ ++ ++ ++ Push to show: require pressure to show the dock ++ True ++ True ++ False ++ 0 ++ True ++ ++ ++ 0 ++ 3 ++ 2 ++ ++ ++ ++ ++ Enable in fullscreen mode ++ True ++ True ++ False ++ 12 ++ 0 ++ True ++ ++ ++ 0 ++ 2 ++ 2 ++ ++ ++ ++ ++ ++ ++ ++ ++ True ++ True ++ ++ ++ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ 32 ++ ++ ++ True ++ False ++ True ++ 0 ++ Show the dock when it doesn't obstruct application windows. ++ True ++ ++ ++ ++ 0 ++ 1 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Dodge windows ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ True ++ True ++ end ++ center ++ ++ ++ 1 ++ 0 ++ 2 ++ ++ ++ ++ ++ True ++ False ++ vertical ++ ++ ++ All windows ++ True ++ True ++ False ++ 12 ++ 0 ++ True ++ True ++ ++ ++ ++ False ++ True ++ 0 ++ ++ ++ ++ ++ Only focused application's windows ++ True ++ True ++ False ++ 0 ++ True ++ True ++ all_windows_radio_button ++ ++ ++ ++ False ++ True ++ 1 ++ ++ ++ ++ ++ Only maximized windows ++ True ++ True ++ False ++ 0 ++ True ++ True ++ all_windows_radio_button ++ ++ ++ ++ False ++ True ++ 2 ++ ++ ++ ++ ++ 0 ++ 2 ++ 2 ++ ++ ++ ++ ++ ++ ++ ++ ++ True ++ True ++ ++ ++ True ++ False ++ 12 ++ 12 ++ 12 ++ 12 ++ True ++ 6 ++ 32 ++ ++ ++ True ++ True ++ end ++ animation_time_adjustment ++ 3 ++ ++ ++ 1 ++ 0 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Animation duration (s) ++ ++ ++ 0 ++ 0 ++ ++ ++ ++ ++ True ++ True ++ end ++ hide_timeout_adjustment ++ 3 ++ ++ ++ 1 ++ 1 ++ ++ ++ ++ ++ True ++ True ++ end ++ show_timeout_adjustment ++ 3 ++ ++ ++ 1 ++ 2 ++ ++ ++ ++ ++ True ++ True ++ 0.000 ++ pressure_threshold_adjustment ++ ++ ++ 1 ++ 3 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Hide timeout (s) ++ ++ ++ 0 ++ 1 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Show timeout (s) ++ ++ ++ 0 ++ 2 ++ ++ ++ ++ ++ True ++ False ++ True ++ 0 ++ Pressure threshold ++ ++ ++ 0 ++ 3 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ False ++ True ++ 0 ++ ++ ++ ++ +diff --git a/extensions/dash-to-dock/appIcons.js b/extensions/dash-to-dock/appIcons.js +new file mode 100644 +index 0000000..ac90585 +--- /dev/null ++++ b/extensions/dash-to-dock/appIcons.js +@@ -0,0 +1,1256 @@ ++// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- ++ ++const Clutter = imports.gi.Clutter; ++const Gio = imports.gi.Gio; ++const GLib = imports.gi.GLib; ++const Gtk = imports.gi.Gtk; ++const Signals = imports.signals; ++const Lang = imports.lang; ++const Meta = imports.gi.Meta; ++const Shell = imports.gi.Shell; ++const St = imports.gi.St; ++const Mainloop = imports.mainloop; ++ ++// Use __ () and N__() for the extension gettext domain, and reuse ++// the shell domain with the default _() and N_() ++const Gettext = imports.gettext.domain('dashtodock'); ++const __ = Gettext.gettext; ++const N__ = function(e) { return e }; ++ ++const AppDisplay = imports.ui.appDisplay; ++const AppFavorites = imports.ui.appFavorites; ++const Dash = imports.ui.dash; ++const DND = imports.ui.dnd; ++const IconGrid = imports.ui.iconGrid; ++const Main = imports.ui.main; ++const PopupMenu = imports.ui.popupMenu; ++const Tweener = imports.ui.tweener; ++const Util = imports.misc.util; ++const Workspace = imports.ui.workspace; ++ ++const Me = imports.misc.extensionUtils.getCurrentExtension(); ++const Utils = Me.imports.utils; ++const WindowPreview = Me.imports.windowPreview; ++ ++let tracker = Shell.WindowTracker.get_default(); ++ ++let DASH_ITEM_LABEL_SHOW_TIME = Dash.DASH_ITEM_LABEL_SHOW_TIME; ++ ++const clickAction = { ++ SKIP: 0, ++ MINIMIZE: 1, ++ LAUNCH: 2, ++ CYCLE_WINDOWS: 3, ++ MINIMIZE_OR_OVERVIEW: 4, ++ PREVIEWS: 5, ++ QUIT: 6 ++}; ++ ++const scrollAction = { ++ DO_NOTHING: 0, ++ CYCLE_WINDOWS: 1, ++ SWITCH_WORKSPACE: 2 ++}; ++ ++let recentlyClickedAppLoopId = 0; ++let recentlyClickedApp = null; ++let recentlyClickedAppWindows = null; ++let recentlyClickedAppIndex = 0; ++let recentlyClickedAppMonitor = -1; ++ ++/** ++ * Extend AppIcon ++ * ++ * - Pass settings to the constructor and bind settings changes ++ * - Apply a css class based on the number of windows of each application (#N); ++ * - Draw a dot for each window of the application based on the default "dot" style which is hidden (#N); ++ * a class of the form "running#N" is applied to the AppWellIcon actor. ++ * like the original .running one. ++ * - Add a .focused style to the focused app ++ * - Customize click actions. ++ * - Update minimization animation target ++ * - Update menu if open on windows change ++ */ ++const MyAppIcon = new Lang.Class({ ++ Name: 'DashToDock.AppIcon', ++ Extends: AppDisplay.AppIcon, ++ ++ // settings are required inside. ++ _init: function(settings, app, monitorIndex, iconParams) { ++ // a prefix is required to avoid conflicting with the parent class variable ++ this._dtdSettings = settings; ++ this.monitorIndex = monitorIndex; ++ this._signalsHandler = new Utils.GlobalSignalsHandler(); ++ this._nWindows = 0; ++ ++ this.parent(app, iconParams); ++ ++ // Monitor windows-changes instead of app state. ++ // Keep using the same Id and function callback (that is extended) ++ if (this._stateChangedId > 0) { ++ this.app.disconnect(this._stateChangedId); ++ this._stateChangedId = 0; ++ } ++ ++ this._stateChangedId = this.app.connect('windows-changed', ++ Lang.bind(this, ++ this.onWindowsChanged)); ++ this._focusAppChangeId = tracker.connect('notify::focus-app', ++ Lang.bind(this, ++ this._onFocusAppChanged)); ++ this._enteredMonitorId = global.screen.connect('window-entered-monitor', ++ Lang.bind(this, ++ this.onWindowsChanged)); ++ ++ this._dots = null; ++ ++ let keys = ['apply-custom-theme', ++ 'custom-theme-running-dots', ++ 'custom-theme-customize-running-dots', ++ 'custom-theme-running-dots-color', ++ 'custom-theme-running-dots-border-color', ++ 'custom-theme-running-dots-border-width']; ++ ++ keys.forEach(function(key) { ++ this._signalsHandler.add([ ++ this._dtdSettings, ++ 'changed::' + key, ++ Lang.bind(this, this._toggleDots) ++ ]); ++ }, this); ++ ++ this._toggleDots(); ++ ++ this._dtdSettings.connect('changed::scroll-action', Lang.bind(this, function() { ++ this._optionalScrollCycleWindows(); ++ })); ++ this._optionalScrollCycleWindows(); ++ ++ this._numberOverlay(); ++ ++ this._previewMenuManager = null; ++ this._previewMenu = null; ++ }, ++ ++ _onDestroy: function() { ++ this.parent(); ++ ++ // This is necessary due to an upstream bug ++ // https://bugzilla.gnome.org/show_bug.cgi?id=757556 ++ // It can be safely removed once it get solved upstrea. ++ if (this._menu) ++ this._menu.close(false); ++ ++ // Disconect global signals ++ // stateChangedId is already handled by parent) ++ if (this._focusAppChangeId > 0) { ++ tracker.disconnect(this._focusAppChangeId); ++ this._focusAppChangeId = 0; ++ } ++ ++ if (this._enteredMonitorId > 0) { ++ global.screen.disconnect(this._enteredMonitorId); ++ this._enteredMonitorId = 0; ++ } ++ ++ this._signalsHandler.destroy(); ++ ++ if (this._scrollEventHandler) ++ this.actor.disconnect(this._scrollEventHandler); ++ }, ++ ++ _optionalScrollCycleWindows: function() { ++ if (this._scrollEventHandler) { ++ this.actor.disconnect(this._scrollEventHandler); ++ this._scrollEventHandler = 0; ++ } ++ ++ let isEnabled = this._dtdSettings.get_enum('scroll-action') === scrollAction.CYCLE_WINDOWS; ++ if (!isEnabled) return; ++ ++ this._scrollEventHandler = this.actor.connect('scroll-event', Lang.bind(this, ++ this.onScrollEvent)); ++ }, ++ ++ onScrollEvent: function(actor, event) { ++ ++ // We only activate windows of running applications, i.e. we never open new windows ++ // We check if the app is running, and that the # of windows is > 0 in ++ // case we use workspace isolation, ++ let appIsRunning = this.app.state == Shell.AppState.RUNNING ++ && this.getInterestingWindows().length > 0; ++ ++ if (!appIsRunning) ++ return false ++ ++ if (this._optionalScrollCycleWindowsDeadTimeId > 0) ++ return false; ++ else ++ this._optionalScrollCycleWindowsDeadTimeId = Mainloop.timeout_add(250, Lang.bind(this, function() { ++ this._optionalScrollCycleWindowsDeadTimeId = 0; ++ })); ++ ++ let direction = null; ++ ++ switch (event.get_scroll_direction()) { ++ case Clutter.ScrollDirection.UP: ++ direction = Meta.MotionDirection.UP; ++ break; ++ case Clutter.ScrollDirection.DOWN: ++ direction = Meta.MotionDirection.DOWN; ++ break; ++ case Clutter.ScrollDirection.SMOOTH: ++ let [dx, dy] = event.get_scroll_delta(); ++ if (dy < 0) ++ direction = Meta.MotionDirection.UP; ++ else if (dy > 0) ++ direction = Meta.MotionDirection.DOWN; ++ break; ++ } ++ ++ let focusedApp = tracker.focus_app; ++ if (!Main.overview._shown) { ++ let reversed = direction === Meta.MotionDirection.UP; ++ if (this.app == focusedApp) ++ this._cycleThroughWindows(reversed); ++ else { ++ // Activate the first window ++ let windows = this.getInterestingWindows(); ++ if (windows.length > 0) { ++ let w = windows[0]; ++ Main.activateWindow(w); ++ } ++ } ++ } ++ else ++ this.app.activate(); ++ return true; ++ }, ++ ++ onWindowsChanged: function() { ++ ++ if (this._menu && this._menu.isOpen) ++ this._menu.update(); ++ ++ this._updateRunningStyle(); ++ this.updateIconGeometry(); ++ }, ++ ++ /** ++ * Update taraget for minimization animation ++ */ ++ updateIconGeometry: function() { ++ // If (for unknown reason) the actor is not on the stage the reported size ++ // and position are random values, which might exceeds the integer range ++ // resulting in an error when assigned to the a rect. This is a more like ++ // a workaround to prevent flooding the system with errors. ++ if (this.actor.get_stage() == null) ++ return; ++ ++ let rect = new Meta.Rectangle(); ++ ++ [rect.x, rect.y] = this.actor.get_transformed_position(); ++ [rect.width, rect.height] = this.actor.get_transformed_size(); ++ ++ let windows = this.app.get_windows(); ++ if (this._dtdSettings.get_boolean('multi-monitor')){ ++ let monitorIndex = this.monitorIndex; ++ windows = windows.filter(function(w) { ++ return w.get_monitor() == monitorIndex; ++ }); ++ } ++ windows.forEach(function(w) { ++ w.set_icon_geometry(rect); ++ }); ++ }, ++ ++ _toggleDots: function() { ++ if (this._dtdSettings.get_boolean('custom-theme-running-dots') || this._dtdSettings.get_boolean('apply-custom-theme')) ++ this._showDots(); ++ else ++ this._hideDots(); ++ }, ++ ++ _showDots: function() { ++ // I use opacity to hide the default dot because the show/hide function ++ // are used by the parent class. ++ this._dot.opacity = 0; ++ ++ // Just update style if dots already exist ++ if (this._dots) { ++ this._updateCounterClass(); ++ return; ++ } ++ ++ this._dots = new St.DrawingArea({x_expand: true, y_expand: true}); ++ this._dots.connect('repaint', Lang.bind(this, function() { ++ this._drawCircles(this._dots, Utils.getPosition(this._dtdSettings)); ++ })); ++ this._iconContainer.add_child(this._dots); ++ this._updateCounterClass(); ++ }, ++ ++ _hideDots: function() { ++ this._dot.opacity = 255; ++ if (this._dots) ++ this._dots.destroy() ++ this._dots = null; ++ }, ++ ++ _updateRunningStyle: function() { ++ // When using workspace isolation, we need to hide the dots of apps with ++ // no windows in the current workspace ++ if (this._dtdSettings.get_boolean('isolate-workspaces') || ++ this._dtdSettings.get_boolean('isolate-monitors')) { ++ if (this.app.state != Shell.AppState.STOPPED ++ && this.getInterestingWindows().length != 0) ++ this._dot.show(); ++ else ++ this._dot.hide(); ++ } ++ else ++ this.parent(); ++ this._onFocusAppChanged(); ++ this._updateCounterClass(); ++ }, ++ ++ popupMenu: function() { ++ this._removeMenuTimeout(); ++ this.actor.fake_release(); ++ this._draggable.fakeRelease(); ++ ++ if (!this._menu) { ++ this._menu = new MyAppIconMenu(this, this._dtdSettings); ++ this._menu.connect('activate-window', Lang.bind(this, function(menu, window) { ++ this.activateWindow(window); ++ })); ++ this._menu.connect('open-state-changed', Lang.bind(this, function(menu, isPoppedUp) { ++ if (!isPoppedUp) ++ this._onMenuPoppedDown(); ++ else { ++ // Setting the max-height is s useful if part of the menu is ++ // scrollable so the minimum height is smaller than the natural height. ++ let monitor_index = Main.layoutManager.findIndexForActor(this.actor); ++ let workArea = Main.layoutManager.getWorkAreaForMonitor(monitor_index); ++ let position = Utils.getPosition(this._dtdSettings); ++ this._isHorizontal = ( position == St.Side.TOP || ++ position == St.Side.BOTTOM); ++ // If horizontal also remove the height of the dash ++ let additional_margin = this._isHorizontal && !this._dtdSettings.get_boolean('dock-fixed') ? Main.overview._dash.actor.height : 0; ++ let verticalMargins = this._menu.actor.margin_top + this._menu.actor.margin_bottom; ++ // Also set a max width to the menu, so long labels (long windows title) get truncated ++ this._menu.actor.style = ('max-height: ' + Math.round(workArea.height - additional_margin - verticalMargins) + 'px;' + ++ 'max-width: 400px'); ++ } ++ })); ++ let id = Main.overview.connect('hiding', Lang.bind(this, function() { ++ this._menu.close(); ++ })); ++ this._menu.actor.connect('destroy', function() { ++ Main.overview.disconnect(id); ++ }); ++ ++ this._menuManager.addMenu(this._menu); ++ } ++ ++ this.emit('menu-state-changed', true); ++ ++ this.actor.set_hover(true); ++ this._menu.popup(); ++ this._menuManager.ignoreRelease(); ++ this.emit('sync-tooltip'); ++ ++ return false; ++ }, ++ ++ _onFocusAppChanged: function() { ++ // We need to check the number of windows, as the focus might be ++ // happening on another monitor if using isolation ++ if (tracker.focus_app == this.app && this.getInterestingWindows().length != 0) ++ this.actor.add_style_class_name('focused'); ++ else ++ this.actor.remove_style_class_name('focused'); ++ }, ++ ++ activate: function(button) { ++ ++ if (!this._dtdSettings.get_boolean('customize-click')) { ++ this.parent(button); ++ return; ++ } ++ ++ let event = Clutter.get_current_event(); ++ let modifiers = event ? event.get_state() : 0; ++ let focusedApp = tracker.focus_app; ++ ++ // Only consider SHIFT and CONTROL as modifiers (exclude SUPER, CAPS-LOCK, etc.) ++ modifiers = modifiers & (Clutter.ModifierType.SHIFT_MASK | Clutter.ModifierType.CONTROL_MASK); ++ ++ // We don't change the CTRL-click behaviour: in such case we just chain ++ // up the parent method and return. ++ if (modifiers & Clutter.ModifierType.CONTROL_MASK) { ++ // Keep default behaviour: launch new window ++ // By calling the parent method I make it compatible ++ // with other extensions tweaking ctrl + click ++ this.parent(button); ++ return; ++ } ++ ++ // We check what type of click we have and if the modifier SHIFT is ++ // being used. We then define what buttonAction should be for this ++ // event. ++ let buttonAction = 0; ++ if (button && button == 2 ) { ++ if (modifiers & Clutter.ModifierType.SHIFT_MASK) ++ buttonAction = this._dtdSettings.get_enum('shift-middle-click-action'); ++ else ++ buttonAction = this._dtdSettings.get_enum('middle-click-action'); ++ } ++ else if (button && button == 1) { ++ if (modifiers & Clutter.ModifierType.SHIFT_MASK) ++ buttonAction = this._dtdSettings.get_enum('shift-click-action'); ++ else ++ buttonAction = this._dtdSettings.get_enum('click-action'); ++ } ++ ++ // We check if the app is running, and that the # of windows is > 0 in ++ // case we use workspace isolation. ++ let windows = this.getInterestingWindows(); ++ let appIsRunning = this.app.state == Shell.AppState.RUNNING ++ && windows.length > 0; ++ ++ // Some action modes (e.g. MINIMIZE_OR_OVERVIEW) require overview to remain open ++ // This variable keeps track of this ++ let shouldHideOverview = true; ++ ++ // We customize the action only when the application is already running ++ if (appIsRunning) { ++ switch (buttonAction) { ++ case clickAction.MINIMIZE: ++ // In overview just activate the app, unless the acion is explicitely ++ // requested with a keyboard modifier ++ if (!Main.overview._shown || modifiers){ ++ // If we have button=2 or a modifier, allow minimization even if ++ // the app is not focused ++ if (this.app == focusedApp || button == 2 || modifiers & Clutter.ModifierType.SHIFT_MASK) { ++ // minimize all windows on double click and always in the case of primary click without ++ // additional modifiers ++ let click_count = 0; ++ if (Clutter.EventType.CLUTTER_BUTTON_PRESS) ++ click_count = event.get_click_count(); ++ let all_windows = (button == 1 && ! modifiers) || click_count > 1; ++ this._minimizeWindow(all_windows); ++ } ++ else ++ this._activateAllWindows(); ++ } ++ else { ++ let w = windows[0]; ++ Main.activateWindow(w); ++ } ++ break; ++ ++ case clickAction.MINIMIZE_OR_OVERVIEW: ++ // When a single window is present, toggle minimization ++ // If only one windows is present toggle minimization, but only when trigggered with the ++ // simple click action (no modifiers, no middle click). ++ if (windows.length == 1 && !modifiers && button == 1) { ++ let w = windows[0]; ++ if (this.app == focusedApp) { ++ // Window is raised, minimize it ++ this._minimizeWindow(w); ++ } else { ++ // Window is minimized, raise it ++ Main.activateWindow(w); ++ } ++ // Launch overview when multiple windows are present ++ // TODO: only show current app windows when gnome shell API will allow it ++ } else { ++ shouldHideOverview = false; ++ Main.overview.toggle(); ++ } ++ break; ++ ++ case clickAction.CYCLE_WINDOWS: ++ if (!Main.overview._shown){ ++ if (this.app == focusedApp) ++ this._cycleThroughWindows(); ++ else { ++ // Activate the first window ++ let w = windows[0]; ++ Main.activateWindow(w); ++ } ++ } ++ else ++ this.app.activate(); ++ break; ++ ++ case clickAction.LAUNCH: ++ this.launchNewWindow(); ++ break; ++ ++ case clickAction.PREVIEWS: ++ if (!Main.overview._shown) { ++ // If only one windows is present just switch to it, but only when trigggered with the ++ // simple click action (no modifiers, no middle click). ++ if (windows.length == 1 && !modifiers && button == 1) { ++ let w = windows[0]; ++ Main.activateWindow(w); ++ } else ++ this._windowPreviews(); ++ } ++ else { ++ this.app.activate(); ++ } ++ break; ++ ++ case clickAction.QUIT: ++ this.closeAllWindows(); ++ break; ++ ++ case clickAction.SKIP: ++ let w = windows[0]; ++ Main.activateWindow(w); ++ break; ++ } ++ } ++ else { ++ this.launchNewWindow(); ++ } ++ ++ // Hide overview except when action mode requires it ++ if(shouldHideOverview) { ++ Main.overview.hide(); ++ } ++ }, ++ ++ shouldShowTooltip: function() { ++ return this.actor.hover && (!this._menu || !this._menu.isOpen) && ++ (!this._previewMenu || !this._previewMenu.isOpen); ++ }, ++ ++ _windowPreviews: function() { ++ if (!this._previewMenu) { ++ this._previewMenuManager = new PopupMenu.PopupMenuManager(this); ++ ++ this._previewMenu = new WindowPreview.WindowPreviewMenu(this, this._dtdSettings); ++ ++ this._previewMenuManager.addMenu(this._previewMenu); ++ ++ this._previewMenu.connect('open-state-changed', Lang.bind(this, function(menu, isPoppedUp) { ++ if (!isPoppedUp) ++ this._onMenuPoppedDown(); ++ })); ++ let id = Main.overview.connect('hiding', Lang.bind(this, function() { ++ this._previewMenu.close(); ++ })); ++ this._previewMenu.actor.connect('destroy', function() { ++ Main.overview.disconnect(id); ++ }); ++ ++ } ++ ++ if (this._previewMenu.isOpen) ++ this._previewMenu.close(); ++ else ++ this._previewMenu.popup(); ++ ++ return false; ++ }, ++ ++ // Try to do the right thing when attempting to launch a new window of an app. In ++ // particular, if the application doens't allow to launch a new window, activate ++ // the existing window instead. ++ launchNewWindow: function(p) { ++ let appInfo = this.app.get_app_info(); ++ let actions = appInfo.list_actions(); ++ if (this.app.can_open_new_window()) { ++ this.animateLaunch(); ++ // This is used as a workaround for a bug resulting in no new windows being opened ++ // for certain running applications when calling open_new_window(). ++ // ++ // https://bugzilla.gnome.org/show_bug.cgi?id=756844 ++ // ++ // Similar to what done when generating the popupMenu entries, if the application provides ++ // a "New Window" action, use it instead of directly requesting a new window with ++ // open_new_window(), which fails for certain application, notably Nautilus. ++ if (actions.indexOf('new-window') == -1) { ++ this.app.open_new_window(-1); ++ } ++ else { ++ let i = actions.indexOf('new-window'); ++ if (i !== -1) ++ this.app.launch_action(actions[i], global.get_current_time(), -1); ++ } ++ } ++ else { ++ // Try to manually activate the first window. Otherwise, when the app is activated by ++ // switching to a different workspace, a launch spinning icon is shown and disappers only ++ // after a timeout. ++ let windows = this.app.get_windows(); ++ if (windows.length > 0) ++ Main.activateWindow(windows[0]) ++ else ++ this.app.activate(); ++ } ++ }, ++ ++ _updateCounterClass: function() { ++ let maxN = 4; ++ this._nWindows = Math.min(this.getInterestingWindows().length, maxN); ++ ++ for (let i = 1; i <= maxN; i++) { ++ let className = 'running' + i; ++ if (i != this._nWindows) ++ this.actor.remove_style_class_name(className); ++ else ++ this.actor.add_style_class_name(className); ++ } ++ ++ if (this._dots) ++ this._dots.queue_repaint(); ++ }, ++ ++ _drawCircles: function(area, side) { ++ let borderColor, borderWidth, bodyColor; ++ ++ if (!this._dtdSettings.get_boolean('apply-custom-theme') ++ && this._dtdSettings.get_boolean('custom-theme-running-dots') ++ && this._dtdSettings.get_boolean('custom-theme-customize-running-dots')) { ++ borderColor = Clutter.color_from_string(this._dtdSettings.get_string('custom-theme-running-dots-border-color'))[1]; ++ borderWidth = this._dtdSettings.get_int('custom-theme-running-dots-border-width'); ++ bodyColor = Clutter.color_from_string(this._dtdSettings.get_string('custom-theme-running-dots-color'))[1]; ++ } ++ else { ++ // Re-use the style - background color, and border width and color - ++ // of the default dot ++ let themeNode = this._dot.get_theme_node(); ++ borderColor = themeNode.get_border_color(side); ++ borderWidth = themeNode.get_border_width(side); ++ bodyColor = themeNode.get_background_color(); ++ } ++ ++ let [width, height] = area.get_surface_size(); ++ let cr = area.get_context(); ++ ++ // Draw the required numbers of dots ++ // Define the radius as an arbitrary size, but keep large enough to account ++ // for the drawing of the border. ++ let radius = Math.max(width/22, borderWidth/2); ++ let padding = 0; // distance from the margin ++ let spacing = radius + borderWidth; // separation between the dots ++ let n = this._nWindows; ++ ++ cr.setLineWidth(borderWidth); ++ Clutter.cairo_set_source_color(cr, borderColor); ++ ++ switch (side) { ++ case St.Side.TOP: ++ cr.translate((width - (2*n)*radius - (n-1)*spacing)/2, padding); ++ for (let i = 0; i < n; i++) { ++ cr.newSubPath(); ++ cr.arc((2*i+1)*radius + i*spacing, radius + borderWidth/2, radius, 0, 2*Math.PI); ++ } ++ break; ++ ++ case St.Side.BOTTOM: ++ cr.translate((width - (2*n)*radius - (n-1)*spacing)/2, height - padding); ++ for (let i = 0; i < n; i++) { ++ cr.newSubPath(); ++ cr.arc((2*i+1)*radius + i*spacing, -radius - borderWidth/2, radius, 0, 2*Math.PI); ++ } ++ break; ++ ++ case St.Side.LEFT: ++ cr.translate(padding, (height - (2*n)*radius - (n-1)*spacing)/2); ++ for (let i = 0; i < n; i++) { ++ cr.newSubPath(); ++ cr.arc(radius + borderWidth/2, (2*i+1)*radius + i*spacing, radius, 0, 2*Math.PI); ++ } ++ break; ++ ++ case St.Side.RIGHT: ++ cr.translate(width - padding , (height - (2*n)*radius - (n-1)*spacing)/2); ++ for (let i = 0; i < n; i++) { ++ cr.newSubPath(); ++ cr.arc(-radius - borderWidth/2, (2*i+1)*radius + i*spacing, radius, 0, 2*Math.PI); ++ } ++ break; ++ } ++ ++ cr.strokePreserve(); ++ ++ Clutter.cairo_set_source_color(cr, bodyColor); ++ cr.fill(); ++ cr.$dispose(); ++ }, ++ ++ _numberOverlay: function() { ++ // Add label for a Hot-Key visual aid ++ this._numberOverlayLabel = new St.Label(); ++ this._numberOverlayBin = new St.Bin({ ++ child: this._numberOverlayLabel, ++ x_align: St.Align.START, y_align: St.Align.START, ++ x_expand: true, y_expand: true ++ }); ++ this._numberOverlayLabel.add_style_class_name('number-overlay'); ++ this._numberOverlayOrder = -1; ++ this._numberOverlayBin.hide(); ++ ++ this._iconContainer.add_child(this._numberOverlayBin); ++ ++ }, ++ ++ updateNumberOverlay: function() { ++ // We apply an overall scale factor that might come from a HiDPI monitor. ++ // Clutter dimensions are in physical pixels, but CSS measures are in logical ++ // pixels, so make sure to consider the scale. ++ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; ++ // Set the font size to something smaller than the whole icon so it is ++ // still visible. The border radius is large to make the shape circular ++ let [minWidth, natWidth] = this._iconContainer.get_preferred_width(-1); ++ let font_size = Math.round(Math.max(12, 0.3*natWidth) / scaleFactor); ++ let size = Math.round(font_size*1.2); ++ this._numberOverlayLabel.set_style( ++ 'font-size: ' + font_size + 'px;' + ++ 'border-radius: ' + this.icon.iconSize + 'px;' + ++ 'width: ' + size + 'px; height: ' + size +'px;' ++ ); ++ }, ++ ++ setNumberOverlay: function(number) { ++ this._numberOverlayOrder = number; ++ this._numberOverlayLabel.set_text(number.toString()); ++ }, ++ ++ toggleNumberOverlay: function(activate) { ++ if (activate && this._numberOverlayOrder > -1) { ++ this.updateNumberOverlay(); ++ this._numberOverlayBin.show(); ++ } ++ else ++ this._numberOverlayBin.hide(); ++ }, ++ ++ _minimizeWindow: function(param) { ++ // Param true make all app windows minimize ++ let windows = this.getInterestingWindows(); ++ let current_workspace = global.screen.get_active_workspace(); ++ for (let i = 0; i < windows.length; i++) { ++ let w = windows[i]; ++ if (w.get_workspace() == current_workspace && w.showing_on_its_workspace()) { ++ w.minimize(); ++ // Just minimize one window. By specification it should be the ++ // focused window on the current workspace. ++ if(!param) ++ break; ++ } ++ } ++ }, ++ ++ // By default only non minimized windows are activated. ++ // This activates all windows in the current workspace. ++ _activateAllWindows: function() { ++ // First activate first window so workspace is switched if needed. ++ // We don't do this if isolation is on! ++ if (!this._dtdSettings.get_boolean('isolate-workspaces') && ++ !this._dtdSettings.get_boolean('isolate-monitors')) ++ this.app.activate(); ++ ++ // then activate all other app windows in the current workspace ++ let windows = this.getInterestingWindows(); ++ let activeWorkspace = global.screen.get_active_workspace_index(); ++ ++ if (windows.length <= 0) ++ return; ++ ++ let activatedWindows = 0; ++ ++ for (let i = windows.length - 1; i >= 0; i--) { ++ if (windows[i].get_workspace().index() == activeWorkspace) { ++ Main.activateWindow(windows[i]); ++ activatedWindows++; ++ } ++ } ++ }, ++ ++ //This closes all windows of the app. ++ closeAllWindows: function() { ++ let windows = this.getInterestingWindows(); ++ for (let i = 0; i < windows.length; i++) ++ windows[i].delete(global.get_current_time()); ++ }, ++ ++ _cycleThroughWindows: function(reversed) { ++ // Store for a little amount of time last clicked app and its windows ++ // since the order changes upon window interaction ++ let MEMORY_TIME=3000; ++ ++ let app_windows = this.getInterestingWindows(); ++ ++ if (app_windows.length <1) ++ return ++ ++ if (recentlyClickedAppLoopId > 0) ++ Mainloop.source_remove(recentlyClickedAppLoopId); ++ recentlyClickedAppLoopId = Mainloop.timeout_add(MEMORY_TIME, this._resetRecentlyClickedApp); ++ ++ // If there isn't already a list of windows for the current app, ++ // or the stored list is outdated, use the current windows list. ++ let monitorIsolation = this._dtdSettings.get_boolean('isolate-monitors'); ++ if (!recentlyClickedApp || ++ recentlyClickedApp.get_id() != this.app.get_id() || ++ recentlyClickedAppWindows.length != app_windows.length || ++ (recentlyClickedAppMonitor != this.monitorIndex && monitorIsolation)) { ++ recentlyClickedApp = this.app; ++ recentlyClickedAppWindows = app_windows; ++ recentlyClickedAppMonitor = this.monitorIndex; ++ recentlyClickedAppIndex = 0; ++ } ++ ++ if (reversed) { ++ recentlyClickedAppIndex--; ++ if (recentlyClickedAppIndex < 0) recentlyClickedAppIndex = recentlyClickedAppWindows.length - 1; ++ } else { ++ recentlyClickedAppIndex++; ++ } ++ let index = recentlyClickedAppIndex % recentlyClickedAppWindows.length; ++ let window = recentlyClickedAppWindows[index]; ++ ++ Main.activateWindow(window); ++ }, ++ ++ _resetRecentlyClickedApp: function() { ++ if (recentlyClickedAppLoopId > 0) ++ Mainloop.source_remove(recentlyClickedAppLoopId); ++ recentlyClickedAppLoopId=0; ++ recentlyClickedApp =null; ++ recentlyClickedAppWindows = null; ++ recentlyClickedAppIndex = 0; ++ recentlyClickedAppMonitor = -1; ++ ++ return false; ++ }, ++ ++ // Filter out unnecessary windows, for instance ++ // nautilus desktop window. ++ getInterestingWindows: function() { ++ return getInterestingWindows(this.app, this._dtdSettings, this.monitorIndex); ++ } ++}); ++/** ++ * Extend AppIconMenu ++ * ++ * - Pass settings to the constructor ++ * - set popup arrow side based on dash orientation ++ * - Add close windows option based on quitfromdash extension ++ * (https://github.com/deuill/shell-extension-quitfromdash) ++ * - Add open windows thumbnails instead of list ++ * - update menu when application windows change ++ */ ++const MyAppIconMenu = new Lang.Class({ ++ Name: 'DashToDock.MyAppIconMenu', ++ Extends: AppDisplay.AppIconMenu, ++ ++ _init: function(source, settings) { ++ let side = Utils.getPosition(settings); ++ ++ // Damm it, there has to be a proper way of doing this... ++ // As I can't call the parent parent constructor (?) passing the side ++ // parameter, I overwite what I need later ++ this.parent(source); ++ ++ // Change the initialized side where required. ++ this._arrowSide = side; ++ this._boxPointer._arrowSide = side; ++ this._boxPointer._userArrowSide = side; ++ ++ this._dtdSettings = settings; ++ }, ++ ++ _redisplay: function() { ++ this.removeAll(); ++ ++ if (this._dtdSettings.get_boolean('show-windows-preview')) { ++ // Display the app windows menu items and the separator between windows ++ // of the current desktop and other windows. ++ ++ this._allWindowsMenuItem = new PopupMenu.PopupSubMenuMenuItem(__('All Windows'), false); ++ this._allWindowsMenuItem.actor.hide(); ++ this.addMenuItem(this._allWindowsMenuItem); ++ ++ if (!this._source.app.is_window_backed()) { ++ this._appendSeparator(); ++ ++ let appInfo = this._source.app.get_app_info(); ++ let actions = appInfo.list_actions(); ++ if (this._source.app.can_open_new_window() && ++ actions.indexOf('new-window') == -1) { ++ this._newWindowMenuItem = this._appendMenuItem(_("New Window")); ++ this._newWindowMenuItem.connect('activate', Lang.bind(this, function() { ++ if (this._source.app.state == Shell.AppState.STOPPED) ++ this._source.animateLaunch(); ++ ++ this._source.app.open_new_window(-1); ++ this.emit('activate-window', null); ++ })); ++ this._appendSeparator(); ++ } ++ ++ ++ if (AppDisplay.discreteGpuAvailable && ++ this._source.app.state == Shell.AppState.STOPPED && ++ actions.indexOf('activate-discrete-gpu') == -1) { ++ this._onDiscreteGpuMenuItem = this._appendMenuItem(_("Launch using Dedicated Graphics Card")); ++ this._onDiscreteGpuMenuItem.connect('activate', Lang.bind(this, function() { ++ if (this._source.app.state == Shell.AppState.STOPPED) ++ this._source.animateLaunch(); ++ ++ this._source.app.launch(0, -1, true); ++ this.emit('activate-window', null); ++ })); ++ } ++ ++ for (let i = 0; i < actions.length; i++) { ++ let action = actions[i]; ++ let item = this._appendMenuItem(appInfo.get_action_name(action)); ++ item.connect('activate', Lang.bind(this, function(emitter, event) { ++ this._source.app.launch_action(action, event.get_time(), -1); ++ this.emit('activate-window', null); ++ })); ++ } ++ ++ let canFavorite = global.settings.is_writable('favorite-apps'); ++ ++ if (canFavorite) { ++ this._appendSeparator(); ++ ++ let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id()); ++ ++ if (isFavorite) { ++ let item = this._appendMenuItem(_("Remove from Favorites")); ++ item.connect('activate', Lang.bind(this, function() { ++ let favs = AppFavorites.getAppFavorites(); ++ favs.removeFavorite(this._source.app.get_id()); ++ })); ++ } else { ++ let item = this._appendMenuItem(_("Add to Favorites")); ++ item.connect('activate', Lang.bind(this, function() { ++ let favs = AppFavorites.getAppFavorites(); ++ favs.addFavorite(this._source.app.get_id()); ++ })); ++ } ++ } ++ ++ if (Shell.AppSystem.get_default().lookup_app('org.gnome.Software.desktop')) { ++ this._appendSeparator(); ++ let item = this._appendMenuItem(_("Show Details")); ++ item.connect('activate', Lang.bind(this, function() { ++ let id = this._source.app.get_id(); ++ let args = GLib.Variant.new('(ss)', [id, '']); ++ Gio.DBus.get(Gio.BusType.SESSION, null, ++ function(o, res) { ++ let bus = Gio.DBus.get_finish(res); ++ bus.call('org.gnome.Software', ++ '/org/gnome/Software', ++ 'org.gtk.Actions', 'Activate', ++ GLib.Variant.new('(sava{sv})', ++ ['details', [args], null]), ++ null, 0, -1, null, null); ++ Main.overview.hide(); ++ }); ++ })); ++ } ++ } ++ ++ } else { ++ this.parent(); ++ } ++ ++ // quit menu ++ this._appendSeparator(); ++ this._quitfromDashMenuItem = this._appendMenuItem(_("Quit")); ++ this._quitfromDashMenuItem.connect('activate', Lang.bind(this, function() { ++ this._source.closeAllWindows(); ++ })); ++ ++ this.update(); ++ }, ++ ++ // update menu content when application windows change. This is desirable as actions ++ // acting on windows (closing) are performed while the menu is shown. ++ update: function() { ++ ++ if(this._dtdSettings.get_boolean('show-windows-preview')){ ++ ++ let windows = this._source.getInterestingWindows(); ++ ++ // update, show or hide the quit menu ++ if ( windows.length > 0) { ++ let quitFromDashMenuText = ""; ++ if (windows.length == 1) ++ this._quitfromDashMenuItem.label.set_text(_("Quit")); ++ else ++ this._quitfromDashMenuItem.label.set_text(_("Quit") + ' ' + windows.length + ' ' + _("Windows")); ++ ++ this._quitfromDashMenuItem.actor.show(); ++ ++ } else { ++ this._quitfromDashMenuItem.actor.hide(); ++ } ++ ++ // update, show, or hide the allWindows menu ++ // Check if there are new windows not already displayed. In such case, repopulate the allWindows ++ // menu. Windows removal is already handled by each preview being connected to the destroy signal ++ let old_windows = this._allWindowsMenuItem.menu._getMenuItems().map(function(item){ ++ return item._window; ++ }); ++ ++ let new_windows = windows.filter(function(w) {return old_windows.indexOf(w) < 0;}); ++ if (new_windows.length > 0) { ++ this._populateAllWindowMenu(windows); ++ ++ // Try to set the width to that of the submenu. ++ // TODO: can't get the actual size, getting a bit less. ++ // Temporary workaround: add 15px to compensate ++ this._allWindowsMenuItem.actor.width = this._allWindowsMenuItem.menu.actor.width + 15; ++ ++ } ++ ++ // The menu is created hidden and never hidded after being shown. Instead, a singlal ++ // connected to its items destroy will set is insensitive if no more windows preview are shown. ++ if (windows.length > 0){ ++ this._allWindowsMenuItem.actor.show(); ++ this._allWindowsMenuItem.setSensitive(true); ++ } ++ ++ // Update separators ++ this._getMenuItems().forEach(Lang.bind(this, this._updateSeparatorVisibility)); ++ } ++ ++ ++ }, ++ ++ _populateAllWindowMenu: function(windows) { ++ ++ this._allWindowsMenuItem.menu.removeAll(); ++ ++ if (windows.length > 0) { ++ ++ let activeWorkspace = global.screen.get_active_workspace(); ++ let separatorShown = windows[0].get_workspace() != activeWorkspace; ++ ++ for (let i = 0; i < windows.length; i++) { ++ let window = windows[i]; ++ if (!separatorShown && window.get_workspace() != activeWorkspace) { ++ this._allWindowsMenuItem.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); ++ separatorShown = true; ++ } ++ ++ let item = new WindowPreview.WindowPreviewMenuItem(window); ++ this._allWindowsMenuItem.menu.addMenuItem(item); ++ item.connect('activate', Lang.bind(this, function() { ++ this.emit('activate-window', window); ++ })); ++ ++ // This is to achieve a more gracefull transition when the last windows is closed. ++ item.connect('destroy', Lang.bind(this, function() { ++ if(this._allWindowsMenuItem.menu._getMenuItems().length == 1) // It's still counting the item just going to be destroyed ++ this._allWindowsMenuItem.setSensitive(false); ++ })); ++ } ++ } ++ }, ++}); ++Signals.addSignalMethods(MyAppIconMenu.prototype); ++ ++// Filter out unnecessary windows, for instance ++// nautilus desktop window. ++function getInterestingWindows(app, settings, monitorIndex) { ++ let windows = app.get_windows().filter(function(w) { ++ return !w.skip_taskbar; ++ }); ++ ++ // When using workspace isolation, we filter out windows ++ // that are not in the current workspace ++ if (settings.get_boolean('isolate-workspaces')) ++ windows = windows.filter(function(w) { ++ return w.get_workspace().index() == global.screen.get_active_workspace_index(); ++ }); ++ ++ if (settings.get_boolean('isolate-monitors')) ++ windows = windows.filter(function(w) { ++ return w.get_monitor() == monitorIndex; ++ }); ++ ++ return windows; ++} ++ ++/** ++ * Extend ShowAppsIcon ++ * ++ * - Pass settings to the constructor ++ * - set label position based on dash orientation ++ * - implement a popupMenu based on the AppIcon code ++ * ++ * I can't subclass the original object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973. ++ * thus use this ugly pattern. ++ */ ++function extendShowAppsIcon(showAppsIcon, settings) { ++ showAppsIcon._dtdSettings = settings; ++ /* the variable equivalent to toggleButton has a different name in the appIcon class ++ (actor): duplicate reference to easily reuse appIcon methods */ ++ showAppsIcon.actor = showAppsIcon.toggleButton; ++ ++ // Re-use appIcon methods ++ showAppsIcon._removeMenuTimeout = AppDisplay.AppIcon.prototype._removeMenuTimeout; ++ showAppsIcon._setPopupTimeout = AppDisplay.AppIcon.prototype._setPopupTimeout; ++ showAppsIcon._onButtonPress = AppDisplay.AppIcon.prototype._onButtonPress; ++ showAppsIcon._onKeyboardPopupMenu = AppDisplay.AppIcon.prototype._onKeyboardPopupMenu; ++ showAppsIcon._onLeaveEvent = AppDisplay.AppIcon.prototype._onLeaveEvent; ++ showAppsIcon._onTouchEvent = AppDisplay.AppIcon.prototype._onTouchEvent; ++ showAppsIcon._onMenuPoppedDown = AppDisplay.AppIcon.prototype._onMenuPoppedDown; ++ ++ ++ // No action on clicked (showing of the appsview is controlled elsewhere) ++ showAppsIcon._onClicked = function(actor, button) { ++ showAppsIcon._removeMenuTimeout(); ++ }; ++ ++ showAppsIcon.actor.connect('leave-event', Lang.bind(showAppsIcon, showAppsIcon._onLeaveEvent)); ++ showAppsIcon.actor.connect('button-press-event', Lang.bind(showAppsIcon, showAppsIcon._onButtonPress)); ++ showAppsIcon.actor.connect('touch-event', Lang.bind(showAppsIcon, showAppsIcon._onTouchEvent)); ++ showAppsIcon.actor.connect('clicked', Lang.bind(showAppsIcon, showAppsIcon._onClicked)); ++ showAppsIcon.actor.connect('popup-menu', Lang.bind(showAppsIcon, showAppsIcon._onKeyboardPopupMenu)); ++ ++ showAppsIcon._menu = null; ++ showAppsIcon._menuManager = new PopupMenu.PopupMenuManager(showAppsIcon); ++ showAppsIcon._menuTimeoutId = 0; ++ ++ showAppsIcon.showLabel = itemShowLabel; ++ ++ showAppsIcon.popupMenu = function() { ++ showAppsIcon._removeMenuTimeout(); ++ showAppsIcon.actor.fake_release(); ++ ++ if (!showAppsIcon._menu) { ++ showAppsIcon._menu = new MyShowAppsIconMenu(showAppsIcon, showAppsIcon._dtdSettings); ++ showAppsIcon._menu.connect('open-state-changed', Lang.bind(showAppsIcon, function(menu, isPoppedUp) { ++ if (!isPoppedUp) ++ showAppsIcon._onMenuPoppedDown(); ++ })); ++ let id = Main.overview.connect('hiding', Lang.bind(showAppsIcon, function() { ++ showAppsIcon._menu.close(); ++ })); ++ showAppsIcon._menu.actor.connect('destroy', function() { ++ Main.overview.disconnect(id); ++ }); ++ showAppsIcon._menuManager.addMenu(showAppsIcon._menu); ++ } ++ ++ showAppsIcon.emit('menu-state-changed', true); ++ ++ showAppsIcon.actor.set_hover(true); ++ showAppsIcon._menu.popup(); ++ showAppsIcon._menuManager.ignoreRelease(); ++ showAppsIcon.emit('sync-tooltip'); ++ ++ return false; ++ }; ++ ++ Signals.addSignalMethods(showAppsIcon); ++} ++ ++/** ++ * A menu for the showAppsIcon ++ */ ++const MyShowAppsIconMenu = new Lang.Class({ ++ Name: 'DashToDock.ShowAppsIconMenu', ++ Extends: MyAppIconMenu, ++ ++ _redisplay: function() { ++ this.removeAll(); ++ ++ /* Translators: %s is "Settings", which is automatically translated. You ++ can also translate the full message if this fits better your language. */ ++ let name = __('Dash to Dock %s').format(_('Settings')) ++ let item = this._appendMenuItem(name); ++ ++ item.connect('activate', function () { ++ Util.spawn(["gnome-shell-extension-prefs", Me.metadata.uuid]); ++ }); ++ } ++}); ++ ++/** ++ * This function is used for both extendShowAppsIcon and extendDashItemContainer ++ */ ++function itemShowLabel() { ++ // Check if the label is still present at all. When switching workpaces, the ++ // item might have been destroyed in between. ++ if (!this._labelText || this.label.get_stage() == null) ++ return; ++ ++ this.label.set_text(this._labelText); ++ this.label.opacity = 0; ++ this.label.show(); ++ ++ let [stageX, stageY] = this.get_transformed_position(); ++ let node = this.label.get_theme_node(); ++ ++ let itemWidth = this.allocation.x2 - this.allocation.x1; ++ let itemHeight = this.allocation.y2 - this.allocation.y1; ++ ++ let labelWidth = this.label.get_width(); ++ let labelHeight = this.label.get_height(); ++ ++ let x, y, xOffset, yOffset; ++ ++ let position = Utils.getPosition(this._dtdSettings); ++ this._isHorizontal = ((position == St.Side.TOP) || (position == St.Side.BOTTOM)); ++ let labelOffset = node.get_length('-x-offset'); ++ ++ switch (position) { ++ case St.Side.LEFT: ++ yOffset = Math.floor((itemHeight - labelHeight) / 2); ++ y = stageY + yOffset; ++ xOffset = labelOffset; ++ x = stageX + this.get_width() + xOffset; ++ break; ++ case St.Side.RIGHT: ++ yOffset = Math.floor((itemHeight - labelHeight) / 2); ++ y = stageY + yOffset; ++ xOffset = labelOffset; ++ x = Math.round(stageX) - labelWidth - xOffset; ++ break; ++ case St.Side.TOP: ++ y = stageY + labelOffset + itemHeight; ++ xOffset = Math.floor((itemWidth - labelWidth) / 2); ++ x = stageX + xOffset; ++ break; ++ case St.Side.BOTTOM: ++ yOffset = labelOffset; ++ y = stageY - labelHeight - yOffset; ++ xOffset = Math.floor((itemWidth - labelWidth) / 2); ++ x = stageX + xOffset; ++ break; ++ } ++ ++ // keep the label inside the screen border ++ // Only needed fot the x coordinate. ++ ++ // Leave a few pixel gap ++ let gap = 5; ++ let monitor = Main.layoutManager.findMonitorForActor(this); ++ if (x - monitor.x < gap) ++ x += monitor.x - x + labelOffset; ++ else if (x + labelWidth > monitor.x + monitor.width - gap) ++ x -= x + labelWidth - (monitor.x + monitor.width) + gap; ++ ++ this.label.set_position(x, y); ++ Tweener.addTween(this.label, { ++ opacity: 255, ++ time: DASH_ITEM_LABEL_SHOW_TIME, ++ transition: 'easeOutQuad', ++ }); ++} +diff --git a/extensions/dash-to-dock/convenience.js b/extensions/dash-to-dock/convenience.js +new file mode 100644 +index 0000000..9c912bf +--- /dev/null ++++ b/extensions/dash-to-dock/convenience.js +@@ -0,0 +1,74 @@ ++/* -*- mode: js; js-basic-offset: 4; indent-tabs-mode: nil -*- */ ++ ++/* ++ * Part of this file comes from gnome-shell-extensions: ++ * http://git.gnome.org/browse/gnome-shell-extensions/ ++ */ ++ ++const Gettext = imports.gettext; ++const Gio = imports.gi.Gio; ++ ++const Config = imports.misc.config; ++const ExtensionUtils = imports.misc.extensionUtils; ++ ++/** ++ * initTranslations: ++ * @domain: (optional): the gettext domain to use ++ * ++ * Initialize Gettext to load translations from extensionsdir/locale. ++ * If @domain is not provided, it will be taken from metadata['gettext-domain'] ++ */ ++function initTranslations(domain) { ++ let extension = ExtensionUtils.getCurrentExtension(); ++ ++ domain = domain || extension.metadata['gettext-domain']; ++ ++ // Check if this extension was built with "make zip-file", and thus ++ // has the locale files in a subfolder ++ // otherwise assume that extension has been installed in the ++ // same prefix as gnome-shell ++ let localeDir = extension.dir.get_child('locale'); ++ if (localeDir.query_exists(null)) ++ Gettext.bindtextdomain(domain, localeDir.get_path()); ++ else ++ Gettext.bindtextdomain(domain, Config.LOCALEDIR); ++} ++ ++/** ++ * getSettings: ++ * @schema: (optional): the GSettings schema id ++ * ++ * Builds and return a GSettings schema for @schema, using schema files ++ * in extensionsdir/schemas. If @schema is not provided, it is taken from ++ * metadata['settings-schema']. ++ */ ++function getSettings(schema) { ++ let extension = ExtensionUtils.getCurrentExtension(); ++ ++ schema = schema || extension.metadata['settings-schema']; ++ ++ const GioSSS = Gio.SettingsSchemaSource; ++ ++ // Check if this extension was built with "make zip-file", and thus ++ // has the schema files in a subfolder ++ // otherwise assume that extension has been installed in the ++ // same prefix as gnome-shell (and therefore schemas are available ++ // in the standard folders) ++ let schemaDir = extension.dir.get_child('schemas'); ++ let schemaSource; ++ if (schemaDir.query_exists(null)) ++ schemaSource = GioSSS.new_from_directory(schemaDir.get_path(), ++ GioSSS.get_default(), ++ false); ++ else ++ schemaSource = GioSSS.get_default(); ++ ++ let schemaObj = schemaSource.lookup(schema, true); ++ if (!schemaObj) ++ throw new Error('Schema ' + schema + ' could not be found for extension ' ++ + extension.metadata.uuid + '. Please check your installation.'); ++ ++ return new Gio.Settings({ ++ settings_schema: schemaObj ++ }); ++} +diff --git a/extensions/dash-to-dock/dash.js b/extensions/dash-to-dock/dash.js +new file mode 100644 +index 0000000..593185a +--- /dev/null ++++ b/extensions/dash-to-dock/dash.js +@@ -0,0 +1,1180 @@ ++// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- ++ ++const Clutter = imports.gi.Clutter; ++const Gio = imports.gi.Gio; ++const GLib = imports.gi.GLib; ++const Gtk = imports.gi.Gtk; ++const Signals = imports.signals; ++const Lang = imports.lang; ++const Meta = imports.gi.Meta; ++const Shell = imports.gi.Shell; ++const St = imports.gi.St; ++const Mainloop = imports.mainloop; ++ ++const AppDisplay = imports.ui.appDisplay; ++const AppFavorites = imports.ui.appFavorites; ++const Dash = imports.ui.dash; ++const DND = imports.ui.dnd; ++const IconGrid = imports.ui.iconGrid; ++const Main = imports.ui.main; ++const PopupMenu = imports.ui.popupMenu; ++const Tweener = imports.ui.tweener; ++const Util = imports.misc.util; ++const Workspace = imports.ui.workspace; ++ ++const Me = imports.misc.extensionUtils.getCurrentExtension(); ++const Utils = Me.imports.utils; ++const AppIcons = Me.imports.appIcons; ++ ++let DASH_ANIMATION_TIME = Dash.DASH_ANIMATION_TIME; ++let DASH_ITEM_LABEL_HIDE_TIME = Dash.DASH_ITEM_LABEL_HIDE_TIME; ++let DASH_ITEM_HOVER_TIMEOUT = Dash.DASH_ITEM_HOVER_TIMEOUT; ++ ++/** ++ * Extend DashItemContainer ++ * ++ * - Pass settings to the constructor ++ * - set label position based on dash orientation ++ * ++ * I can't subclass the original object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973. ++ * thus use this ugly pattern. ++ */ ++function extendDashItemContainer(dashItemContainer, settings) { ++ dashItemContainer._dtdSettings = settings; ++ dashItemContainer.showLabel = AppIcons.itemShowLabel; ++} ++ ++/** ++ * This class is a fork of the upstream DashActor class (ui.dash.js) ++ * ++ * Summary of changes: ++ * - passed settings to class as parameter ++ * - modified chldBox calculations for when 'show-apps-at-top' option is checked ++ * - handle horizontal dash ++ */ ++const MyDashActor = new Lang.Class({ ++ Name: 'DashToDock.MyDashActor', ++ ++ _init: function(settings) { ++ // a prefix is required to avoid conflicting with the parent class variable ++ this._dtdSettings = settings; ++ this._rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL); ++ ++ this._position = Utils.getPosition(settings); ++ this._isHorizontal = ((this._position == St.Side.TOP) || ++ (this._position == St.Side.BOTTOM)); ++ ++ let layout = new Clutter.BoxLayout({ ++ orientation: this._isHorizontal ? Clutter.Orientation.HORIZONTAL : Clutter.Orientation.VERTICAL ++ }); ++ ++ this.actor = new Shell.GenericContainer({ ++ name: 'dash', ++ layout_manager: layout, ++ clip_to_allocation: true ++ }); ++ this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); ++ this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); ++ this.actor.connect('allocate', Lang.bind(this, this._allocate)); ++ ++ this.actor._delegate = this; ++ }, ++ ++ _allocate: function(actor, box, flags) { ++ let contentBox = box; ++ let availWidth = contentBox.x2 - contentBox.x1; ++ let availHeight = contentBox.y2 - contentBox.y1; ++ ++ let [appIcons, showAppsButton] = actor.get_children(); ++ let [showAppsMinHeight, showAppsNatHeight] = showAppsButton.get_preferred_height(availWidth); ++ let [showAppsMinWidth, showAppsNatWidth] = showAppsButton.get_preferred_width(availHeight); ++ ++ let offset_x = this._isHorizontal?showAppsNatWidth:0; ++ let offset_y = this._isHorizontal?0:showAppsNatHeight; ++ ++ let childBox = new Clutter.ActorBox(); ++ if ((this._dtdSettings.get_boolean('show-apps-at-top') && !this._isHorizontal) ++ || (this._dtdSettings.get_boolean('show-apps-at-top') && !this._rtl) ++ || (!this._dtdSettings.get_boolean('show-apps-at-top') && this._isHorizontal && this._rtl)) { ++ childBox.x1 = contentBox.x1 + offset_x; ++ childBox.y1 = contentBox.y1 + offset_y; ++ childBox.x2 = contentBox.x2; ++ childBox.y2 = contentBox.y2; ++ appIcons.allocate(childBox, flags); ++ ++ childBox.y1 = contentBox.y1; ++ childBox.x1 = contentBox.x1; ++ childBox.x2 = contentBox.x1 + showAppsNatWidth; ++ childBox.y2 = contentBox.y1 + showAppsNatHeight; ++ showAppsButton.allocate(childBox, flags); ++ } ++ else { ++ childBox.x1 = contentBox.x1; ++ childBox.y1 = contentBox.y1; ++ childBox.x2 = contentBox.x2 - offset_x; ++ childBox.y2 = contentBox.y2 - offset_y; ++ appIcons.allocate(childBox, flags); ++ ++ childBox.x2 = contentBox.x2; ++ childBox.y2 = contentBox.y2; ++ childBox.x1 = contentBox.x2 - showAppsNatWidth; ++ childBox.y1 = contentBox.y2 - showAppsNatHeight; ++ showAppsButton.allocate(childBox, flags); ++ } ++ }, ++ ++ _getPreferredWidth: function(actor, forHeight, alloc) { ++ // We want to request the natural height of all our children ++ // as our natural height, so we chain up to StWidget (which ++ // then calls BoxLayout), but we only request the showApps ++ // button as the minimum size ++ ++ let [, natWidth] = this.actor.layout_manager.get_preferred_width(this.actor, forHeight); ++ ++ let themeNode = this.actor.get_theme_node(); ++ let [, showAppsButton] = this.actor.get_children(); ++ let [minWidth, ] = showAppsButton.get_preferred_height(forHeight); ++ ++ alloc.min_size = minWidth; ++ alloc.natural_size = natWidth; ++ ++ }, ++ ++ _getPreferredHeight: function(actor, forWidth, alloc) { ++ // We want to request the natural height of all our children ++ // as our natural height, so we chain up to StWidget (which ++ // then calls BoxLayout), but we only request the showApps ++ // button as the minimum size ++ ++ let [, natHeight] = this.actor.layout_manager.get_preferred_height(this.actor, forWidth); ++ ++ let themeNode = this.actor.get_theme_node(); ++ let [, showAppsButton] = this.actor.get_children(); ++ let [minHeight, ] = showAppsButton.get_preferred_height(forWidth); ++ ++ alloc.min_size = minHeight; ++ alloc.natural_size = natHeight; ++ } ++}); ++ ++const baseIconSizes = [16, 22, 24, 32, 48, 64, 96, 128]; ++ ++/** ++ * This class is a fork of the upstream dash class (ui.dash.js) ++ * ++ * Summary of changes: ++ * - disconnect global signals adding a destroy method; ++ * - play animations even when not in overview mode ++ * - set a maximum icon size ++ * - show running and/or favorite applications ++ * - emit a custom signal when an app icon is added ++ * - hide showApps label when the custom menu is shown. ++ * - add scrollview ++ * ensure actor is visible on keyfocus inseid the scrollview ++ * - add 128px icon size, might be usefull for hidpi display ++ * - sync minimization application target position. ++ * - keep running apps ordered. ++ */ ++const MyDash = new Lang.Class({ ++ Name: 'DashToDock.MyDash', ++ ++ _init: function(settings, monitorIndex) { ++ this._maxHeight = -1; ++ this.iconSize = 64; ++ this._availableIconSizes = baseIconSizes; ++ this._shownInitially = false; ++ ++ this._dtdSettings = settings; ++ this._monitorIndex = monitorIndex; ++ this._position = Utils.getPosition(settings); ++ this._isHorizontal = ((this._position == St.Side.TOP) || ++ (this._position == St.Side.BOTTOM)); ++ this._signalsHandler = new Utils.GlobalSignalsHandler(); ++ ++ this._dragPlaceholder = null; ++ this._dragPlaceholderPos = -1; ++ this._animatingPlaceholdersCount = 0; ++ this._showLabelTimeoutId = 0; ++ this._resetHoverTimeoutId = 0; ++ this._ensureAppIconVisibilityTimeoutId = 0; ++ this._labelShowing = false; ++ ++ this._containerObject = new MyDashActor(settings); ++ this._container = this._containerObject.actor; ++ this._scrollView = new St.ScrollView({ ++ name: 'dashtodockDashScrollview', ++ hscrollbar_policy: Gtk.PolicyType.NEVER, ++ vscrollbar_policy: Gtk.PolicyType.NEVER, ++ enable_mouse_scrolling: false ++ }); ++ ++ this._scrollView.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); ++ ++ this._box = new St.BoxLayout({ ++ vertical: !this._isHorizontal, ++ clip_to_allocation: false, ++ x_align: Clutter.ActorAlign.START, ++ y_align: Clutter.ActorAlign.START ++ }); ++ this._box._delegate = this; ++ this._container.add_actor(this._scrollView); ++ this._scrollView.add_actor(this._box); ++ ++ this._showAppsIcon = new Dash.ShowAppsIcon(); ++ AppIcons.extendShowAppsIcon(this._showAppsIcon, this._dtdSettings); ++ this._showAppsIcon.childScale = 1; ++ this._showAppsIcon.childOpacity = 255; ++ this._showAppsIcon.icon.setIconSize(this.iconSize); ++ this._hookUpLabel(this._showAppsIcon); ++ ++ let appsIcon = this._showAppsIcon; ++ appsIcon.connect('menu-state-changed', Lang.bind(this, function(appsIcon, opened) { ++ this._itemMenuStateChanged(appsIcon, opened); ++ })); ++ ++ this.showAppsButton = this._showAppsIcon.toggleButton; ++ ++ this._container.add_actor(this._showAppsIcon); ++ ++ let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL; ++ this.actor = new St.Bin({ ++ child: this._container, ++ y_align: St.Align.START, ++ x_align: rtl ? St.Align.END : St.Align.START ++ }); ++ ++ if (this._isHorizontal) { ++ this.actor.connect('notify::width', Lang.bind(this, function() { ++ if (this._maxHeight != this.actor.width) ++ this._queueRedisplay(); ++ this._maxHeight = this.actor.width; ++ })); ++ } ++ else { ++ this.actor.connect('notify::height', Lang.bind(this, function() { ++ if (this._maxHeight != this.actor.height) ++ this._queueRedisplay(); ++ this._maxHeight = this.actor.height; ++ })); ++ } ++ ++ // Update minimization animation target position on allocation of the ++ // container and on scrollview change. ++ this._box.connect('notify::allocation', Lang.bind(this, this._updateAppsIconGeometry)); ++ let scrollViewAdjustment = this._isHorizontal ? this._scrollView.hscroll.adjustment : this._scrollView.vscroll.adjustment; ++ scrollViewAdjustment.connect('notify::value', Lang.bind(this, this._updateAppsIconGeometry)); ++ ++ this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay)); ++ ++ this._settings = new Gio.Settings({ ++ schema_id: 'org.gnome.shell' ++ }); ++ ++ this._appSystem = Shell.AppSystem.get_default(); ++ ++ this._signalsHandler.add([ ++ this._appSystem, ++ 'installed-changed', ++ Lang.bind(this, function() { ++ AppFavorites.getAppFavorites().reload(); ++ this._queueRedisplay(); ++ }) ++ ], [ ++ AppFavorites.getAppFavorites(), ++ 'changed', ++ Lang.bind(this, this._queueRedisplay) ++ ], [ ++ this._appSystem, ++ 'app-state-changed', ++ Lang.bind(this, this._queueRedisplay) ++ ], [ ++ Main.overview, ++ 'item-drag-begin', ++ Lang.bind(this, this._onDragBegin) ++ ], [ ++ Main.overview, ++ 'item-drag-end', ++ Lang.bind(this, this._onDragEnd) ++ ], [ ++ Main.overview, ++ 'item-drag-cancelled', ++ Lang.bind(this, this._onDragCancelled) ++ ]); ++ }, ++ ++ destroy: function() { ++ this._signalsHandler.destroy(); ++ }, ++ ++ _onScrollEvent: function(actor, event) { ++ // If scroll is not used because the icon is resized, let the scroll event propagate. ++ if (!this._dtdSettings.get_boolean('icon-size-fixed')) ++ return Clutter.EVENT_PROPAGATE; ++ ++ // Event coordinates are relative to the stage but can be transformed ++ // as the actor will only receive events within his bounds. ++ let stage_x, stage_y, ok, event_x, event_y, actor_w, actor_h; ++ [stage_x, stage_y] = event.get_coords(); ++ [ok, event_x, event_y] = actor.transform_stage_point(stage_x, stage_y); ++ [actor_w, actor_h] = actor.get_size(); ++ ++ // If the scroll event is within a 1px margin from ++ // the relevant edge of the actor, let the event propagate. ++ if ((this._position == St.Side.LEFT && event_x <= 1) ++ || (this._position == St.Side.RIGHT && event_x >= actor_w - 2) ++ || (this._position == St.Side.TOP && event_y <= 1) ++ || (this._position == St.Side.BOTTOM && event_y >= actor_h - 2)) ++ return Clutter.EVENT_PROPAGATE; ++ ++ // reset timeout to avid conflicts with the mousehover event ++ if (this._ensureAppIconVisibilityTimeoutId > 0) { ++ Mainloop.source_remove(this._ensureAppIconVisibilityTimeoutId); ++ this._ensureAppIconVisibilityTimeoutId = 0; ++ } ++ ++ // Skip to avoid double events mouse ++ if (event.is_pointer_emulated()) ++ return Clutter.EVENT_STOP; ++ ++ let adjustment, delta; ++ ++ if (this._isHorizontal) ++ adjustment = this._scrollView.get_hscroll_bar().get_adjustment(); ++ else ++ adjustment = this._scrollView.get_vscroll_bar().get_adjustment(); ++ ++ let increment = adjustment.step_increment; ++ ++ switch (event.get_scroll_direction()) { ++ case Clutter.ScrollDirection.UP: ++ delta = -increment; ++ break; ++ case Clutter.ScrollDirection.DOWN: ++ delta = +increment; ++ break; ++ case Clutter.ScrollDirection.SMOOTH: ++ let [dx, dy] = event.get_scroll_delta(); ++ delta = dy * increment; ++ // Also consider horizontal component, for instance touchpad ++ if (this._isHorizontal) ++ delta += dx * increment; ++ break; ++ } ++ ++ adjustment.set_value(adjustment.get_value() + delta); ++ ++ return Clutter.EVENT_STOP; ++ }, ++ ++ _onDragBegin: function() { ++ this._dragCancelled = false; ++ this._dragMonitor = { ++ dragMotion: Lang.bind(this, this._onDragMotion) ++ }; ++ DND.addDragMonitor(this._dragMonitor); ++ ++ if (this._box.get_n_children() == 0) { ++ this._emptyDropTarget = new Dash.EmptyDropTargetItem(); ++ this._box.insert_child_at_index(this._emptyDropTarget, 0); ++ this._emptyDropTarget.show(true); ++ } ++ }, ++ ++ _onDragCancelled: function() { ++ this._dragCancelled = true; ++ this._endDrag(); ++ }, ++ ++ _onDragEnd: function() { ++ if (this._dragCancelled) ++ return; ++ ++ this._endDrag(); ++ }, ++ ++ _endDrag: function() { ++ this._clearDragPlaceholder(); ++ this._clearEmptyDropTarget(); ++ this._showAppsIcon.setDragApp(null); ++ DND.removeDragMonitor(this._dragMonitor); ++ }, ++ ++ _onDragMotion: function(dragEvent) { ++ let app = Dash.getAppFromSource(dragEvent.source); ++ if (app == null) ++ return DND.DragMotionResult.CONTINUE; ++ ++ let showAppsHovered = this._showAppsIcon.contains(dragEvent.targetActor); ++ ++ if (!this._box.contains(dragEvent.targetActor) || showAppsHovered) ++ this._clearDragPlaceholder(); ++ ++ if (showAppsHovered) ++ this._showAppsIcon.setDragApp(app); ++ else ++ this._showAppsIcon.setDragApp(null); ++ ++ return DND.DragMotionResult.CONTINUE; ++ }, ++ ++ _appIdListToHash: function(apps) { ++ let ids = {}; ++ for (let i = 0; i < apps.length; i++) ++ ids[apps[i].get_id()] = apps[i]; ++ return ids; ++ }, ++ ++ _queueRedisplay: function() { ++ Main.queueDeferredWork(this._workId); ++ }, ++ ++ _hookUpLabel: function(item, appIcon) { ++ item.child.connect('notify::hover', Lang.bind(this, function() { ++ this._syncLabel(item, appIcon); ++ })); ++ ++ let id = Main.overview.connect('hiding', Lang.bind(this, function() { ++ this._labelShowing = false; ++ item.hideLabel(); ++ })); ++ item.child.connect('destroy', function() { ++ Main.overview.disconnect(id); ++ }); ++ ++ if (appIcon) { ++ appIcon.connect('sync-tooltip', Lang.bind(this, function() { ++ this._syncLabel(item, appIcon); ++ })); ++ } ++ }, ++ ++ _createAppItem: function(app) { ++ let appIcon = new AppIcons.MyAppIcon(this._dtdSettings, app, this._monitorIndex, ++ { setSizeManually: true, ++ showLabel: false }); ++ if (appIcon._draggable) { ++ appIcon._draggable.connect('drag-begin', Lang.bind(this, function() { ++ appIcon.actor.opacity = 50; ++ })); ++ appIcon._draggable.connect('drag-end', Lang.bind(this, function() { ++ appIcon.actor.opacity = 255; ++ })); ++ } ++ ++ appIcon.connect('menu-state-changed', Lang.bind(this, function(appIcon, opened) { ++ this._itemMenuStateChanged(item, opened); ++ })); ++ ++ let item = new Dash.DashItemContainer(); ++ ++ extendDashItemContainer(item, this._dtdSettings); ++ item.setChild(appIcon.actor); ++ ++ appIcon.actor.connect('notify::hover', Lang.bind(this, function() { ++ if (appIcon.actor.hover) { ++ this._ensureAppIconVisibilityTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function() { ++ ensureActorVisibleInScrollView(this._scrollView, appIcon.actor); ++ this._ensureAppIconVisibilityTimeoutId = 0; ++ return GLib.SOURCE_REMOVE; ++ })); ++ } ++ else { ++ if (this._ensureAppIconVisibilityTimeoutId > 0) { ++ Mainloop.source_remove(this._ensureAppIconVisibilityTimeoutId); ++ this._ensureAppIconVisibilityTimeoutId = 0; ++ } ++ } ++ })); ++ ++ appIcon.actor.connect('clicked', Lang.bind(this, function(actor) { ++ ensureActorVisibleInScrollView(this._scrollView, actor); ++ })); ++ ++ appIcon.actor.connect('key-focus-in', Lang.bind(this, function(actor) { ++ let [x_shift, y_shift] = ensureActorVisibleInScrollView(this._scrollView, actor); ++ ++ // This signal is triggered also by mouse click. The popup menu is opened at the original ++ // coordinates. Thus correct for the shift which is going to be applied to the scrollview. ++ if (appIcon._menu) { ++ appIcon._menu._boxPointer.xOffset = -x_shift; ++ appIcon._menu._boxPointer.yOffset = -y_shift; ++ } ++ })); ++ ++ // Override default AppIcon label_actor, now the ++ // accessible_name is set at DashItemContainer.setLabelText ++ appIcon.actor.label_actor = null; ++ item.setLabelText(app.get_name()); ++ ++ appIcon.icon.setIconSize(this.iconSize); ++ this._hookUpLabel(item, appIcon); ++ ++ return item; ++ }, ++ ++ /** ++ * Return an array with the "proper" appIcons currently in the dash ++ */ ++ _getAppIcons: function() { ++ // Only consider children which are "proper" ++ // icons (i.e. ignoring drag placeholders) and which are not ++ // animating out (which means they will be destroyed at the end of ++ // the animation) ++ let iconChildren = this._box.get_children().filter(function(actor) { ++ return actor.child && ++ actor.child._delegate && ++ actor.child._delegate.icon && ++ !actor.animatingOut; ++ }); ++ ++ let appIcons = iconChildren.map(function(actor) { ++ return actor.child._delegate; ++ }); ++ ++ return appIcons; ++ }, ++ ++ _updateAppsIconGeometry: function() { ++ let appIcons = this._getAppIcons(); ++ appIcons.forEach(function(icon) { ++ icon.updateIconGeometry(); ++ }); ++ }, ++ ++ _itemMenuStateChanged: function(item, opened) { ++ // When the menu closes, it calls sync_hover, which means ++ // that the notify::hover handler does everything we need to. ++ if (opened) { ++ if (this._showLabelTimeoutId > 0) { ++ Mainloop.source_remove(this._showLabelTimeoutId); ++ this._showLabelTimeoutId = 0; ++ } ++ ++ item.hideLabel(); ++ } ++ else { ++ // I want to listen from outside when a menu is closed. I used to ++ // add a custom signal to the appIcon, since gnome 3.8 the signal ++ // calling this callback was added upstream. ++ this.emit('menu-closed'); ++ } ++ }, ++ ++ _syncLabel: function(item, appIcon) { ++ let shouldShow = appIcon ? appIcon.shouldShowTooltip() : item.child.get_hover(); ++ ++ if (shouldShow) { ++ if (this._showLabelTimeoutId == 0) { ++ let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT; ++ this._showLabelTimeoutId = Mainloop.timeout_add(timeout, Lang.bind(this, function() { ++ this._labelShowing = true; ++ item.showLabel(); ++ this._showLabelTimeoutId = 0; ++ return GLib.SOURCE_REMOVE; ++ })); ++ GLib.Source.set_name_by_id(this._showLabelTimeoutId, '[gnome-shell] item.showLabel'); ++ if (this._resetHoverTimeoutId > 0) { ++ Mainloop.source_remove(this._resetHoverTimeoutId); ++ this._resetHoverTimeoutId = 0; ++ } ++ } ++ } ++ else { ++ if (this._showLabelTimeoutId > 0) ++ Mainloop.source_remove(this._showLabelTimeoutId); ++ this._showLabelTimeoutId = 0; ++ item.hideLabel(); ++ if (this._labelShowing) { ++ this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT, Lang.bind(this, function() { ++ this._labelShowing = false; ++ this._resetHoverTimeoutId = 0; ++ return GLib.SOURCE_REMOVE; ++ })); ++ GLib.Source.set_name_by_id(this._resetHoverTimeoutId, '[gnome-shell] this._labelShowing'); ++ } ++ } ++ }, ++ ++ _adjustIconSize: function() { ++ // For the icon size, we only consider children which are "proper" ++ // icons (i.e. ignoring drag placeholders) and which are not ++ // animating out (which means they will be destroyed at the end of ++ // the animation) ++ let iconChildren = this._box.get_children().filter(function(actor) { ++ return actor.child && ++ actor.child._delegate && ++ actor.child._delegate.icon && ++ !actor.animatingOut; ++ }); ++ ++ iconChildren.push(this._showAppsIcon); ++ ++ if (this._maxHeight == -1) ++ return; ++ ++ // Check if the container is present in the stage. This avoids critical ++ // errors when unlocking the screen ++ if (!this._container.get_stage()) ++ return; ++ ++ let themeNode = this._container.get_theme_node(); ++ let maxAllocation = new Clutter.ActorBox({ ++ x1: 0, ++ y1: 0, ++ x2: this._isHorizontal ? this._maxHeight : 42 /* whatever */, ++ y2: this._isHorizontal ? 42 : this._maxHeight ++ }); ++ let maxContent = themeNode.get_content_box(maxAllocation); ++ let availHeight; ++ if (this._isHorizontal) ++ availHeight = maxContent.x2 - maxContent.x1; ++ else ++ availHeight = maxContent.y2 - maxContent.y1; ++ let spacing = themeNode.get_length('spacing'); ++ ++ let firstButton = iconChildren[0].child; ++ let firstIcon = firstButton._delegate.icon; ++ ++ let minHeight, natHeight, minWidth, natWidth; ++ ++ // Enforce the current icon size during the size request ++ firstIcon.setIconSize(this.iconSize); ++ [minHeight, natHeight] = firstButton.get_preferred_height(-1); ++ [minWidth, natWidth] = firstButton.get_preferred_width(-1); ++ ++ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; ++ let iconSizes = this._availableIconSizes.map(function(s) { ++ return s * scaleFactor; ++ }); ++ ++ // Subtract icon padding and box spacing from the available height ++ if (this._isHorizontal) ++ availHeight -= iconChildren.length * (natWidth - this.iconSize * scaleFactor) + ++ (iconChildren.length - 1) * spacing; ++ else ++ availHeight -= iconChildren.length * (natHeight - this.iconSize * scaleFactor) + ++ (iconChildren.length - 1) * spacing; ++ ++ let availSize = availHeight / iconChildren.length; ++ ++ ++ let newIconSize = this._availableIconSizes[0]; ++ for (let i = 0; i < iconSizes.length; i++) { ++ if (iconSizes[i] < availSize) ++ newIconSize = this._availableIconSizes[i]; ++ } ++ ++ if (newIconSize == this.iconSize) ++ return; ++ ++ let oldIconSize = this.iconSize; ++ this.iconSize = newIconSize; ++ this.emit('icon-size-changed'); ++ ++ let scale = oldIconSize / newIconSize; ++ for (let i = 0; i < iconChildren.length; i++) { ++ let icon = iconChildren[i].child._delegate.icon; ++ ++ // Set the new size immediately, to keep the icons' sizes ++ // in sync with this.iconSize ++ icon.setIconSize(this.iconSize); ++ ++ // Don't animate the icon size change when the overview ++ // is transitioning, or when initially filling ++ // the dash ++ if (Main.overview.animationInProgress || ++ !this._shownInitially) ++ continue; ++ ++ let [targetWidth, targetHeight] = icon.icon.get_size(); ++ ++ // Scale the icon's texture to the previous size and ++ // tween to the new size ++ icon.icon.set_size(icon.icon.width * scale, ++ icon.icon.height * scale); ++ ++ Tweener.addTween(icon.icon, ++ { width: targetWidth, ++ height: targetHeight, ++ time: DASH_ANIMATION_TIME, ++ transition: 'easeOutQuad', ++ }); ++ } ++ }, ++ ++ _redisplay: function() { ++ let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); ++ ++ let running = this._appSystem.get_running(); ++ if (this._dtdSettings.get_boolean('isolate-workspaces') || ++ this._dtdSettings.get_boolean('isolate-monitors')) { ++ // When using isolation, we filter out apps that have no windows in ++ // the current workspace ++ let settings = this._dtdSettings; ++ let monitorIndex = this._monitorIndex; ++ running = running.filter(function(_app) { ++ return AppIcons.getInterestingWindows(_app, settings, monitorIndex).length != 0; ++ }); ++ } ++ ++ let children = this._box.get_children().filter(function(actor) { ++ return actor.child && ++ actor.child._delegate && ++ actor.child._delegate.app; ++ }); ++ // Apps currently in the dash ++ let oldApps = children.map(function(actor) { ++ return actor.child._delegate.app; ++ }); ++ // Apps supposed to be in the dash ++ let newApps = []; ++ ++ if (this._dtdSettings.get_boolean('show-favorites')) { ++ for (let id in favorites) ++ newApps.push(favorites[id]); ++ } ++ ++ // We reorder the running apps so that they don't change position on the ++ // dash with every redisplay() call ++ if (this._dtdSettings.get_boolean('show-running')) { ++ // First: add the apps from the oldApps list that are still running ++ for (let i = 0; i < oldApps.length; i++) { ++ let index = running.indexOf(oldApps[i]); ++ if (index > -1) { ++ let app = running.splice(index, 1)[0]; ++ if (this._dtdSettings.get_boolean('show-favorites') && (app.get_id() in favorites)) ++ continue; ++ newApps.push(app); ++ } ++ } ++ // Second: add the new apps ++ for (let i = 0; i < running.length; i++) { ++ let app = running[i]; ++ if (this._dtdSettings.get_boolean('show-favorites') && (app.get_id() in favorites)) ++ continue; ++ newApps.push(app); ++ } ++ } ++ ++ // Figure out the actual changes to the list of items; we iterate ++ // over both the list of items currently in the dash and the list ++ // of items expected there, and collect additions and removals. ++ // Moves are both an addition and a removal, where the order of ++ // the operations depends on whether we encounter the position ++ // where the item has been added first or the one from where it ++ // was removed. ++ // There is an assumption that only one item is moved at a given ++ // time; when moving several items at once, everything will still ++ // end up at the right position, but there might be additional ++ // additions/removals (e.g. it might remove all the launchers ++ // and add them back in the new order even if a smaller set of ++ // additions and removals is possible). ++ // If above assumptions turns out to be a problem, we might need ++ // to use a more sophisticated algorithm, e.g. Longest Common ++ // Subsequence as used by diff. ++ ++ let addedItems = []; ++ let removedActors = []; ++ ++ let newIndex = 0; ++ let oldIndex = 0; ++ while ((newIndex < newApps.length) || (oldIndex < oldApps.length)) { ++ // No change at oldIndex/newIndex ++ if (oldApps[oldIndex] && oldApps[oldIndex] == newApps[newIndex]) { ++ oldIndex++; ++ newIndex++; ++ continue; ++ } ++ ++ // App removed at oldIndex ++ if (oldApps[oldIndex] && (newApps.indexOf(oldApps[oldIndex]) == -1)) { ++ removedActors.push(children[oldIndex]); ++ oldIndex++; ++ continue; ++ } ++ ++ // App added at newIndex ++ if (newApps[newIndex] && (oldApps.indexOf(newApps[newIndex]) == -1)) { ++ let newItem = this._createAppItem(newApps[newIndex]); ++ addedItems.push({ app: newApps[newIndex], ++ item: newItem, ++ pos: newIndex }); ++ newIndex++; ++ continue; ++ } ++ ++ // App moved ++ let insertHere = newApps[newIndex + 1] && (newApps[newIndex + 1] == oldApps[oldIndex]); ++ let alreadyRemoved = removedActors.reduce(function(result, actor) { ++ let removedApp = actor.child._delegate.app; ++ return result || removedApp == newApps[newIndex]; ++ }, false); ++ ++ if (insertHere || alreadyRemoved) { ++ let newItem = this._createAppItem(newApps[newIndex]); ++ addedItems.push({ ++ app: newApps[newIndex], ++ item: newItem, ++ pos: newIndex + removedActors.length ++ }); ++ newIndex++; ++ } ++ else { ++ removedActors.push(children[oldIndex]); ++ oldIndex++; ++ } ++ } ++ ++ for (let i = 0; i < addedItems.length; i++) ++ this._box.insert_child_at_index(addedItems[i].item, ++ addedItems[i].pos); ++ ++ for (let i = 0; i < removedActors.length; i++) { ++ let item = removedActors[i]; ++ ++ // Don't animate item removal when the overview is transitioning ++ if (!Main.overview.animationInProgress) ++ item.animateOutAndDestroy(); ++ else ++ item.destroy(); ++ } ++ ++ this._adjustIconSize(); ++ ++ for (let i = 0; i < addedItems.length; i++) ++ // Emit a custom signal notifying that a new item has been added ++ this.emit('item-added', addedItems[i]); ++ ++ // Skip animations on first run when adding the initial set ++ // of items, to avoid all items zooming in at once ++ ++ let animate = this._shownInitially && ++ !Main.overview.animationInProgress; ++ ++ if (!this._shownInitially) ++ this._shownInitially = true; ++ ++ for (let i = 0; i < addedItems.length; i++) ++ addedItems[i].item.show(animate); ++ ++ // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744 ++ // Without it, StBoxLayout may use a stale size cache ++ this._box.queue_relayout(); ++ ++ // This is required for icon reordering when the scrollview is used. ++ this._updateAppsIconGeometry(); ++ ++ // This will update the size, and the corresponding number for each icon ++ this._updateNumberOverlay(); ++ }, ++ ++ _updateNumberOverlay: function() { ++ let appIcons = this._getAppIcons(); ++ let counter = 1; ++ appIcons.forEach(function(icon) { ++ if (counter < 10){ ++ icon.setNumberOverlay(counter); ++ counter++; ++ } ++ else if (counter == 10) { ++ icon.setNumberOverlay(0); ++ counter++; ++ } ++ else { ++ // No overlay after 10 ++ icon.setNumberOverlay(-1); ++ } ++ icon.updateNumberOverlay(); ++ }); ++ ++ }, ++ ++ toggleNumberOverlay: function(activate) { ++ let appIcons = this._getAppIcons(); ++ appIcons.forEach(function(icon) { ++ icon.toggleNumberOverlay(activate); ++ }); ++ }, ++ ++ setIconSize: function(max_size, doNotAnimate) { ++ let max_allowed = baseIconSizes[baseIconSizes.length-1]; ++ max_size = Math.min(max_size, max_allowed); ++ ++ if (this._dtdSettings.get_boolean('icon-size-fixed')) ++ this._availableIconSizes = [max_size]; ++ else { ++ this._availableIconSizes = baseIconSizes.filter(function(val) { ++ return (val numChildren) ++ pos = numChildren; ++ } ++ else ++ pos = 0; // always insert at the top when dash is empty ++ ++ // Take into account childredn position in rtl ++ if (this._isHorizontal && (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)) ++ pos = numChildren - pos; ++ ++ if ((pos != this._dragPlaceholderPos) && (pos <= numFavorites) && (this._animatingPlaceholdersCount == 0)) { ++ this._dragPlaceholderPos = pos; ++ ++ // Don't allow positioning before or after self ++ if ((favPos != -1) && (pos == favPos || pos == favPos + 1)) { ++ this._clearDragPlaceholder(); ++ return DND.DragMotionResult.CONTINUE; ++ } ++ ++ // If the placeholder already exists, we just move ++ // it, but if we are adding it, expand its size in ++ // an animation ++ let fadeIn; ++ if (this._dragPlaceholder) { ++ this._dragPlaceholder.destroy(); ++ fadeIn = false; ++ } ++ else ++ fadeIn = true; ++ ++ this._dragPlaceholder = new Dash.DragPlaceholderItem(); ++ this._dragPlaceholder.child.set_width (this.iconSize); ++ this._dragPlaceholder.child.set_height (this.iconSize / 2); ++ this._box.insert_child_at_index(this._dragPlaceholder, ++ this._dragPlaceholderPos); ++ this._dragPlaceholder.show(fadeIn); ++ // Ensure the next and previous icon are visible when moving the placeholder ++ // (I assume there's room for both of them) ++ if (this._dragPlaceholderPos > 1) ++ ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[this._dragPlaceholderPos-1]); ++ if (this._dragPlaceholderPos < this._box.get_children().length-1) ++ ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[this._dragPlaceholderPos+1]); ++ } ++ ++ // Remove the drag placeholder if we are not in the ++ // "favorites zone" ++ if (pos > numFavorites) ++ this._clearDragPlaceholder(); ++ ++ if (!this._dragPlaceholder) ++ return DND.DragMotionResult.NO_DROP; ++ ++ let srcIsFavorite = (favPos != -1); ++ ++ if (srcIsFavorite) ++ return DND.DragMotionResult.MOVE_DROP; ++ ++ return DND.DragMotionResult.COPY_DROP; ++ }, ++ ++ /** ++ * Draggable target interface ++ */ ++ acceptDrop: function(source, actor, x, y, time) { ++ let app = Dash.getAppFromSource(source); ++ ++ // Don't allow favoriting of transient apps ++ if (app == null || app.is_window_backed()) ++ return false; ++ ++ if (!this._settings.is_writable('favorite-apps') || !this._dtdSettings.get_boolean('show-favorites')) ++ return false; ++ ++ let id = app.get_id(); ++ ++ let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); ++ ++ let srcIsFavorite = (id in favorites); ++ ++ let favPos = 0; ++ let children = this._box.get_children(); ++ for (let i = 0; i < this._dragPlaceholderPos; i++) { ++ if (this._dragPlaceholder && (children[i] == this._dragPlaceholder)) ++ continue; ++ ++ let childId = children[i].child._delegate.app.get_id(); ++ if (childId == id) ++ continue; ++ if (childId in favorites) ++ favPos++; ++ } ++ ++ // No drag placeholder means we don't wan't to favorite the app ++ // and we are dragging it to its original position ++ if (!this._dragPlaceholder) ++ return true; ++ ++ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { ++ let appFavorites = AppFavorites.getAppFavorites(); ++ if (srcIsFavorite) ++ appFavorites.moveFavoriteToPos(id, favPos); ++ else ++ appFavorites.addFavoriteAtPos(id, favPos); ++ return false; ++ })); ++ ++ return true; ++ }, ++ ++ showShowAppsButton: function() { ++ this.showAppsButton.visible = true ++ this.showAppsButton.set_width(-1) ++ this.showAppsButton.set_height(-1) ++ }, ++ ++ hideShowAppsButton: function() { ++ this.showAppsButton.hide() ++ this.showAppsButton.set_width(0) ++ this.showAppsButton.set_height(0) ++ } ++}); ++ ++Signals.addSignalMethods(MyDash.prototype); ++ ++/** ++ * This is a copy of the same function in utils.js, but also adjust horizontal scrolling ++ * and perform few further cheks on the current value to avoid changing the values when ++ * it would be clamp to the current one in any case. ++ * Return the amount of shift applied ++ */ ++function ensureActorVisibleInScrollView(scrollView, actor) { ++ let adjust_v = true; ++ let adjust_h = true; ++ ++ let vadjustment = scrollView.vscroll.adjustment; ++ let hadjustment = scrollView.hscroll.adjustment; ++ let [vvalue, vlower, vupper, vstepIncrement, vpageIncrement, vpageSize] = vadjustment.get_values(); ++ let [hvalue, hlower, hupper, hstepIncrement, hpageIncrement, hpageSize] = hadjustment.get_values(); ++ ++ let [hvalue0, vvalue0] = [hvalue, vvalue]; ++ ++ let voffset = 0; ++ let hoffset = 0; ++ let fade = scrollView.get_effect('fade'); ++ if (fade) { ++ voffset = fade.vfade_offset; ++ hoffset = fade.hfade_offset; ++ } ++ ++ let box = actor.get_allocation_box(); ++ let y1 = box.y1, y2 = box.y2, x1 = box.x1, x2 = box.x2; ++ ++ let parent = actor.get_parent(); ++ while (parent != scrollView) { ++ if (!parent) ++ throw new Error('Actor not in scroll view'); ++ ++ let box = parent.get_allocation_box(); ++ y1 += box.y1; ++ y2 += box.y1; ++ x1 += box.x1; ++ x2 += box.x1; ++ parent = parent.get_parent(); ++ } ++ ++ if (y1 < vvalue + voffset) ++ vvalue = Math.max(0, y1 - voffset); ++ else if (vvalue < vupper - vpageSize && y2 > vvalue + vpageSize - voffset) ++ vvalue = Math.min(vupper -vpageSize, y2 + voffset - vpageSize); ++ ++ if (x1 < hvalue + hoffset) ++ hvalue = Math.max(0, x1 - hoffset); ++ else if (hvalue < hupper - hpageSize && x2 > hvalue + hpageSize - hoffset) ++ hvalue = Math.min(hupper - hpageSize, x2 + hoffset - hpageSize); ++ ++ if (vvalue !== vvalue0) { ++ Tweener.addTween(vadjustment, { value: vvalue, ++ time: Util.SCROLL_TIME, ++ transition: 'easeOutQuad' ++ }); ++ } ++ ++ if (hvalue !== hvalue0) { ++ Tweener.addTween(hadjustment, ++ { value: hvalue, ++ time: Util.SCROLL_TIME, ++ transition: 'easeOutQuad' }); ++ } ++ ++ return [hvalue- hvalue0, vvalue - vvalue0]; ++} +diff --git a/extensions/dash-to-dock/dockedDash.js b/extensions/dash-to-dock/dockedDash.js +new file mode 100644 +index 0000000..1e145c6 +--- /dev/null ++++ b/extensions/dash-to-dock/dockedDash.js +@@ -0,0 +1,1684 @@ ++// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- ++ ++const Clutter = imports.gi.Clutter; ++const GLib = imports.gi.GLib; ++const Gtk = imports.gi.Gtk; ++const Lang = imports.lang; ++const Meta = imports.gi.Meta; ++const Shell = imports.gi.Shell; ++const St = imports.gi.St; ++const Mainloop = imports.mainloop; ++const Params = imports.misc.params; ++ ++const Main = imports.ui.main; ++const Dash = imports.ui.dash; ++const IconGrid = imports.ui.iconGrid; ++const MessageTray = imports.ui.messageTray; ++const Overview = imports.ui.overview; ++const OverviewControls = imports.ui.overviewControls; ++const PointerWatcher = imports.ui.pointerWatcher; ++const Tweener = imports.ui.tweener; ++const Signals = imports.signals; ++const ViewSelector = imports.ui.viewSelector; ++const WorkspaceSwitcherPopup= imports.ui.workspaceSwitcherPopup; ++const Layout = imports.ui.layout; ++const LayoutManager = imports.ui.main.layoutManager; ++ ++const Me = imports.misc.extensionUtils.getCurrentExtension(); ++const Convenience = Me.imports.myConvenience; ++const Intellihide = Me.imports.intellihide; ++const MyDash = Me.imports.myDash; ++ ++const DOCK_DWELL_CHECK_INTERVAL = 100; //TODO ++ ++const State = { ++ HIDDEN: 0, ++ SHOWING: 1, ++ SHOWN: 2, ++ HIDING: 3 ++}; ++ ++/* Return the actual position reverseing left and right in rtl */ ++function getPosition(settings) { ++ let position = settings.get_enum('dock-position'); ++ if(Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) { ++ if (position == St.Side.LEFT) ++ position = St.Side.RIGHT; ++ else if (position == St.Side.RIGHT) ++ position = St.Side.LEFT; ++ } ++ return position; ++} ++ ++/* ++ * A simple St.Widget with one child whose allocation takes into account the ++ * slide out of its child via the _slidex parameter ([0:1]). ++ * ++ * Required since I want to track the input region of this container which is ++ * based on its allocation even if the child overlows the parent actor. By doing ++ * this the region of the dash that is slideout is not steling anymore the input ++ * regions making the extesion usable when the primary monitor is the right one. ++ * ++ * The slidex parameter can be used to directly animate the sliding. The parent ++ * must have a WEST (SOUTH) anchor_point to achieve the sliding to the RIGHT (BOTTOM) ++ * side. ++ * ++ * It can't be an extended object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973. ++ * thus use the Shell.GenericContainer pattern. ++*/ ++ ++const DashSlideContainer = new Lang.Class({ ++ Name: 'DashSlideContainer', ++ ++ _init: function(params) { ++ ++ /* Default local params */ ++ let localDefaults = { ++ side: St.Side.LEFT, ++ initialSlideValue: 1 ++ } ++ ++ let localParams = Params.parse(params, localDefaults, true); ++ ++ if (params){ ++ /* Remove local params before passing the params to the parent ++ constructor to avoid errors. */ ++ let prop; ++ for (prop in localDefaults) { ++ if ((prop in params)) ++ delete params[prop]; ++ } ++ } ++ ++ this.actor = new Shell.GenericContainer(params); ++ this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); ++ this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); ++ this.actor.connect('allocate', Lang.bind(this, this._allocate)); ++ ++ this.actor._delegate = this; ++ ++ this._child = null; ++ ++ // slide parameter: 1 = visible, 0 = hidden. ++ this._slidex = localParams.initialSlideValue; ++ this._side = localParams.side; ++ this._slideoutSize = 0; // minimum size when slided out ++ }, ++ ++ ++ _allocate: function(actor, box, flags) { ++ ++ if (this._child == null) ++ return; ++ ++ let availWidth = box.x2 - box.x1; ++ let availHeight = box.y2 - box.y1; ++ let [minChildWidth, minChildHeight, natChildWidth, natChildHeight] = ++ this._child.get_preferred_size(); ++ ++ let childWidth = natChildWidth; ++ let childHeight = natChildHeight; ++ ++ let childBox = new Clutter.ActorBox(); ++ ++ let slideoutSize = this._slideoutSize; ++ ++ if (this._side == St.Side.LEFT) { ++ childBox.x1 = (this._slidex -1)*(childWidth - slideoutSize); ++ childBox.x2 = slideoutSize + this._slidex*(childWidth - slideoutSize); ++ childBox.y1 = 0; ++ childBox.y2 = childBox.y1 + childHeight; ++ } else if (this._side == St.Side.RIGHT ++ || this._side == St.Side.BOTTOM) { ++ childBox.x1 = 0; ++ childBox.x2 = childWidth; ++ childBox.y1 = 0; ++ childBox.y2 = childBox.y1 + childHeight; ++ } else if (this._side == St.Side.TOP) { ++ childBox.x1 = 0; ++ childBox.x2 = childWidth; ++ childBox.y1 = (this._slidex -1)*(childHeight - slideoutSize); ++ childBox.y2 = slideoutSize + this._slidex*(childHeight - slideoutSize); ++ } ++ ++ this._child.allocate(childBox, flags); ++ this._child.set_clip(-childBox.x1, -childBox.y1, ++ -childBox.x1+availWidth,-childBox.y1 + availHeight); ++ }, ++ ++ /* Just the child width but taking into account the slided out part */ ++ _getPreferredWidth: function(actor, forHeight, alloc) { ++ let [minWidth, natWidth ] = this._child.get_preferred_width(forHeight); ++ if (this._side == St.Side.LEFT ++ || this._side == St.Side.RIGHT) { ++ minWidth = (minWidth - this._slideoutSize)*this._slidex + this._slideoutSize; ++ natWidth = (natWidth - this._slideoutSize)*this._slidex + this._slideoutSize; ++ } ++ ++ alloc.min_size = minWidth; ++ alloc.natural_size = natWidth; ++ }, ++ ++ /* Just the child height but taking into account the slided out part */ ++ _getPreferredHeight: function(actor, forWidth, alloc) { ++ let [minHeight, natHeight] = this._child.get_preferred_height(forWidth); ++ if (this._side == St.Side.TOP ++ || this._side == St.Side.BOTTOM) { ++ minHeight = (minHeight - this._slideoutSize)*this._slidex + this._slideoutSize; ++ natHeight = (natHeight - this._slideoutSize)*this._slidex + this._slideoutSize; ++ } ++ alloc.min_size = minHeight; ++ alloc.natural_size = natHeight; ++ }, ++ ++ /* I was expecting it to be a virtual function... stil I don't understand ++ how things work. ++ */ ++ add_child: function(actor) { ++ ++ /* I'm supposed to have only on child */ ++ if(this._child !== null) { ++ this.actor.remove_child(actor); ++ } ++ ++ this._child = actor; ++ this.actor.add_child(actor); ++ }, ++ ++ set slidex(value) { ++ this._slidex = value; ++ this._child.queue_relayout(); ++ }, ++ ++ get slidex() { ++ return this._slidex; ++ } ++ ++}); ++ ++const dockedDash = new Lang.Class({ ++ Name: 'dockedDash', ++ ++ _init: function(settings) { ++ ++ this._rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL; ++ ++ // Load settings ++ this._settings = settings; ++ this._bindSettingsChanges(); ++ ++ this._position = getPosition(settings); ++ this._isHorizontal = ( this._position == St.Side.TOP || ++ this._position == St.Side.BOTTOM ); ++ ++ // Temporary ignore hover events linked to autohide for whatever reason ++ this._ignoreHover = false; ++ this._oldignoreHover = null; ++ // This variables are linked to the settings regardles of autohide or intellihide ++ // being temporary disable. Get set by _updateVisibilityMode; ++ this._autohideIsEnabled = null; ++ this._intellihideIsEnabled = null; ++ this._fixedIsEnabled = null; ++ ++ // Create intellihide object to monitor windows overlapping ++ this._intellihide = new Intellihide.intellihide(this._settings); ++ ++ // initialize dock state ++ this._dockState = State.HIDDEN; ++ ++ /* status variable: true when the overview is shown through the dash ++ * applications button. ++ */ ++ this.forcedOverview = false; ++ ++ // Put dock on the primary monitor ++ this._monitor = Main.layoutManager.primaryMonitor; ++ ++ // this store size and the position where the dash is shown; ++ // used by intellihide module to check window overlap. ++ this.staticBox = new Clutter.ActorBox(); ++ ++ // Initialize pressure barrier variables ++ this._canUsePressure = false; ++ this._pressureBarrier = null; ++ this._barrier = null; ++ this._messageTrayShowing = false; ++ this._removeBarrierTimeoutId = 0; ++ ++ // Initialize dwelling system variables ++ this._dockDwelling = false; ++ this._dockWatch = null; ++ this._dockDwellUserTime = 0; ++ this._dockDwellTimeoutId = 0 ++ ++ // Create a new dash object ++ this.dash = new MyDash.myDash(this._settings); ++ ++ // set stored icon size to the new dash ++ Main.overview.dashIconSize = this.dash.iconSize; ++ ++ // connect app icon into the view selector ++ this.dash.showAppsButton.connect('notify::checked', Lang.bind(this, this._onShowAppsButtonToggled)); ++ ++ // Create the main actor and the containers for sliding in and out and ++ // centering, turn on track hover ++ ++ let positionStyleClass = ['top', 'right', 'bottom', 'left']; ++ // This is the centering actor ++ this.actor = new St.Bin({ name: 'dashtodockContainer',reactive: false, ++ style_class:positionStyleClass[this._position], ++ x_align: this._isHorizontal?St.Align.MIDDLE:St.Align.START, ++ y_align: this._isHorizontal?St.Align.START:St.Align.MIDDLE}); ++ this.actor._delegate = this; ++ ++ // This is the sliding actor whose allocation is to be tracked for input regions ++ this._slider = new DashSlideContainer({side: this._position, initialSlideValue: 0}); ++ ++ // This is the actor whose hover status us tracked for autohide ++ this._box = new St.BoxLayout({ name: 'dashtodockBox', reactive: true, track_hover:true } ); ++ this._box.connect("notify::hover", Lang.bind(this, this._hoverChanged)); ++ ++ // Create and apply height constraint to the dash. It's controlled by this.actor height ++ this.constrainSize = new Clutter.BindConstraint({ source: this.actor, ++ coordinate: this._isHorizontal?Clutter.BindCoordinate.WIDTH:Clutter.BindCoordinate.HEIGHT }); ++ this.dash.actor.add_constraint(this.constrainSize); ++ ++ // Connect global signals ++ this._signalsHandler = new Convenience.GlobalSignalsHandler(); ++ this._signalsHandler.add( ++ [ ++ Main.overview, ++ 'item-drag-begin', ++ Lang.bind(this, this._onDragStart) ++ ], ++ [ ++ Main.overview, ++ 'item-drag-end', ++ Lang.bind(this, this._onDragEnd) ++ ], ++ [ ++ Main.overview, ++ 'item-drag-cancelled', ++ Lang.bind(this, this._onDragEnd) ++ ], ++ // update wne monitor changes, for instance in multimonitor when monitor are attached ++ [ ++ global.screen, ++ 'monitors-changed', ++ Lang.bind(this, this._resetPosition ) ++ ], ++ [ ++ Main.overview, ++ 'showing', ++ Lang.bind(this, this._onOverviewShowing) ++ ], ++ [ ++ Main.overview, ++ 'hiding', ++ Lang.bind(this, this._onOverviewHiding) ++ ], ++ // Hide on appview ++ [ ++ Main.overview.viewSelector, ++ 'page-changed', ++ Lang.bind(this, this._pageChanged) ++ ], ++ [ ++ Main.overview.viewSelector, ++ 'page-empty', ++ Lang.bind(this, this._onPageEmpty) ++ ], ++ // Ensure the ShowAppsButton status is kept in sync ++ [ ++ Main.overview.viewSelector._showAppsButton, ++ 'notify::checked', ++ Lang.bind(this, this._syncShowAppsButtonToggled) ++ ], ++ [ ++ Main.messageTray, ++ 'showing', ++ Lang.bind(this, this._onMessageTrayShowing) ++ ], ++ [ ++ Main.messageTray, ++ 'hiding', ++ Lang.bind(this, this._onMessageTrayHiding) ++ ], ++ // Monitor windows overlapping ++ [ ++ this._intellihide, ++ 'status-changed', ++ Lang.bind(this, this._updateDashVisibility) ++ ], ++ // Keep dragged icon consistent in size with this dash ++ [ ++ this.dash, ++ 'icon-size-changed', ++ Lang.bind(this, function() { ++ Main.overview.dashIconSize = this.dash.iconSize; ++ }) ++ ], ++ // This duplicate the similar signal which is in owerview.js. ++ // Being connected and thus executed later this effectively ++ // overwrite any attempt to use the size of the default dash ++ //which given the customization is usually much smaller. ++ // I can't easily disconnect the original signal ++ [ ++ Main.overview._controls.dash, ++ 'icon-size-changed', ++ Lang.bind(this, function() { ++ Main.overview.dashIconSize = this.dash.iconSize; ++ }) ++ ] ++ ); ++ ++ this._injectionsHandler = new Convenience.InjectionsHandler(); ++ this._themeManager = new themeManager(this._settings, this.actor, this.dash); ++ ++ // Since the actor is not a topLevel child and its parent is now not added to the Chrome, ++ // the allocation change of the parent container (slide in and slideout) doesn't trigger ++ // anymore an update of the input regions. Force the update manually. ++ this.actor.connect('notify::allocation', ++ Lang.bind(Main.layoutManager, Main.layoutManager._queueUpdateRegions)); ++ ++ this.dash._container.connect('allocation-changed', Lang.bind(this, this._updateStaticBox)); ++ this._slider.actor.connect(this._isHorizontal?'notify::x':'notify::y', Lang.bind(this, this._updateStaticBox)); ++ ++ // sync hover after a popupmenu is closed ++ this.dash.connect('menu-closed', Lang.bind(this, function(){this._box.sync_hover();})); ++ ++ // Restore dash accessibility ++ Main.ctrlAltTabManager.addGroup( ++ this.dash.actor, _("Dash"),'user-bookmarks-symbolic', ++ {focusCallback: Lang.bind(this, this._onAccessibilityFocus)}); ++ ++ // Load optional features ++ this._optionalScrollWorkspaceSwitch(); ++ ++ // Delay operations that require the shell to be fully loaded and with ++ // user theme applied. ++ ++ this._paintId = this.actor.connect("paint", Lang.bind(this, this._initialize)); ++ ++ // Hide usual Dash ++ Main.overview._controls.dash.actor.hide(); ++ ++ // Also set dash width to 1, so it's almost not taken into account by code ++ // calculaing the reserved space in the overview. The reason to keep it at 1 is ++ // to allow its visibility change to trigger an allocaion of the appGrid which ++ // in turn is triggergin the appsIcon spring animation, required when no other ++ // actors has this effect, i.e in horizontal mode and without the workspaceThumnails ++ // 1 static workspace only) ++ Main.overview._controls.dash.actor.set_width(1); ++ ++ // Manage the which is used to reserve space in the overview for the dock ++ // Add and additional dashSpacer positioned according to the dash positioning. ++ // It gets restored on extension unload. ++ this._dashSpacer = new OverviewControls.DashSpacer(); ++ this._dashSpacer.setDashActor(this._box); ++ ++ // shift overview messageIndicator for bottom dock ++ this._oldMessageIndicatorPosition = Main.overview._controls._indicator.actor.get_first_child().y; ++ if (this._position == St.Side.BOTTOM) { ++ this._dashSpacer.connect('notify::height', Lang.bind(this, function(){ ++ Main.overview._controls._indicator.actor.get_first_child().y = -this._dashSpacer.height; ++ })); ++ } ++ ++ if (this._position == St.Side.LEFT) ++ Main.overview._controls._group.insert_child_at_index(this._dashSpacer, this._rtl?-1:0); // insert on first ++ else if (this._position == St.Side.RIGHT) ++ Main.overview._controls._group.insert_child_at_index(this._dashSpacer, this._rtl?0:-1); // insert on last ++ else if (this._position == St.Side.TOP) ++ Main.overview._overview.insert_child_at_index(this._dashSpacer, 0); ++ else if (this._position == St.Side.BOTTOM) ++ Main.overview._overview.insert_child_at_index(this._dashSpacer, -1); ++ ++ // Add dash container actor and the container to the Chrome. ++ this.actor.set_child(this._slider.actor); ++ this._slider.add_child(this._box); ++ this._box.add_actor(this.dash.actor); ++ ++ // Add aligning container without tracking it for input region (old affectsinputRegion: false that was removed). ++ // The public method trackChrome requires the actor to be child of a tracked actor. Since I don't want the parent ++ // to be tracked I use the private internal _trackActor instead. ++ Main.uiGroup.add_child(this.actor); ++ Main.layoutManager._trackActor(this._slider.actor, {trackFullscreen: true}); ++ ++ // Keep the dash below the modalDialogGroup ++ Main.layoutManager.uiGroup.set_child_below_sibling(this.actor,Main.layoutManager.modalDialogGroup); ++ ++ if ( this._settings.get_boolean('dock-fixed') ) ++ Main.layoutManager._trackActor(this.dash.actor, {affectsStruts: true}); ++ ++ // pretend this._slider is isToplevel child so that fullscreen is actually tracked ++ let index = Main.layoutManager._findActor(this._slider.actor); ++ Main.layoutManager._trackedActors[index].isToplevel = true ; ++ ++ // Set initial position ++ this._resetPosition(); ++ ++ }, ++ ++ _initialize: function(){ ++ ++ if(this._paintId>0){ ++ this.actor.disconnect(this._paintId); ++ this._paintId=0; ++ } ++ ++ this.dash.setIconSize(this._settings.get_int('dash-max-icon-size'), true); ++ ++ // Apply custome css class according to the settings ++ this._themeManager.updateCustomTheme(); ++ ++ // Since Gnome 3.8 dragging an app without having opened the overview before cause the attemp to ++ //animate a null target since some variables are not initialized when the viewSelector is created ++ if(Main.overview.viewSelector._activePage == null) ++ Main.overview.viewSelector._activePage = Main.overview.viewSelector._workspacesPage; ++ ++ this._updateVisibilityMode(); ++ ++ // Setup pressure barrier (GS38+ only) ++ this._updatePressureBarrier(); ++ this._updateBarrier(); ++ ++ // setup dwelling system if pressure barriers are not available ++ this._setupDockDwellIfNeeded(); ++ ++ // Insensitive Message Tray ++ this._updateInsensitiveTray(); ++ }, ++ ++ destroy: function(){ ++ ++ // Disconnect global signals ++ this._signalsHandler.destroy(); ++ // The dash and intellihide have global signals as well internally ++ this.dash.destroy(); ++ this._intellihide.destroy(); ++ ++ this._injectionsHandler.destroy(); ++ ++ // Destroy main clutter actor: this should be sufficient removing it and ++ // destroying all its children ++ this.actor.destroy(); ++ ++ // Remove barrier timeout ++ if (this._removeBarrierTimeoutId > 0) ++ Mainloop.source_remove(this._removeBarrierTimeoutId); ++ ++ // Remove existing barrier ++ this._removeBarrier(); ++ ++ // Remove pointer watcher ++ if(this._dockWatch){ ++ PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch); ++ this._dockWatch = null; ++ } ++ ++ // Remove the dashSpacer ++ this._dashSpacer.destroy(); ++ ++ // restore messageIndicator position ++ Main.overview._controls._indicator.actor.get_first_child().y = this._oldMessageIndicatorPosition; ++ ++ // Reshow normal dash previously hidden, restore panel position if changed. ++ Main.overview._controls.dash.actor.show(); ++ Main.overview._controls.dash.actor.set_width(-1); //reset default dash size ++ // This force the recalculation of the icon size ++ Main.overview._controls.dash._maxHeight = -1; ++ ++ // reset stored icon size to the default dash ++ Main.overview.dashIconSize = Main.overview._controls.dash.iconSize; ++ // Reshow panel corners ++ this._revertPanelCorners(); ++ }, ++ ++ _bindSettingsChanges: function() { ++ ++ this._settings.connect('changed::scroll-switch-workspace', Lang.bind(this, function(){ ++ this._optionalScrollWorkspaceSwitch(this._settings.get_boolean('scroll-switch-workspace')); ++ })); ++ ++ this._settings.connect('changed::dash-max-icon-size', Lang.bind(this, function(){ ++ this.dash.setIconSize(this._settings.get_int('dash-max-icon-size')); ++ })); ++ ++ this._settings.connect('changed::icon-size-fixed', Lang.bind(this, function(){ ++ this.dash.setIconSize(this._settings.get_int('dash-max-icon-size')); ++ })); ++ ++ this._settings.connect('changed::show-running', Lang.bind(this, function(){ ++ this.dash.resetAppIcons(); ++ })); ++ ++ this._settings.connect('changed::show-apps-at-top', Lang.bind(this, function(){ ++ this.dash.resetAppIcons(); ++ })); ++ ++ this._settings.connect('changed::dock-fixed', Lang.bind(this, function(){ ++ ++ if(this._settings.get_boolean('dock-fixed')) { ++ Main.layoutManager._trackActor(this.dash.actor, {affectsStruts: true}); ++ } else { ++ Main.layoutManager._untrackActor(this.dash.actor); ++ } ++ ++ this._resetPosition(); ++ ++ // Add or remove barrier depending on if dock-fixed ++ this._updateBarrier(); ++ ++ this._updateVisibilityMode(); ++ })); ++ ++ this._settings.connect('changed::intellihide', Lang.bind(this, this._updateVisibilityMode)); ++ ++ this._settings.connect('changed::intellihide-perapp', Lang.bind(this, function(){ ++ this._intellihide.forceUpdate(); ++ })); ++ ++ this._settings.connect('changed::autohide', Lang.bind(this, function(){ ++ this._updateVisibilityMode(); ++ this._updateBarrier(); ++ })); ++ this._settings.connect('changed::extend-height', Lang.bind(this,this._resetPosition)); ++ this._settings.connect('changed::preferred-monitor', Lang.bind(this,this._resetPosition)); ++ this._settings.connect('changed::height-fraction', Lang.bind(this,this._resetPosition)); ++ this._settings.connect('changed::insensitive-message-tray', Lang.bind(this,this._updateInsensitiveTray)); ++ this._settings.connect('changed::require-pressure-to-show', Lang.bind(this,function(){ ++ // Remove pointer watcher ++ if(this._dockWatch){ ++ PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch); ++ this._dockWatch = null; ++ } ++ this._setupDockDwellIfNeeded(); ++ this._updateBarrier(); ++ })); ++ this._settings.connect('changed::pressure-threshold', Lang.bind(this,function() { ++ this._updatePressureBarrier(); ++ this._updateBarrier(); ++ })); ++ ++ }, ++ ++ // This is call when visibility settings change ++ _updateVisibilityMode: function() { ++ ++ if (this._settings.get_boolean('dock-fixed')) { ++ this._fixedIsEnabled = true; ++ this._autohideIsEnabled = false; ++ this._intellihideIsEnabled = false; ++ } else { ++ this._fixedIsEnabled = false; ++ this._autohideIsEnabled = this._settings.get_boolean('autohide') ++ this._intellihideIsEnabled = this._settings.get_boolean('intellihide') ++ } ++ ++ if (this._intellihideIsEnabled) ++ this._intellihide.enable(); ++ else ++ this._intellihide.disable(); ++ ++ this._updateDashVisibility(); ++ }, ++ ++ /* Show/hide dash based on, in order of priority: ++ * overview visibility ++ * fixed mode ++ * intellihide ++ * autohide ++ * overview visibility ++ */ ++ _updateDashVisibility: function() { ++ ++ if (Main.overview.visibleTarget) ++ return; ++ ++ if ( this._fixedIsEnabled ) { ++ this._removeAnimations(); ++ this._animateIn(this._settings.get_double('animation-time'), 0); ++ } else if (this._intellihideIsEnabled) { ++ if ( this._intellihide.getOverlapStatus() ) { ++ this._ignoreHover = false; ++ // Do not hide if autohide is enabled and mouse is hover ++ if (!this._box.hover || !this._autohideIsEnabled) { ++ this._animateOut(this._settings.get_double('animation-time'), 0); ++ } ++ } else { ++ this._ignoreHover = true; ++ this._removeAnimations(); ++ this._animateIn(this._settings.get_double('animation-time'), 0); ++ } ++ } else { ++ if (this._autohideIsEnabled) { ++ this._ignoreHover = false; ++ global.sync_pointer(); ++ ++ if( this._box.hover ) { ++ this._animateIn(this._settings.get_double('animation-time'), 0); ++ } else { ++ this._animateOut(this._settings.get_double('animation-time'), 0); ++ } ++ ++ } else { ++ this._animateOut(this._settings.get_double('animation-time'), 0); ++ } ++ } ++ }, ++ ++ _onOverviewShowing: function() { ++ this._ignoreHover = true; ++ this._intellihide.disable(); ++ this._removeAnimations(); ++ this._animateIn(this._settings.get_double('animation-time'), 0); ++ }, ++ ++ _onOverviewHiding: function() { ++ this._ignoreHover = false; ++ this._intellihide.enable(); ++ this._updateDashVisibility(); ++ }, ++ ++ _hoverChanged: function() { ++ ++ if (!this._ignoreHover) { ++ ++ // Skip if dock is not in autohide mode for instance because it is shown ++ // by intellihide. ++ if(this._autohideIsEnabled) { ++ if( this._box.hover ) { ++ this._show(); ++ } else { ++ this._hide(); ++ } ++ } ++ } ++ }, ++ ++ _show: function() { ++ ++ if ( this._dockState == State.HIDDEN || this._dockState == State.HIDING ) { ++ ++ if(this._dockState == State.HIDING){ ++ // suppress all potential queued hiding animations - i.e. added to Tweener but not started, ++ // always give priority to show ++ this._removeAnimations(); ++ } ++ ++ this.emit("showing"); ++ this._animateIn(this._settings.get_double('animation-time'), 0); ++ } ++ }, ++ ++ _hide: function() { ++ ++ // If no hiding animation is running or queued ++ if ( this._dockState == State.SHOWN || this._dockState == State.SHOWING ) { ++ ++ let delay; ++ ++ if (this._dockState == State.SHOWING) { ++ //if a show already started, let it finish; queue hide without removing the show. ++ // to obtain this I increase the delay to avoid the overlap and interference ++ // between the animations ++ delay = this._settings.get_double('hide-delay') + this._settings.get_double('animation-time'); ++ } else { ++ delay = this._settings.get_double('hide-delay'); ++ } ++ ++ this.emit("hiding"); ++ this._animateOut(this._settings.get_double('animation-time'), delay); ++ ++ } ++ }, ++ ++ _animateIn: function(time, delay) { ++ ++ this._dockState = State.SHOWING; ++ ++ Tweener.addTween(this._slider,{ ++ slidex: 1, ++ time: time, ++ delay: delay, ++ transition: 'easeOutQuad', ++ onComplete: Lang.bind(this, function() { ++ this._dockState = State.SHOWN; ++ // Remove barrier so that mouse pointer is released and can access monitors on other side of dock ++ // NOTE: Delay needed to keep mouse from moving past dock and re-hiding dock immediately. This ++ // gives users an opportunity to hover over the dock ++ if (this._removeBarrierTimeoutId > 0) { ++ Mainloop.source_remove(this._removeBarrierTimeoutId); ++ } ++ this._removeBarrierTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, this._removeBarrier)); ++ }) ++ }); ++ }, ++ ++ _animateOut: function(time, delay){ ++ ++ this._dockState = State.HIDING; ++ Tweener.addTween(this._slider,{ ++ slidex: 0, ++ time: time, ++ delay: delay , ++ transition: 'easeOutQuad', ++ onComplete: Lang.bind(this, function() { ++ this._dockState = State.HIDDEN; ++ this._updateBarrier(); ++ }) ++ }); ++ }, ++ ++ // Dwelling system based on the GNOME Shell 3.14 messageTray code. ++ _setupDockDwellIfNeeded: function() { ++ // If we don't have extended barrier features, then we need ++ // to support the old tray dwelling mechanism. ++ if (!global.display.supports_extended_barriers() || !this._settings.get_boolean('require-pressure-to-show')) { ++ let pointerWatcher = PointerWatcher.getPointerWatcher(); ++ this._dockWatch = pointerWatcher.addWatch(DOCK_DWELL_CHECK_INTERVAL, Lang.bind(this, this._checkDockDwell)); ++ this._dockDwelling = false; ++ this._dockDwellUserTime = 0; ++ } ++ }, ++ ++ _checkDockDwell: function(x, y) { ++ let monitor = this._monitor; ++ ++ // Check for the dock area ++ let shouldDwell = (x >= this.staticBox.x1 && x <= this.staticBox.x2 && ++ y >= this.staticBox.y1 && y <= this.staticBox.y2); ++ ++ // Check for the correct screen edge ++ // Position is approximated to the lower integer ++ if(this._position==St.Side.LEFT){ ++ shouldDwell = shouldDwell && x == this._monitor.x; ++ } else if(this._position==St.Side.RIGHT) { ++ shouldDwell = shouldDwell && x == this._monitor.x + this._monitor.width - 1; ++ } else if(this._position==St.Side.TOP) { ++ shouldDwell = shouldDwell && y == this._monitor.y; ++ } else if (this._position==St.Side.BOTTOM) { ++ shouldDwell = shouldDwell && y == this._monitor.y + this._monitor.height - 1; ++ } ++ ++ if (shouldDwell) { ++ // We only set up dwell timeout when the user is not hovering over the dock ++ // already (!this._box._hover). ++ // The _dockDwelling variable is used so that we only try to ++ // fire off one dock dwell - if it fails (because, say, the user has the mouse down), ++ // we don't try again until the user moves the mouse up and down again. ++ if (!this._dockDwelling && !this._box._hover && this._dockDwellTimeoutId == 0) { ++ // Save the interaction timestamp so we can detect user input ++ let focusWindow = global.display.focus_window; ++ this._dockDwellUserTime = focusWindow ? focusWindow.user_time : 0; ++ ++ this._dockDwellTimeoutId = Mainloop.timeout_add(this._settings.get_double('show-delay')*1000, ++ Lang.bind(this, this._dockDwellTimeout)); ++ GLib.Source.set_name_by_id(this._dockDwellTimeoutId, '[dash-to-dock] this._dockDwellTimeout'); ++ } ++ this._dockDwelling = true; ++ } else { ++ this._cancelDockDwell(); ++ this._dockDwelling = false; ++ } ++ }, ++ ++ _cancelDockDwell: function() { ++ if (this._dockDwellTimeoutId != 0) { ++ Mainloop.source_remove(this._dockDwellTimeoutId); ++ this._dockDwellTimeoutId = 0; ++ } ++ }, ++ ++ _dockDwellTimeout: function() { ++ this._dockDwellTimeoutId = 0; ++ ++ if (this._monitor.inFullscreen) ++ return GLib.SOURCE_REMOVE; ++ ++ // We don't want to open the tray when a modal dialog ++ // is up, so we check the modal count for that. When we are in the ++ // overview we have to take the overview's modal push into account ++ if (Main.modalCount > (Main.overview.visible ? 1 : 0)) ++ return GLib.SOURCE_REMOVE; ++ ++ // If the user interacted with the focus window since we started the tray ++ // dwell (by clicking or typing), don't activate the message tray ++ let focusWindow = global.display.focus_window; ++ let currentUserTime = focusWindow ? focusWindow.user_time : 0; ++ if (currentUserTime != this._dockDwellUserTime) ++ return GLib.SOURCE_REMOVE; ++ ++ // Reuse the pressure version function, the logic is the same ++ this._onPressureSensed(); ++ return GLib.SOURCE_REMOVE; ++ }, ++ ++ _updatePressureBarrier: function() { ++ this._canUsePressure = global.display.supports_extended_barriers(); ++ let pressureThreshold = this._settings.get_double('pressure-threshold'); ++ ++ // Remove existing pressure barrier ++ if (this._pressureBarrier) { ++ this._pressureBarrier.destroy(); ++ this._pressureBarrier = null; ++ } ++ ++ // Create new pressure barrier based on pressure threshold setting ++ if (this._canUsePressure) { ++ this._pressureBarrier = new Layout.PressureBarrier(pressureThreshold, this._settings.get_double('show-delay')*1000, ++ Shell.KeyBindingMode.NORMAL | Shell.KeyBindingMode.OVERVIEW); ++ this._pressureBarrier.connect('trigger', Lang.bind(this, function(barrier){ ++ if (this._monitor.inFullscreen) ++ return; ++ this._onPressureSensed(); ++ })); ++ } ++ }, ++ ++ // handler for mouse pressure sensed ++ _onPressureSensed: function() { ++ ++ if (Main.overview.visibleTarget) ++ return; ++ ++ // In case the mouse move away from the dock area before hovering it, in such case the leave event ++ // would never be triggered and the dock would stay visible forever. ++ let triggerTimeoutId = Mainloop.timeout_add(250, ++ Lang.bind(this, function() { ++ triggerTimeoutId = 0; ++ this._hoverChanged(); ++ return GLib.SOURCE_REMOVE; ++ })); ++ ++ this._show(); ++ }, ++ ++ _onMessageTrayShowing: function() { ++ ++ // Temporary move the dash below the top panel so that it slide below it. ++ Main.layoutManager.uiGroup.set_child_below_sibling(this.actor, Main.layoutManager.panelBox); ++ ++ // Remove other tweens that could mess with the state machine ++ Tweener.removeTweens(this.actor); ++ this._oldignoreHover = this._ignoreHover; ++ this._ignoreHover = true; ++ this.dash.cleanUpLabels(); ++ Tweener.addTween(this.actor, { ++ y: this._y0 - Main.messageTray.actor.height, ++ time: MessageTray.ANIMATION_TIME, ++ transition: 'easeOutQuad' ++ }); ++ this._messageTrayShowing = true; ++ this._updateBarrier(); ++ }, ++ ++ _onMessageTrayHiding: function() { ++ ++ // Remove other tweens that could mess with the state machine ++ Tweener.removeTweens(this.actor); ++ Tweener.addTween(this.actor, { ++ y: this._y0, ++ time: MessageTray.ANIMATION_TIME, ++ transition: 'easeOutQuad', ++ onComplete: Lang.bind(this, function(){ ++ // Reset desired dash stack order (on top to accept dnd of app icons) ++ Main.layoutManager.uiGroup.set_child_below_sibling(this.actor, Main.layoutManager.modalDialogGroup); ++ // restore previous ignoreHover. If it was not set, set it to false ++ if (this._oldignoreHover !== null) ++ this._ignoreHover = this._oldignoreHover; ++ this._oldignoreHover == null; ++ if (!isMouseHover(this._box)) ++ this._box.hover = false; ++ }) ++ }); ++ ++ this._messageTrayShowing = false; ++ this._updateBarrier(); ++ }, ++ ++ // Remove pressure barrier ++ _removeBarrier: function() { ++ if (this._barrier) { ++ if (this._pressureBarrier) { ++ this._pressureBarrier.removeBarrier(this._barrier); ++ } ++ this._barrier.destroy(); ++ this._barrier = null; ++ } ++ this._removeBarrierTimeoutId = 0; ++ return false; ++ }, ++ ++ // Update pressure barrier size ++ _updateBarrier: function() { ++ // Remove existing barrier ++ this._removeBarrier(); ++ ++ // Manually reset pressure barrier ++ // This is necessary because we remove the pressure barrier when it is triggered to show the dock ++ if (this._pressureBarrier) { ++ this._pressureBarrier._reset(); ++ this._pressureBarrier._isTriggered = false; ++ } ++ ++ // Create new barrier ++ // Note: dash in fixed position doesn't use pressure barrier ++ if (this._slider.actor.visible && this._canUsePressure && this._autohideIsEnabled && this._settings.get_boolean('require-pressure-to-show') && !this._messageTrayShowing) { ++ let x1, x2, y1, y2, direction; ++ ++ if(this._position==St.Side.LEFT){ ++ x1 = this.staticBox.x1; ++ x2 = this.staticBox.x1; ++ y1 = this.staticBox.y1; ++ y2 = this.staticBox.y2; ++ direction = Meta.BarrierDirection.POSITIVE_X; ++ } else if(this._position==St.Side.RIGHT) { ++ x1 = this.staticBox.x2; ++ x2 = this.staticBox.x2; ++ y1 = this.staticBox.y1; ++ y2 = this.staticBox.y2; ++ direction = Meta.BarrierDirection.NEGATIVE_X; ++ } else if(this._position==St.Side.TOP) { ++ x1 = this.staticBox.x1; ++ x2 = this.staticBox.x2; ++ y1 = this.staticBox.y1; ++ y2 = this.staticBox.y1; ++ direction = Meta.BarrierDirection.POSITIVE_Y; ++ } else if (this._position==St.Side.BOTTOM) { ++ x1 = this.staticBox.x1; ++ x2 = this.staticBox.x2; ++ y1 = this.staticBox.y2; ++ y2 = this.staticBox.y2; ++ direction = Meta.BarrierDirection.NEGATIVE_Y; ++ } ++ ++ this._barrier = new Meta.Barrier({display: global.display, ++ x1: x1, x2: x2, ++ y1: y1, y2: y2, ++ directions: direction}); ++ if (this._pressureBarrier) { ++ this._pressureBarrier.addBarrier(this._barrier); ++ } ++ } ++ ++ }, ++ ++ _isPrimaryMonitor: function() { ++ return (this._monitor.x == Main.layoutManager.primaryMonitor.x && ++ this._monitor.y == Main.layoutManager.primaryMonitor.y); ++ }, ++ ++ _resetPosition: function() { ++ ++ // Ensure variables linked to settings are updated. ++ this._updateVisibilityMode(); ++ ++ this._monitor = this._getMonitor(); ++ ++ let unavailableTopSpace = 0; ++ let unavailableBottomSpace = 0; ++ ++ let extendHeight = this._settings.get_boolean('extend-height'); ++ ++ // Reserve space for the dash on the overview ++ // if the dock is on the primary monitor ++ if (this._isPrimaryMonitor()){ ++ unavailableTopSpace = Main.panel.actor.height; ++ this._dashSpacer.show(); ++ } else { ++ // No space is required in the overview of the dash ++ this._dashSpacer.hide(); ++ } ++ ++ let fraction = this._settings.get_double('height-fraction'); ++ ++ if(extendHeight) ++ fraction = 1; ++ else if(fraction<0 || fraction >1) ++ fraction = 0.95; ++ ++ let anchor_point; ++ ++ if(this._isHorizontal){ ++ ++ let availableWidth = this._monitor.width; ++ this.actor.width = Math.round( fraction * availableWidth); ++ ++ let pos_y; ++ if( this._position == St.Side.BOTTOM) { ++ pos_y = this._monitor.y + this._monitor.height; ++ anchor_point = Clutter.Gravity.SOUTH_WEST; ++ } else { ++ pos_y = this._monitor.y + unavailableTopSpace; ++ anchor_point = Clutter.Gravity.NORTH_WEST; ++ } ++ ++ this.actor.move_anchor_point_from_gravity(anchor_point); ++ this.actor.x = this._monitor.x + Math.round( (1-fraction)/2 * availableWidth); ++ this.actor.y = pos_y; ++ ++ if(extendHeight){ ++ this.dash._container.set_width(this.actor.width); ++ this.actor.add_style_class_name('extended'); ++ } else { ++ this.dash._container.set_width(-1); ++ this.actor.remove_style_class_name('extended'); ++ } ++ ++ } else { ++ ++ let availableHeight = this._monitor.height - unavailableTopSpace - unavailableBottomSpace; ++ this.actor.height = Math.round( fraction * availableHeight); ++ ++ let pos_x; ++ if( this._position == St.Side.RIGHT) { ++ pos_x = this._monitor.x + this._monitor.width; ++ anchor_point = Clutter.Gravity.NORTH_EAST; ++ } else { ++ pos_x = this._monitor.x; ++ anchor_point = Clutter.Gravity.NORTH_WEST; ++ } ++ ++ this.actor.move_anchor_point_from_gravity(anchor_point); ++ this.actor.x = pos_x; ++ this.actor.y = this._monitor.y + unavailableTopSpace + Math.round( (1-fraction)/2 * availableHeight); ++ ++ if(extendHeight){ ++ this.dash._container.set_height(this.actor.height); ++ this.actor.add_style_class_name('extended'); ++ } else { ++ this.dash._container.set_height(-1); ++ this.actor.remove_style_class_name('extended'); ++ } ++ } ++ ++ this._y0 = this.actor.y; ++ this._adjustPanelCorners(); ++ ++ this._updateStaticBox(); ++ }, ++ ++ _updateStaticBox: function() { ++ ++ this.staticBox.init_rect( ++ this.actor.x + this._slider.actor.x - (this._position==St.Side.RIGHT?this._box.width:0), ++ this.actor.y + this._slider.actor.y - (this._position==St.Side.BOTTOM?this._box.height:0), ++ this._box.width, ++ this._box.height ++ ); ++ ++ this._intellihide.updateTargetBox(this.staticBox); ++ }, ++ ++ // Adjust Panel corners ++ _adjustPanelCorners: function() { ++ let extendHeight = this._settings.get_boolean('extend-height'); ++ if (!this._isHorizontal && this._isPrimaryMonitor() && extendHeight && this._fixedIsEnabled) { ++ Main.panel._rightCorner.actor.hide(); ++ Main.panel._leftCorner.actor.hide(); ++ } else { ++ this._revertPanelCorners(); ++ } ++ }, ++ ++ _revertPanelCorners: function() { ++ Main.panel._leftCorner.actor.show(); ++ Main.panel._rightCorner.actor.show(); ++ }, ++ ++ _getMonitor: function(){ ++ ++ let monitorIndex = this._settings.get_int('preferred-monitor'); ++ let monitor; ++ ++ if (monitorIndex >0 && monitorIndex< Main.layoutManager.monitors.length) ++ monitor = Main.layoutManager.monitors[monitorIndex]; ++ else ++ monitor = Main.layoutManager.primaryMonitor; ++ ++ return monitor; ++ }, ++ ++ _removeAnimations: function() { ++ Tweener.removeTweens(this._slider); ++ }, ++ ++ _onDragStart: function(){ ++ // The dash need to be above the top_window_group, otherwise it doesn't ++ // accept dnd of app icons when not in overiew mode. ++ Main.layoutManager.uiGroup.set_child_above_sibling(this.actor, global.top_window_group); ++ this._oldignoreHover = this._ignoreHover; ++ this._ignoreHover = true; ++ this._animateIn(this._settings.get_double('animation-time'), 0); ++ }, ++ ++ _onDragEnd: function(){ ++ // Restore drag default dash stack order ++ Main.layoutManager.uiGroup.set_child_below_sibling(this.actor, Main.layoutManager.modalDialogGroup); ++ if (this._oldignoreHover !== null) ++ this._ignoreHover = this._oldignoreHover; ++ this._oldignoreHover = null; ++ this._box.sync_hover(); ++ if(Main.overview._shown) ++ this._pageChanged(); ++ }, ++ ++ _pageChanged: function() { ++ ++ let activePage = Main.overview.viewSelector.getActivePage(); ++ let dashVisible = (activePage == ViewSelector.ViewPage.WINDOWS || ++ activePage == ViewSelector.ViewPage.APPS); ++ ++ if(dashVisible){ ++ this._animateIn(this._settings.get_double('animation-time'), 0); ++ } else { ++ this._animateOut(this._settings.get_double('animation-time'), 0); ++ } ++ }, ++ ++ _onPageEmpty: function() { ++ /* The dash spacer is required only in the WINDOWS view if in the default position. ++ * The 'page-empty' signal is emitted in between a change of view, ++ * signalling the spacer can be added and removed without visible effect, ++ * as it's done for the upstream dashSpacer. ++ * ++ * Moreover, hiding the spacer ensure the appGrid allocaton is triggered. ++ * This matter as the appview spring animation is triggered by to first reallocaton of the appGrid, ++ * (See appDisplay.js, line 202 on GNOME Shell 3.14: ++ * this._grid.actor.connect('notify::allocation', ...) ++ * which in turn seems to be triggered by changes in the other actors in the overview. ++ * Normally, as far as I could understand, either the dashSpacer being hidden or the workspacesThumbnails ++ * sliding out would trigger the allocation. However, with no stock dash ++ * and no thumbnails, which happen if the user configured only 1 and static workspace, ++ * the animation out of icons is not played. ++ */ ++ ++ let activePage = Main.overview.viewSelector.getActivePage(); ++ this._dashSpacer.visible = (this._isHorizontal || activePage == ViewSelector.ViewPage.WINDOWS); ++ }, ++ ++ // Show dock and give key focus to it ++ _onAccessibilityFocus: function(){ ++ this._box.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); ++ this._animateIn(this._settings.get_double('animation-time'), 0); ++ }, ++ ++ _onShowAppsButtonToggled: function() { ++ ++ // Sync the status of the default appButtons. Only if the two statuses are ++ // different, that means the user interacted with the extension provided ++ // application button, cutomize the behaviour. Otherwise the shell has changed the ++ // status (due to the _syncShowAppsButtonToggled function below) and it ++ // has already performed the desired action. ++ ++ let selector = Main.overview.viewSelector; ++ ++ if(selector._showAppsButton.checked !== this.dash.showAppsButton.checked){ ++ ++ // find visible view ++ let visibleView; ++ Main.overview.viewSelector.appDisplay._views.every(function(v, index) { ++ if (v.view.actor.visible) { ++ visibleView = index; ++ return false; ++ } else { ++ return true; ++ } ++ }); ++ ++ if(this.dash.showAppsButton.checked){ ++ // force entering overview if needed ++ if (!Main.overview._shown) { ++ ++ let view = Main.overview.viewSelector.appDisplay._views[visibleView].view; ++ let grid = view._grid; ++ ++ // Animate in the the appview, hide the appGrid to avoiud flashing ++ // Go to the appView before entering the overview, skipping the workspaces. ++ // Do this manually avoiding opacity in transitions so that the setting of the opacity ++ // to 0 doesn't get overwritten. ++ Main.overview.viewSelector._activePage.opacity = 0; ++ Main.overview.viewSelector._activePage.hide(); ++ Main.overview.viewSelector._activePage = Main.overview.viewSelector._appsPage; ++ Main.overview.viewSelector._activePage.show(); ++ grid.actor.opacity = 0; ++ selector._showAppsButton.checked = true; ++ ++ // The animation has to be trigered manually because the AppDisplay.animate ++ // method is waiting for an allocation not happening, as we skip the workspace view ++ // and the appgrid could already be allocated from previous shown. ++ // It has to be triggered after the overview is shown as wrong coordinates are obtained ++ // otherwise. ++ let overviewShownId = Main.overview.connect('shown', Lang.bind(this, function(){ ++ Main.overview.disconnect(overviewShownId); ++ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { ++ grid.actor.opacity = 255; ++ grid.animateSpring(IconGrid.AnimationDirection.IN, this.dash.showAppsButton); ++ })); ++ })); ++ ++ // Finally show the overview ++ Main.overview.show(); ++ this.forcedOverview = true; ++ } else { ++ selector._showAppsButton.checked = true; ++ } ++ } else { ++ if (this.forcedOverview) { ++ // force exiting overview if needed ++ ++ // Manually trigger springout animation without activating the ++ // workspaceView to avoid the zoomout animation. Hide the appPage ++ // onComplete to avoid ugly flashing of original icons. ++ let view = Main.overview.viewSelector.appDisplay._views[visibleView].view; ++ let grid = view._grid; ++ view.animate(IconGrid.AnimationDirection.OUT, Lang.bind(this, function(){ ++ Main.overview.viewSelector._appsPage.hide(); ++ Main.overview.hide(); ++ selector._showAppsButton.checked = false; ++ this.forcedOverview = false; ++ })); ++ ++ } else { ++ selector._showAppsButton.checked = false; ++ } ++ ++ } ++ } ++ ++ // whenever the button is unactivated even if not by the user still reset the ++ // forcedOverview flag ++ if( this.dash.showAppsButton.checked==false) ++ this.forcedOverview = false; ++ }, ++ ++ // Keep ShowAppsButton status in sync with the overview status ++ _syncShowAppsButtonToggled: function() { ++ let status = Main.overview.viewSelector._showAppsButton.checked; ++ if(this.dash.showAppsButton.checked !== status) ++ this.dash.showAppsButton.checked = status; ++ }, ++ ++ // Optional features enable/disable ++ ++ // Switch workspace by scrolling over the dock ++ _optionalScrollWorkspaceSwitch: function() { ++ ++ let label = 'optionalScrollWorkspaceSwitch'; ++ ++ this._settings.connect('changed::scroll-switch-workspace',Lang.bind(this, function(){ ++ if(this._settings.get_boolean('scroll-switch-workspace')) ++ Lang.bind(this, enable)(); ++ else ++ Lang.bind(this, disable)(); ++ })); ++ ++ if(this._settings.get_boolean('scroll-switch-workspace')) ++ Lang.bind(this, enable)(); ++ ++ function enable(){ ++ ++ this._signalsHandler.removeWithLabel(label); ++ ++ this._signalsHandler.addWithLabel(label, ++ [ ++ this._box, ++ 'scroll-event', ++ Lang.bind(this, onScrollEvent) ++ ] ++ ); ++ ++ this._optionalScrollWorkspaceSwitchDeadTimeId=0; ++ } ++ ++ function disable() { ++ this._signalsHandler.removeWithLabel(label); ++ ++ if(this._optionalScrollWorkspaceSwitchDeadTimeId>0){ ++ Mainloop.source_remove(this._optionalScrollWorkspaceSwitchDeadTimeId); ++ this._optionalScrollWorkspaceSwitchDeadTimeId=0; ++ } ++ } ++ ++ // This was inspired to desktop-scroller@obsidien.github.com ++ function onScrollEvent(actor, event) { ++ ++ // When in overview change workscape only in windows view ++ if (Main.overview.visible && Main.overview.viewSelector.getActivePage() !== ViewSelector.ViewPage.WINDOWS) ++ return false; ++ ++ let activeWs = global.screen.get_active_workspace(); ++ let direction = null; ++ ++ switch ( event.get_scroll_direction() ) { ++ case Clutter.ScrollDirection.UP: ++ direction = Meta.MotionDirection.UP; ++ break; ++ case Clutter.ScrollDirection.DOWN: ++ direction = Meta.MotionDirection.DOWN; ++ break; ++ case Clutter.ScrollDirection.SMOOTH: ++ let [dx, dy] = event.get_scroll_delta(); ++ if(dy < 0){ ++ direction = Meta.MotionDirection.UP; ++ } else if(dy > 0) { ++ direction = Meta.MotionDirection.DOWN; ++ } ++ break; ++ } ++ ++ if(direction !==null ){ ++ ++ // Prevent scroll events from triggering too many workspace switches ++ // by adding a 250ms deadtime between each scroll event. ++ // Usefull on laptops when using a touchpad. ++ ++ // During the deadtime do nothing ++ if(this._optionalScrollWorkspaceSwitchDeadTimeId>0) ++ return false; ++ else { ++ this._optionalScrollWorkspaceSwitchDeadTimeId = ++ Mainloop.timeout_add(250, ++ Lang.bind(this, function() { ++ this._optionalScrollWorkspaceSwitchDeadTimeId=0; ++ } ++ )); ++ } ++ ++ ++ let ws; ++ ++ ws = activeWs.get_neighbor(direction) ++ ++ if (Main.wm._workspaceSwitcherPopup == null) ++ Main.wm._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup(); ++ // Set the actor non reactive, so that it doesn't prevent the ++ // clicks events from reaching the dash actor. I can't see a reason ++ // why it should be reactive. ++ Main.wm._workspaceSwitcherPopup.actor.reactive = false; ++ Main.wm._workspaceSwitcherPopup.connect('destroy', function() { ++ Main.wm._workspaceSwitcherPopup = null; ++ }); ++ ++ // Do not show wokspaceSwithcer in overview ++ if(!Main.overview.visible) ++ Main.wm._workspaceSwitcherPopup.display(direction, ws.index()); ++ Main.wm.actionMoveWorkspace(ws); ++ ++ return true; ++ ++ } else { ++ return false; ++ } ++ } ++ ++ }, ++ ++ // Makes the message not being triggered by mouse. SOURCE: insensitive-tray extension. ++ _updateInsensitiveTray: function() { ++ ++ let insensitive = this._settings.get_boolean('insensitive-message-tray'); ++ ++ if("_trayPressure" in LayoutManager) { ++ // Systems supporting pressure ++ if (insensitive) { ++ LayoutManager._trayPressure._keybindingMode = null; ++ } else { ++ LayoutManager._trayPressure._keybindingMode = Shell.KeyBindingMode.NORMAL ++ | Shell.KeyBindingMode.OVERVIEW; ++ } ++ } else { ++ //systems using the old dwell mechanism ++ if (insensitive) { ++ this._injectionsHandler.addWithLabel('insensitive-message-tray', ++ [ ++ Main.messageTray, ++ '_trayDwellTimeout', ++ function() { return false; } ++ ] ++ ); ++ } else { ++ this._injectionsHandler.removeWithLabel('insensitive-message-tray') ++ } ++ } ++ } ++}); ++Signals.addSignalMethods(dockedDash.prototype); ++ ++/* ++ * Manage theme customization and custom theme support ++*/ ++const themeManager = new Lang.Class({ ++ Name: 'ThemeManager', ++ ++ _init: function(settings, actor, dash) { ++ ++ this._settings = settings; ++ this._bindSettingsChanges(); ++ this._actor = actor; ++ this._dash = dash; ++ ++ // initialize colors with generic values ++ this._defaultBackground = {red: 0, green:0, blue: 0, alpha:0}; ++ this._defaultBackgroundColor = {red: 0, green:0, blue: 0, alpha:0}; ++ this._customizedBackground = {red: 0, green:0, blue: 0, alpha:0}; ++ ++ this._signalsHandler = new Convenience.GlobalSignalsHandler(); ++ this._signalsHandler.add( ++ // When theme changes re-obtain default background color ++ [ ++ St.ThemeContext.get_for_stage (global.stage), ++ 'changed', ++ Lang.bind(this, this.updateCustomTheme) ++ ], ++ // update :overview pseudoclass ++ [ ++ Main.overview, ++ 'showing', ++ Lang.bind(this, this._onOverviewShowing) ++ ], ++ [ ++ Main.overview, ++ 'hiding', ++ Lang.bind(this, this._onOverviewHiding) ++ ] ++ ); ++ ++ this._updateCustomStyleClasses(); ++ ++ }, ++ ++ destroy: function() { ++ this._signalsHandler.destroy(); ++ }, ++ ++ _onOverviewShowing: function() { ++ this._actor.add_style_pseudo_class('overview'); ++ }, ++ ++ _onOverviewHiding: function() { ++ this._actor.remove_style_pseudo_class('overview'); ++ }, ++ ++ _updateBackgroundOpacity: function() { ++ ++ let newAlpha = this._settings.get_double('background-opacity'); ++ ++ this._defaultBackground = 'rgba('+ ++ this._defaultBackgroundColor.red + ','+ ++ this._defaultBackgroundColor.green + ','+ ++ this._defaultBackgroundColor.blue + ','+ ++ Math.round(this._defaultBackgroundColor.alpha/2.55)/100 + ')'; ++ ++ this._customizedBackground = 'rgba('+ ++ this._defaultBackgroundColor.red + ','+ ++ this._defaultBackgroundColor.green + ','+ ++ this._defaultBackgroundColor.blue + ','+ ++ newAlpha + ')'; ++ }, ++ ++ _getBackgroundColor: function() { ++ ++ // Prevent shell crash if the actor is not on the stage. ++ // It happens enabling/disabling repeatedly the extension ++ if(!this._dash._container.get_stage()) ++ return; ++ ++ // Remove custom style ++ let oldStyle = this._dash._container.get_style(); ++ this._dash._container.set_style(null); ++ ++ let themeNode = this._dash._container.get_theme_node(); ++ this._dash._container.set_style(oldStyle); ++ ++ this._defaultBackgroundColor = themeNode.get_background_color(); ++ }, ++ ++ _updateCustomStyleClasses: function(){ ++ ++ if (this._settings.get_boolean('apply-custom-theme')) ++ this._actor.add_style_class_name('dashtodock'); ++ else { ++ this._actor.remove_style_class_name('dashtodock'); ++ } ++ ++ if (this._settings.get_boolean('custom-theme-shrink')) ++ this._actor.add_style_class_name('shrink'); ++ else { ++ this._actor.remove_style_class_name('shrink'); ++ } ++ ++ if (this._settings.get_boolean('custom-theme-running-dots')) ++ this._actor.add_style_class_name('running-dots'); ++ else { ++ this._actor.remove_style_class_name('running-dots'); ++ } ++ ++ }, ++ ++ updateCustomTheme: function() { ++ this._updateCustomStyleClasses(); ++ this._getBackgroundColor(); ++ this._updateBackgroundOpacity(); ++ this._adjustTheme(); ++ this._dash._redisplay(); ++ }, ++ ++ /* Reimported back and adapted from atomdock */ ++ _adjustTheme: function() { ++ // Prevent shell crash if the actor is not on the stage. ++ // It happens enabling/disabling repeatedly the extension ++ if (!this._dash._container.get_stage()) { ++ return; ++ } ++ ++ // Remove prior style edits ++ this._dash._container.set_style(null); ++ ++ /* If built-in theme is enabled do nothing else */ ++ if( this._settings.get_boolean('apply-custom-theme') ) ++ return; ++ ++ let newStyle = ''; ++ let position = getPosition(this._settings); ++ ++ if ( ! this._settings.get_boolean('custom-theme-shrink') ) { ++ ++ // obtain theme border settings ++ let themeNode = this._dash._container.get_theme_node(); ++ let borderColor = themeNode.get_border_color(St.Side.TOP); ++ let borderWidth = themeNode.get_border_width(St.Side.TOP); ++ let borderRadius = themeNode.get_border_radius(St.Corner.TOPRIGHT); ++ ++ /* We're copying border and corner styles to left border and top-left ++ * corner, also removing bottom border and bottom-right corner styles ++ */ ++ let borderInner = ''; ++ let borderRadiusValue = ''; ++ let borderMissingStyle = ''; ++ ++ if (this._rtl && position != St.Side.RIGHT) { ++ borderMissingStyle = 'border-right: ' + borderWidth + 'px solid ' + ++ borderColor.to_string() + ';'; ++ } else if (!this._rtl && position != St.Side.LEFT){ ++ borderMissingStyle = 'border-left: ' + borderWidth + 'px solid ' + ++ borderColor.to_string() + ';'; ++ } ++ ++ switch(position) { ++ case St.Side.LEFT: ++ borderInner = 'border-left'; ++ borderRadiusValue = '0 ' + borderRadius + 'px ' + borderRadius + 'px 0;'; ++ break; ++ case St.Side.RIGHT: ++ borderInner = 'border-right'; ++ borderRadiusValue = borderRadius + 'px 0 0 ' + borderRadius + 'px;'; ++ break; ++ case St.Side.TOP: ++ borderInner = 'border-top'; ++ borderRadiusValue = '0 0 ' + borderRadius + 'px ' + borderRadius + 'px;'; ++ break; ++ case St.Side.BOTTOM: ++ borderInner = 'border-bottom'; ++ borderRadiusValue = borderRadius + 'px ' + borderRadius + 'px 0 0;'; ++ break; ++ } ++ ++ newStyle = borderInner + ': none;' + ++ 'border-radius: ' + borderRadiusValue + ++ borderMissingStyle ; ++ ++ /* I do call set_style possibly twice so that only the background gets the transition. ++ * The transition-property css rules seems to be unsupported ++ */ ++ this._dash._container.set_style(newStyle); ++ } ++ ++ /* Customize background */ ++ if ( this._settings.get_boolean('opaque-background') ) { ++ newStyle = newStyle + 'background-color:'+ this._customizedBackground + '; ' + ++ 'transition-delay: 0s; transition-duration: 0.250s;'; ++ this._dash._container.set_style(newStyle); ++ } ++ }, ++ ++ _bindSettingsChanges: function() { ++ ++ let keys = ['opaque-background', ++ 'background-opacity', ++ 'apply-custom-theme', ++ 'custom-theme-shrink', ++ 'custom-theme-running-dots', ++ 'extend-height']; ++ ++ keys.forEach(function(key){ ++ this._settings.connect('changed::'+key, ++ Lang.bind(this, this.updateCustomTheme) ++ ); ++ }, this ); ++ ++ } ++}); ++ ++ ++/* ++ * Manually check if mouse "can be" hover from the mouse position. The hover porperty ++ * is not reliable when focus move from the clutter actors to the the windows, giving a false ++ * positive hover status. If the mouse pointer is not in the right position I can be sure ++ * that the hover has to be false. ++*/ ++function isMouseHover(actor) { ++ let [pointerX, pointerY, mods] = global.get_pointer(); ++ ++ let [x, y] = actor.get_transformed_position(); ++ let [width, height] =actor.get_transformed_size(); ++ ++ let test = (pointerX < x + width) && ++ (pointerX > x) && ++ (pointerY < y + height) && ++ (pointerY > y); ++ ++ return test; ++} +diff --git a/extensions/dash-to-dock/docking.js b/extensions/dash-to-dock/docking.js +new file mode 100644 +index 0000000..08707b4 +--- /dev/null ++++ b/extensions/dash-to-dock/docking.js +@@ -0,0 +1,1909 @@ ++// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- ++ ++const Clutter = imports.gi.Clutter; ++const GLib = imports.gi.GLib; ++const Gtk = imports.gi.Gtk; ++const Lang = imports.lang; ++const Meta = imports.gi.Meta; ++const Shell = imports.gi.Shell; ++const St = imports.gi.St; ++const Mainloop = imports.mainloop; ++const Params = imports.misc.params; ++ ++const Main = imports.ui.main; ++const Dash = imports.ui.dash; ++const IconGrid = imports.ui.iconGrid; ++const Overview = imports.ui.overview; ++const OverviewControls = imports.ui.overviewControls; ++const PointerWatcher = imports.ui.pointerWatcher; ++const Tweener = imports.ui.tweener; ++const Signals = imports.signals; ++const ViewSelector = imports.ui.viewSelector; ++const WorkspaceSwitcherPopup= imports.ui.workspaceSwitcherPopup; ++const Layout = imports.ui.layout; ++const LayoutManager = imports.ui.main.layoutManager; ++ ++const Me = imports.misc.extensionUtils.getCurrentExtension(); ++const Convenience = Me.imports.convenience; ++const Utils = Me.imports.utils; ++const Intellihide = Me.imports.intellihide; ++const Theming = Me.imports.theming; ++const MyDash = Me.imports.dash; ++ ++const DOCK_DWELL_CHECK_INTERVAL = 100; ++ ++const State = { ++ HIDDEN: 0, ++ SHOWING: 1, ++ SHOWN: 2, ++ HIDING: 3 ++}; ++ ++const scrollAction = { ++ DO_NOTHING: 0, ++ CYCLE_WINDOWS: 1, ++ SWITCH_WORKSPACE: 2 ++}; ++ ++/** ++ * A simple St.Widget with one child whose allocation takes into account the ++ * slide out of its child via the _slidex parameter ([0:1]). ++ * ++ * Required since I want to track the input region of this container which is ++ * based on its allocation even if the child overlows the parent actor. By doing ++ * this the region of the dash that is slideout is not steling anymore the input ++ * regions making the extesion usable when the primary monitor is the right one. ++ * ++ * The slidex parameter can be used to directly animate the sliding. The parent ++ * must have a WEST (SOUTH) anchor_point to achieve the sliding to the RIGHT (BOTTOM) ++ * side. ++ * ++ * It can't be an extended object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973. ++ * thus use the Shell.GenericContainer pattern. ++*/ ++const DashSlideContainer = new Lang.Class({ ++ Name: 'DashToDock.DashSlideContainer', ++ ++ _init: function(params) { ++ // Default local params ++ let localDefaults = { ++ side: St.Side.LEFT, ++ initialSlideValue: 1 ++ } ++ ++ let localParams = Params.parse(params, localDefaults, true); ++ ++ if (params) { ++ // Remove local params before passing the params to the parent ++ // constructor to avoid errors. ++ let prop; ++ for (prop in localDefaults) { ++ if ((prop in params)) ++ delete params[prop]; ++ } ++ } ++ ++ this.actor = new Shell.GenericContainer(params); ++ this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); ++ this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); ++ this.actor.connect('allocate', Lang.bind(this, this._allocate)); ++ ++ this.actor._delegate = this; ++ ++ this._child = null; ++ ++ // slide parameter: 1 = visible, 0 = hidden. ++ this._slidex = localParams.initialSlideValue; ++ this._side = localParams.side; ++ this._slideoutSize = 0; // minimum size when slided out ++ }, ++ ++ _allocate: function(actor, box, flags) { ++ if (this._child == null) ++ return; ++ ++ let availWidth = box.x2 - box.x1; ++ let availHeight = box.y2 - box.y1; ++ let [minChildWidth, minChildHeight, natChildWidth, natChildHeight] = ++ this._child.get_preferred_size(); ++ ++ let childWidth = natChildWidth; ++ let childHeight = natChildHeight; ++ ++ let childBox = new Clutter.ActorBox(); ++ ++ let slideoutSize = this._slideoutSize; ++ ++ if (this._side == St.Side.LEFT) { ++ childBox.x1 = (this._slidex -1) * (childWidth - slideoutSize); ++ childBox.x2 = slideoutSize + this._slidex*(childWidth - slideoutSize); ++ childBox.y1 = 0; ++ childBox.y2 = childBox.y1 + childHeight; ++ } ++ else if ((this._side == St.Side.RIGHT) || (this._side == St.Side.BOTTOM)) { ++ childBox.x1 = 0; ++ childBox.x2 = childWidth; ++ childBox.y1 = 0; ++ childBox.y2 = childBox.y1 + childHeight; ++ } ++ else if (this._side == St.Side.TOP) { ++ childBox.x1 = 0; ++ childBox.x2 = childWidth; ++ childBox.y1 = (this._slidex -1) * (childHeight - slideoutSize); ++ childBox.y2 = slideoutSize + this._slidex * (childHeight - slideoutSize); ++ } ++ ++ this._child.allocate(childBox, flags); ++ this._child.set_clip(-childBox.x1, -childBox.y1, ++ -childBox.x1+availWidth, -childBox.y1 + availHeight); ++ }, ++ ++ /** ++ * Just the child width but taking into account the slided out part ++ */ ++ _getPreferredWidth: function(actor, forHeight, alloc) { ++ let [minWidth, natWidth] = this._child.get_preferred_width(forHeight); ++ if ((this._side == St.Side.LEFT) || (this._side == St.Side.RIGHT)) { ++ minWidth = (minWidth - this._slideoutSize) * this._slidex + this._slideoutSize; ++ natWidth = (natWidth - this._slideoutSize) * this._slidex + this._slideoutSize; ++ } ++ ++ alloc.min_size = minWidth; ++ alloc.natural_size = natWidth; ++ }, ++ ++ /** ++ * Just the child height but taking into account the slided out part ++ */ ++ _getPreferredHeight: function(actor, forWidth, alloc) { ++ let [minHeight, natHeight] = this._child.get_preferred_height(forWidth); ++ if ((this._side == St.Side.TOP) || (this._side == St.Side.BOTTOM)) { ++ minHeight = (minHeight - this._slideoutSize) * this._slidex + this._slideoutSize; ++ natHeight = (natHeight - this._slideoutSize) * this._slidex + this._slideoutSize; ++ } ++ alloc.min_size = minHeight; ++ alloc.natural_size = natHeight; ++ }, ++ ++ /** ++ * I was expecting it to be a virtual function... stil I don't understand ++ * how things work. ++ */ ++ add_child: function(actor) { ++ // I'm supposed to have only on child ++ if (this._child !== null) ++ this.actor.remove_child(actor); ++ ++ this._child = actor; ++ this.actor.add_child(actor); ++ }, ++ ++ set slidex(value) { ++ this._slidex = value; ++ this._child.queue_relayout(); ++ }, ++ ++ get slidex() { ++ return this._slidex; ++ } ++}); ++ ++const DockedDash = new Lang.Class({ ++ Name: 'DashToDock.DockedDash', ++ ++ _init: function(settings, monitorIndex) { ++ this._rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL); ++ ++ // Load settings ++ this._settings = settings; ++ this._monitorIndex = monitorIndex; ++ // Connect global signals ++ this._signalsHandler = new Utils.GlobalSignalsHandler(); ++ ++ this._bindSettingsChanges(); ++ ++ this._position = Utils.getPosition(settings); ++ this._isHorizontal = ((this._position == St.Side.TOP) || (this._position == St.Side.BOTTOM)); ++ ++ // Temporary ignore hover events linked to autohide for whatever reason ++ this._ignoreHover = false; ++ this._oldignoreHover = null; ++ // This variables are linked to the settings regardles of autohide or intellihide ++ // being temporary disable. Get set by _updateVisibilityMode; ++ this._autohideIsEnabled = null; ++ this._intellihideIsEnabled = null; ++ this._fixedIsEnabled = null; ++ ++ // Create intellihide object to monitor windows overlapping ++ this._intellihide = new Intellihide.Intellihide(this._settings, this._monitorIndex); ++ ++ // initialize dock state ++ this._dockState = State.HIDDEN; ++ ++ // Put dock on the required monitor ++ this._monitor = Main.layoutManager.monitors[this._monitorIndex]; ++ ++ // this store size and the position where the dash is shown; ++ // used by intellihide module to check window overlap. ++ this.staticBox = new Clutter.ActorBox(); ++ ++ // Initialize pressure barrier variables ++ this._canUsePressure = false; ++ this._pressureBarrier = null; ++ this._barrier = null; ++ this._removeBarrierTimeoutId = 0; ++ ++ // Initialize dwelling system variables ++ this._dockDwelling = false; ++ this._dockWatch = null; ++ this._dockDwellUserTime = 0; ++ this._dockDwellTimeoutId = 0 ++ ++ // Create a new dash object ++ this.dash = new MyDash.MyDash(this._settings, this._monitorIndex); ++ ++ if (!this._settings.get_boolean('show-show-apps-button')) ++ this.dash.hideShowAppsButton(); ++ ++ // Create the main actor and the containers for sliding in and out and ++ // centering, turn on track hover ++ ++ let positionStyleClass = ['top', 'right', 'bottom', 'left']; ++ // This is the centering actor ++ this.actor = new St.Bin({ ++ name: 'dashtodockContainer', ++ reactive: false, ++ style_class: positionStyleClass[this._position], ++ x_align: this._isHorizontal?St.Align.MIDDLE:St.Align.START, ++ y_align: this._isHorizontal?St.Align.START:St.Align.MIDDLE ++ }); ++ this.actor._delegate = this; ++ ++ // This is the sliding actor whose allocation is to be tracked for input regions ++ this._slider = new DashSlideContainer({ ++ side: this._position, ++ initialSlideValue: 0 ++ }); ++ ++ // This is the actor whose hover status us tracked for autohide ++ this._box = new St.BoxLayout({ ++ name: 'dashtodockBox', ++ reactive: true, ++ track_hover: true ++ }); ++ this._box.connect('notify::hover', Lang.bind(this, this._hoverChanged)); ++ ++ // Create and apply height constraint to the dash. It's controlled by this.actor height ++ this.constrainSize = new Clutter.BindConstraint({ ++ source: this.actor, ++ coordinate: this._isHorizontal?Clutter.BindCoordinate.WIDTH:Clutter.BindCoordinate.HEIGHT ++ }); ++ this.dash.actor.add_constraint(this.constrainSize); ++ ++ this._signalsHandler.add([ ++ Main.overview, ++ 'item-drag-begin', ++ Lang.bind(this, this._onDragStart) ++ ], [ ++ Main.overview, ++ 'item-drag-end', ++ Lang.bind(this, this._onDragEnd) ++ ], [ ++ Main.overview, ++ 'item-drag-cancelled', ++ Lang.bind(this, this._onDragEnd) ++ ], [ ++ // update when workarea changes, for instance if other extensions modify the struts ++ //(like moving th panel at the bottom) ++ global.screen, ++ 'workareas-changed', ++ Lang.bind(this, this._resetPosition) ++ ], [ ++ Main.overview, ++ 'showing', ++ Lang.bind(this, this._onOverviewShowing) ++ ], [ ++ Main.overview, ++ 'hiding', ++ Lang.bind(this, this._onOverviewHiding) ++ ], [ ++ // Hide on appview ++ Main.overview.viewSelector, ++ 'page-changed', ++ Lang.bind(this, this._pageChanged) ++ ], [ ++ Main.overview.viewSelector, ++ 'page-empty', ++ Lang.bind(this, this._onPageEmpty) ++ ], [ ++ // Ensure the ShowAppsButton status is kept in sync ++ Main.overview.viewSelector._showAppsButton, ++ 'notify::checked', ++ Lang.bind(this, this._syncShowAppsButtonToggled) ++ ], [ ++ global.screen, ++ 'in-fullscreen-changed', ++ Lang.bind(this, this._updateBarrier) ++ ], [ ++ // Monitor windows overlapping ++ this._intellihide, ++ 'status-changed', ++ Lang.bind(this, this._updateDashVisibility) ++ ], [ ++ // Keep dragged icon consistent in size with this dash ++ this.dash, ++ 'icon-size-changed', ++ Lang.bind(this, function() { ++ Main.overview.dashIconSize = this.dash.iconSize; ++ }) ++ ], [ ++ // This duplicate the similar signal which is in owerview.js. ++ // Being connected and thus executed later this effectively ++ // overwrite any attempt to use the size of the default dash ++ //which given the customization is usually much smaller. ++ // I can't easily disconnect the original signal ++ Main.overview._controls.dash, ++ 'icon-size-changed', ++ Lang.bind(this, function() { ++ Main.overview.dashIconSize = this.dash.iconSize; ++ }) ++ ]); ++ ++ this._injectionsHandler = new Utils.InjectionsHandler(); ++ this._themeManager = new Theming.ThemeManager(this._settings, this.actor, this.dash); ++ ++ // Since the actor is not a topLevel child and its parent is now not added to the Chrome, ++ // the allocation change of the parent container (slide in and slideout) doesn't trigger ++ // anymore an update of the input regions. Force the update manually. ++ this.actor.connect('notify::allocation', ++ Lang.bind(Main.layoutManager, Main.layoutManager._queueUpdateRegions)); ++ ++ this.dash._container.connect('allocation-changed', Lang.bind(this, this._updateStaticBox)); ++ this._slider.actor.connect(this._isHorizontal ? 'notify::x' : 'notify::y', Lang.bind(this, this._updateStaticBox)); ++ ++ // sync hover after a popupmenu is closed ++ this.dash.connect('menu-closed', Lang.bind(this, function() { ++ this._box.sync_hover(); ++ })); ++ ++ // Load optional features that need to be activated for one dock only ++ if (this._monitorIndex == this._settings.get_int('preferred-monitor')) ++ this._enableExtraFeatures(); ++ // Load optional features that need to be activated once per dock ++ this._optionalScrollWorkspaceSwitch(); ++ ++ // Delay operations that require the shell to be fully loaded and with ++ // user theme applied. ++ ++ this._paintId = this.actor.connect('paint', Lang.bind(this, this._initialize)); ++ ++ // Manage the which is used to reserve space in the overview for the dock ++ // Add and additional dashSpacer positioned according to the dash positioning. ++ // It gets restored on extension unload. ++ this._dashSpacer = new OverviewControls.DashSpacer(); ++ this._dashSpacer.setDashActor(this._box); ++ ++ if (this._position == St.Side.LEFT) ++ Main.overview._controls._group.insert_child_at_index(this._dashSpacer, this._rtl ? -1 : 0); // insert on first ++ else if (this._position == St.Side.RIGHT) ++ Main.overview._controls._group.insert_child_at_index(this._dashSpacer, this._rtl ? 0 : -1); // insert on last ++ else if (this._position == St.Side.TOP) ++ Main.overview._overview.insert_child_at_index(this._dashSpacer, 0); ++ else if (this._position == St.Side.BOTTOM) ++ Main.overview._overview.insert_child_at_index(this._dashSpacer, -1); ++ ++ // Add dash container actor and the container to the Chrome. ++ this.actor.set_child(this._slider.actor); ++ this._slider.add_child(this._box); ++ this._box.add_actor(this.dash.actor); ++ ++ // Add aligning container without tracking it for input region ++ Main.uiGroup.add_child(this.actor); ++ ++ if (this._settings.get_boolean('dock-fixed')) { ++ // Note: tracking the fullscreen directly on the slider actor causes some hiccups when fullscreening ++ // windows of certain applications ++ Main.layoutManager._trackActor(this.actor, {affectsInputRegion: false, trackFullscreen: true}); ++ Main.layoutManager._trackActor(this._slider.actor, {affectsStruts: true}); ++ } ++ else ++ Main.layoutManager._trackActor(this._slider.actor); ++ ++ // Set initial position ++ this._resetDepth(); ++ this._resetPosition(); ++ }, ++ ++ _initialize: function() { ++ if (this._paintId > 0) { ++ this.actor.disconnect(this._paintId); ++ this._paintId=0; ++ } ++ ++ this.dash.setIconSize(this._settings.get_int('dash-max-icon-size'), true); ++ ++ // Apply custome css class according to the settings ++ this._themeManager.updateCustomTheme(); ++ ++ // Since Gnome 3.8 dragging an app without having opened the overview before cause the attemp to ++ //animate a null target since some variables are not initialized when the viewSelector is created ++ if (Main.overview.viewSelector._activePage == null) ++ Main.overview.viewSelector._activePage = Main.overview.viewSelector._workspacesPage; ++ ++ this._updateVisibilityMode(); ++ ++ // In case we are already inside the overview when the extension is loaded, ++ // for instance on unlocking the screen if it was locked with the overview open. ++ if (Main.overview.visibleTarget) { ++ this._onOverviewShowing(); ++ this._pageChanged(); ++ } ++ ++ // Setup pressure barrier (GS38+ only) ++ this._updatePressureBarrier(); ++ this._updateBarrier(); ++ ++ // setup dwelling system if pressure barriers are not available ++ this._setupDockDwellIfNeeded(); ++ }, ++ ++ destroy: function() { ++ // Disconnect global signals ++ this._signalsHandler.destroy(); ++ // The dash, intellihide and themeManager have global signals as well internally ++ this.dash.destroy(); ++ this._intellihide.destroy(); ++ this._themeManager.destroy(); ++ ++ this._injectionsHandler.destroy(); ++ ++ // Destroy main clutter actor: this should be sufficient removing it and ++ // destroying all its children ++ this.actor.destroy(); ++ ++ // Remove barrier timeout ++ if (this._removeBarrierTimeoutId > 0) ++ Mainloop.source_remove(this._removeBarrierTimeoutId); ++ ++ // Remove existing barrier ++ this._removeBarrier(); ++ ++ // Remove pointer watcher ++ if (this._dockWatch) { ++ PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch); ++ this._dockWatch = null; ++ } ++ ++ // Remove the dashSpacer ++ this._dashSpacer.destroy(); ++ ++ // Restore legacyTray position ++ this._resetLegacyTray(); ++ ++ }, ++ ++ _bindSettingsChanges: function() { ++ this._signalsHandler.add([ ++ this._settings, ++ 'changed::scroll-action', ++ Lang.bind(this, function() { ++ this._optionalScrollWorkspaceSwitch(); ++ }) ++ ], [ ++ this._settings, ++ 'changed::dash-max-icon-size', ++ Lang.bind(this, function() { ++ this.dash.setIconSize(this._settings.get_int('dash-max-icon-size')); ++ }) ++ ], [ ++ this._settings, ++ 'changed::icon-size-fixed', ++ Lang.bind(this, function() { ++ this.dash.setIconSize(this._settings.get_int('dash-max-icon-size')); ++ }) ++ ], [ ++ this._settings, ++ 'changed::show-favorites', ++ Lang.bind(this, function() { ++ this.dash.resetAppIcons(); ++ }) ++ ], [ ++ this._settings, ++ 'changed::show-running', ++ Lang.bind(this, function() { ++ this.dash.resetAppIcons(); ++ }) ++ ], [ ++ this._settings, ++ 'changed::show-apps-at-top', ++ Lang.bind(this, function() { ++ this.dash.resetAppIcons(); ++ }) ++ ], [ ++ this._settings, ++ 'changed::show-show-apps-button', ++ Lang.bind(this, function() { ++ if (this._settings.get_boolean('show-show-apps-button')) ++ this.dash.showShowAppsButton(); ++ else ++ this.dash.hideShowAppsButton(); ++ }) ++ ], [ ++ this._settings, ++ 'changed::dock-fixed', ++ Lang.bind(this, function() { ++ if (this._settings.get_boolean('dock-fixed')) { ++ Main.layoutManager._untrackActor(this.actor); ++ Main.layoutManager._trackActor(this.actor, {affectsInputRegion: false, trackFullscreen: true}); ++ Main.layoutManager._untrackActor(this._slider.actor); ++ Main.layoutManager._trackActor(this._slider.actor, {affectsStruts: true}); ++ } else { ++ Main.layoutManager._untrackActor(this.actor); ++ Main.layoutManager._untrackActor(this._slider.actor); ++ Main.layoutManager._trackActor(this._slider.actor); ++ } + -diff --git a/extensions/dash-to-dock/media/one.svg b/extensions/dash-to-dock/media/one.svg -new file mode 100644 -index 0000000..0582b88 ---- /dev/null -+++ b/extensions/dash-to-dock/media/one.svg -@@ -0,0 +1,107 @@ -+ -+ ++ this._resetPosition(); + -+ -+ -+ -+ -+ image/svg+xml -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/extensions/dash-to-dock/media/one_bottom.svg b/extensions/dash-to-dock/media/one_bottom.svg -new file mode 100644 -index 0000000..ebbaf33 ---- /dev/null -+++ b/extensions/dash-to-dock/media/one_bottom.svg -@@ -0,0 +1,108 @@ -+ -+ ++ // Add or remove barrier depending on if dock-fixed ++ this._updateBarrier(); + -+ -+ -+ -+ -+ image/svg+xml -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/extensions/dash-to-dock/media/one_rtl.svg b/extensions/dash-to-dock/media/one_rtl.svg -new file mode 100644 -index 0000000..8f4813e ---- /dev/null -+++ b/extensions/dash-to-dock/media/one_rtl.svg -@@ -0,0 +1,108 @@ -+ -+ ++ this._updateVisibilityMode(); ++ }) ++ ], [ ++ this._settings, ++ 'changed::intellihide', ++ Lang.bind(this, this._updateVisibilityMode) ++ ], [ ++ this._settings, ++ 'changed::intellihide-mode', ++ Lang.bind(this, function() { ++ this._intellihide.forceUpdate(); ++ }) ++ ], [ ++ this._settings, ++ 'changed::autohide', ++ Lang.bind(this, function() { ++ this._updateVisibilityMode(); ++ this._updateBarrier(); ++ }) ++ ], [ ++ this._settings, ++ 'changed::autohide-in-fullscreen', ++ Lang.bind(this, this._updateBarrier) ++ ], ++ [ ++ this._settings, ++ 'changed::extend-height', ++ Lang.bind(this, this._resetPosition) ++ ], [ ++ this._settings, ++ 'changed::height-fraction', ++ Lang.bind(this, this._resetPosition) ++ ], [ ++ this._settings, ++ 'changed::require-pressure-to-show', ++ Lang.bind(this, function() { ++ // Remove pointer watcher ++ if (this._dockWatch) { ++ PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch); ++ this._dockWatch = null; ++ } ++ this._setupDockDwellIfNeeded(); ++ this._updateBarrier(); ++ }) ++ ], [ ++ this._settings, ++ 'changed::pressure-threshold', ++ Lang.bind(this, function() { ++ this._updatePressureBarrier(); ++ this._updateBarrier(); ++ }) ++ ]); + -+ -+ -+ -+ -+ image/svg+xml -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/extensions/dash-to-dock/media/one_top.svg b/extensions/dash-to-dock/media/one_top.svg -new file mode 100644 -index 0000000..d771e3a ---- /dev/null -+++ b/extensions/dash-to-dock/media/one_top.svg -@@ -0,0 +1,108 @@ -+ -+ ++ }, + -+ -+ -+ -+ -+ image/svg+xml -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/extensions/dash-to-dock/media/three.svg b/extensions/dash-to-dock/media/three.svg -new file mode 100644 -index 0000000..5d908d6 ---- /dev/null -+++ b/extensions/dash-to-dock/media/three.svg -@@ -0,0 +1,121 @@ -+ -+ ++ /** ++ * This is call when visibility settings change ++ */ ++ _updateVisibilityMode: function() { ++ if (this._settings.get_boolean('dock-fixed')) { ++ this._fixedIsEnabled = true; ++ this._autohideIsEnabled = false; ++ this._intellihideIsEnabled = false; ++ } ++ else { ++ this._fixedIsEnabled = false; ++ this._autohideIsEnabled = this._settings.get_boolean('autohide') ++ this._intellihideIsEnabled = this._settings.get_boolean('intellihide') ++ } ++ ++ if (this._intellihideIsEnabled) ++ this._intellihide.enable(); ++ else ++ this._intellihide.disable(); ++ ++ this._updateDashVisibility(); ++ }, ++ ++ /** ++ * Show/hide dash based on, in order of priority: ++ * overview visibility ++ * fixed mode ++ * intellihide ++ * autohide ++ * overview visibility ++ */ ++ _updateDashVisibility: function() { ++ if (Main.overview.visibleTarget) ++ return; ++ ++ if (this._fixedIsEnabled) { ++ this._removeAnimations(); ++ this._animateIn(this._settings.get_double('animation-time'), 0); ++ } ++ else if (this._intellihideIsEnabled) { ++ if (this._intellihide.getOverlapStatus()) { ++ this._ignoreHover = false; ++ // Do not hide if autohide is enabled and mouse is hover ++ if (!this._box.hover || !this._autohideIsEnabled) ++ this._animateOut(this._settings.get_double('animation-time'), 0); ++ } ++ else { ++ this._ignoreHover = true; ++ this._removeAnimations(); ++ this._animateIn(this._settings.get_double('animation-time'), 0); ++ } ++ } ++ else { ++ if (this._autohideIsEnabled) { ++ this._ignoreHover = false; ++ global.sync_pointer(); ++ ++ if (this._box.hover) ++ this._animateIn(this._settings.get_double('animation-time'), 0); ++ else ++ this._animateOut(this._settings.get_double('animation-time'), 0); ++ } ++ else ++ this._animateOut(this._settings.get_double('animation-time'), 0); ++ } ++ }, + -+ -+ -+ -+ -+ image/svg+xml -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/extensions/dash-to-dock/media/three_bottom.svg b/extensions/dash-to-dock/media/three_bottom.svg -new file mode 100644 -index 0000000..30d0ede ---- /dev/null -+++ b/extensions/dash-to-dock/media/three_bottom.svg -@@ -0,0 +1,122 @@ -+ -+ ++ _onOverviewShowing: function() { ++ this._ignoreHover = true; ++ this._intellihide.disable(); ++ this._removeAnimations(); ++ this._animateIn(this._settings.get_double('animation-time'), 0); ++ }, + -+ -+ -+ -+ -+ image/svg+xml -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/extensions/dash-to-dock/media/three_rtl.svg b/extensions/dash-to-dock/media/three_rtl.svg -new file mode 100644 -index 0000000..f4c2722 ---- /dev/null -+++ b/extensions/dash-to-dock/media/three_rtl.svg -@@ -0,0 +1,122 @@ -+ -+ ++ _onOverviewHiding: function() { ++ this._ignoreHover = false; ++ this._intellihide.enable(); ++ this._updateDashVisibility(); ++ }, + -+ -+ -+ -+ -+ image/svg+xml -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/extensions/dash-to-dock/media/three_top.svg b/extensions/dash-to-dock/media/three_top.svg -new file mode 100644 -index 0000000..2fca42c ---- /dev/null -+++ b/extensions/dash-to-dock/media/three_top.svg -@@ -0,0 +1,122 @@ -+ -+ ++ _hoverChanged: function() { ++ if (!this._ignoreHover) { ++ // Skip if dock is not in autohide mode for instance because it is shown ++ // by intellihide. ++ if (this._autohideIsEnabled) { ++ if (this._box.hover) ++ this._show(); ++ else ++ this._hide(); ++ } ++ } ++ }, + -+ -+ -+ -+ -+ image/svg+xml -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/extensions/dash-to-dock/media/two.svg b/extensions/dash-to-dock/media/two.svg -new file mode 100644 -index 0000000..4565f34 ---- /dev/null -+++ b/extensions/dash-to-dock/media/two.svg -@@ -0,0 +1,118 @@ -+ -+ ++ _show: function() { ++ if ((this._dockState == State.HIDDEN) || (this._dockState == State.HIDING)) { ++ if (this._dockState == State.HIDING) ++ // suppress all potential queued hiding animations - i.e. added to Tweener but not started, ++ // always give priority to show ++ this._removeAnimations(); ++ ++ this.emit('showing'); ++ this._animateIn(this._settings.get_double('animation-time'), 0); ++ } ++ }, ++ ++ _hide: function() { ++ // If no hiding animation is running or queued ++ if ((this._dockState == State.SHOWN) || (this._dockState == State.SHOWING)) { ++ let delay; ++ ++ if (this._dockState == State.SHOWING) ++ //if a show already started, let it finish; queue hide without removing the show. ++ // to obtain this I increase the delay to avoid the overlap and interference ++ // between the animations ++ delay = this._settings.get_double('hide-delay') + this._settings.get_double('animation-time'); ++ else ++ delay = this._settings.get_double('hide-delay'); ++ ++ this.emit('hiding'); ++ this._animateOut(this._settings.get_double('animation-time'), delay); ++ } ++ }, ++ ++ _animateIn: function(time, delay) { ++ this._dockState = State.SHOWING; ++ ++ Tweener.addTween(this._slider, { ++ slidex: 1, ++ time: time, ++ delay: delay, ++ transition: 'easeOutQuad', ++ onComplete: Lang.bind(this, function() { ++ this._dockState = State.SHOWN; ++ // Remove barrier so that mouse pointer is released and can access monitors on other side of dock ++ // NOTE: Delay needed to keep mouse from moving past dock and re-hiding dock immediately. This ++ // gives users an opportunity to hover over the dock ++ if (this._removeBarrierTimeoutId > 0) ++ Mainloop.source_remove(this._removeBarrierTimeoutId); ++ this._removeBarrierTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, this._removeBarrier)); ++ }) ++ }); ++ }, ++ ++ _animateOut: function(time, delay) { ++ this._dockState = State.HIDING; ++ Tweener.addTween(this._slider, { ++ slidex: 0, ++ time: time, ++ delay: delay , ++ transition: 'easeOutQuad', ++ onComplete: Lang.bind(this, function() { ++ this._dockState = State.HIDDEN; ++ this._updateBarrier(); ++ }) ++ }); ++ }, + -+ -+ -+ -+ -+ image/svg+xml -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/extensions/dash-to-dock/media/two_bottom.svg b/extensions/dash-to-dock/media/two_bottom.svg -new file mode 100644 -index 0000000..83e6251 ---- /dev/null -+++ b/extensions/dash-to-dock/media/two_bottom.svg -@@ -0,0 +1,119 @@ -+ -+ ++ /** ++ * Dwelling system based on the GNOME Shell 3.14 messageTray code. ++ */ ++ _setupDockDwellIfNeeded: function() { ++ // If we don't have extended barrier features, then we need ++ // to support the old tray dwelling mechanism. ++ if (!global.display.supports_extended_barriers() || !this._settings.get_boolean('require-pressure-to-show')) { ++ let pointerWatcher = PointerWatcher.getPointerWatcher(); ++ this._dockWatch = pointerWatcher.addWatch(DOCK_DWELL_CHECK_INTERVAL, Lang.bind(this, this._checkDockDwell)); ++ this._dockDwelling = false; ++ this._dockDwellUserTime = 0; ++ } ++ }, + -+ -+ -+ -+ -+ image/svg+xml -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/extensions/dash-to-dock/media/two_rtl.svg b/extensions/dash-to-dock/media/two_rtl.svg -new file mode 100644 -index 0000000..e6f1159 ---- /dev/null -+++ b/extensions/dash-to-dock/media/two_rtl.svg -@@ -0,0 +1,119 @@ -+ -+ ++ _checkDockDwell: function(x, y) { + -+ -+ -+ -+ -+ image/svg+xml -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/extensions/dash-to-dock/media/two_top.svg b/extensions/dash-to-dock/media/two_top.svg -new file mode 100644 -index 0000000..4bf86a5 ---- /dev/null -+++ b/extensions/dash-to-dock/media/two_top.svg -@@ -0,0 +1,119 @@ -+ -+ ++ let workArea = Main.layoutManager.getWorkAreaForMonitor(this._monitor.index) ++ let shouldDwell; ++ // Check for the correct screen edge, extending the sensitive area to the whole workarea, ++ // minus 1 px to avoid conflicting with other active corners. ++ if (this._position == St.Side.LEFT) ++ shouldDwell = (x == this._monitor.x) && (y > workArea.y) && (y < workArea.y + workArea.height); ++ else if (this._position == St.Side.RIGHT) ++ shouldDwell = (x == this._monitor.x + this._monitor.width - 1) && (y > workArea.y) && (y < workArea.y + workArea.height); ++ else if (this._position == St.Side.TOP) ++ shouldDwell = (y == this._monitor.y) && (x > workArea.x) && (x < workArea.x + workArea.width); ++ else if (this._position == St.Side.BOTTOM) ++ shouldDwell = (y == this._monitor.y + this._monitor.height - 1) && (x > workArea.x) && (x < workArea.x + workArea.width); + -+ -+ -+ -+ -+ image/svg+xml -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/extensions/dash-to-dock/metadata.json.in b/extensions/dash-to-dock/metadata.json.in -new file mode 100644 -index 0000000..a090272 ---- /dev/null -+++ b/extensions/dash-to-dock/metadata.json.in -@@ -0,0 +1,12 @@ -+{ -+"extension-id": "@extension_id@", -+"uuid": "@uuid@", -+"settings-schema": "@gschemaname@", -+"gettext-domain": "@gettext_domain@", -+"original-author": "micxgx@gmail.com", -+"name": "Dash to Dock", -+"description": "A dock for the Gnome Shell. This extension moves the dash out of the overview transforming it in a dock for an easier launching of applications and a faster switching between windows and desktops. Side and bottom placement options are available.", -+"shell-version": [ "@shell_current@" ], -+"version": 45, -+"url": "https://micheleg.github.io/dash-to-dock/" -+} -diff --git a/extensions/dash-to-dock/myConvenience.js b/extensions/dash-to-dock/myConvenience.js -new file mode 100644 -index 0000000..39223a6 ---- /dev/null -+++ b/extensions/dash-to-dock/myConvenience.js -@@ -0,0 +1,103 @@ -+const Lang = imports.lang; ++ if (shouldDwell) { ++ // We only set up dwell timeout when the user is not hovering over the dock ++ // already (!this._box.hover). ++ // The _dockDwelling variable is used so that we only try to ++ // fire off one dock dwell - if it fails (because, say, the user has the mouse down), ++ // we don't try again until the user moves the mouse up and down again. ++ if (!this._dockDwelling && !this._box.hover && (this._dockDwellTimeoutId == 0)) { ++ // Save the interaction timestamp so we can detect user input ++ let focusWindow = global.display.focus_window; ++ this._dockDwellUserTime = focusWindow ? focusWindow.user_time : 0; ++ ++ this._dockDwellTimeoutId = Mainloop.timeout_add(this._settings.get_double('show-delay') * 1000, ++ Lang.bind(this, this._dockDwellTimeout)); ++ GLib.Source.set_name_by_id(this._dockDwellTimeoutId, '[dash-to-dock] this._dockDwellTimeout'); ++ } ++ this._dockDwelling = true; ++ } ++ else { ++ this._cancelDockDwell(); ++ this._dockDwelling = false; ++ } ++ }, ++ ++ _cancelDockDwell: function() { ++ if (this._dockDwellTimeoutId != 0) { ++ Mainloop.source_remove(this._dockDwellTimeoutId); ++ this._dockDwellTimeoutId = 0; ++ } ++ }, ++ ++ _dockDwellTimeout: function() { ++ this._dockDwellTimeoutId = 0; ++ ++ if (!this._settings.get_boolean('autohide-in-fullscreen') && this._monitor.inFullscreen) ++ return GLib.SOURCE_REMOVE; ++ ++ // We don't want to open the tray when a modal dialog ++ // is up, so we check the modal count for that. When we are in the ++ // overview we have to take the overview's modal push into account ++ if (Main.modalCount > (Main.overview.visible ? 1 : 0)) ++ return GLib.SOURCE_REMOVE; + -+// simplify global signals and function injections handling -+// abstract class -+const BasicHandler = new Lang.Class({ -+ Name: 'dashToDock.BasicHandler', ++ // If the user interacted with the focus window since we started the tray ++ // dwell (by clicking or typing), don't activate the message tray ++ let focusWindow = global.display.focus_window; ++ let currentUserTime = focusWindow ? focusWindow.user_time : 0; ++ if (currentUserTime != this._dockDwellUserTime) ++ return GLib.SOURCE_REMOVE; + -+ _init: function(){ -+ this._storage = new Object(); ++ // Reuse the pressure version function, the logic is the same ++ this._onPressureSensed(); ++ return GLib.SOURCE_REMOVE; + }, + -+ add: function(/*unlimited 3-long array arguments*/){ ++ _updatePressureBarrier: function() { ++ this._canUsePressure = global.display.supports_extended_barriers(); ++ let pressureThreshold = this._settings.get_double('pressure-threshold'); + -+ // convert arguments object to array, concatenate with generic -+ let args = Array.concat('generic', Array.slice(arguments)); -+ // call addWithLabel with ags as if they were passed arguments -+ this.addWithLabel.apply(this, args); ++ // Remove existing pressure barrier ++ if (this._pressureBarrier) { ++ this._pressureBarrier.destroy(); ++ this._pressureBarrier = null; ++ } ++ ++ if (this._barrier) { ++ this._barrier.destroy(); ++ this._barrier = null; ++ } ++ ++ // Create new pressure barrier based on pressure threshold setting ++ if (this._canUsePressure) { ++ this._pressureBarrier = new Layout.PressureBarrier(pressureThreshold, this._settings.get_double('show-delay')*1000, ++ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW); ++ this._pressureBarrier.connect('trigger', Lang.bind(this, function(barrier) { ++ if (!this._settings.get_boolean('autohide-in-fullscreen') && this._monitor.inFullscreen) ++ return; ++ this._onPressureSensed(); ++ })); ++ } + }, + -+ destroy: function() { -+ for( let label in this._storage ) -+ this.removeWithLabel(label); ++ /** ++ * handler for mouse pressure sensed ++ */ ++ _onPressureSensed: function() { ++ if (Main.overview.visibleTarget) ++ return; ++ ++ // In case the mouse move away from the dock area before hovering it, in such case the leave event ++ // would never be triggered and the dock would stay visible forever. ++ let triggerTimeoutId = Mainloop.timeout_add(250, Lang.bind(this, function() { ++ triggerTimeoutId = 0; ++ ++ let [x, y, mods] = global.get_pointer(); ++ let shouldHide = true; ++ switch (this._position) { ++ case St.Side.LEFT: ++ if (x <= this.staticBox.x2 && ++ x >= this._monitor.x && ++ y >= this._monitor.y && ++ y <= this._monitor.y + this._monitor.height) { ++ shouldHide = false; ++ } ++ break; ++ case St.Side.RIGHT: ++ if (x >= this.staticBox.x1 && ++ x <= this._monitor.x + this._monitor.width && ++ y >= this._monitor.y && ++ y <= this._monitor.y + this._monitor.height) { ++ shouldHide = false; ++ } ++ break; ++ case St.Side.TOP: ++ if (x >= this._monitor.x && ++ x <= this._monitor.x + this._monitor.width && ++ y <= this.staticBox.y2 && ++ y >= this._monitor.y) { ++ shouldHide = false; ++ } ++ break; ++ case St.Side.BOTTOM: ++ if (x >= this._monitor.x && ++ x <= this._monitor.x + this._monitor.width && ++ y >= this.staticBox.y1 && ++ y <= this._monitor.y + this._monitor.height) { ++ shouldHide = false; ++ } ++ } ++ if (shouldHide) { ++ this._hoverChanged(); ++ return GLib.SOURCE_REMOVE; ++ } ++ else { ++ return GLib.SOURCE_CONTINUE; ++ } ++ ++ })); ++ ++ this._show(); + }, + -+ addWithLabel: function( label /* plus unlimited 3-long array arguments*/) { ++ /** ++ * Remove pressure barrier ++ */ ++ _removeBarrier: function() { ++ if (this._barrier) { ++ if (this._pressureBarrier) ++ this._pressureBarrier.removeBarrier(this._barrier); ++ this._barrier.destroy(); ++ this._barrier = null; ++ } ++ this._removeBarrierTimeoutId = 0; ++ return false; ++ }, + -+ if(this._storage[label] == undefined) -+ this._storage[label] = new Array(); ++ /** ++ * Update pressure barrier size ++ */ ++ _updateBarrier: function() { ++ // Remove existing barrier ++ this._removeBarrier(); ++ ++ // The barrier needs to be removed in fullscreen with autohide disabled, otherwise the mouse can ++ // get trapped on monitor. ++ if (this._monitor.inFullscreen && !this._settings.get_boolean('autohide-in-fullscreen')) ++ return ++ ++ // Manually reset pressure barrier ++ // This is necessary because we remove the pressure barrier when it is triggered to show the dock ++ if (this._pressureBarrier) { ++ this._pressureBarrier._reset(); ++ this._pressureBarrier._isTriggered = false; ++ } ++ ++ // Create new barrier ++ // The barrier extends to the whole workarea, minus 1 px to avoid conflicting with other active corners ++ // Note: dash in fixed position doesn't use pressure barrier. ++ if (this._canUsePressure && this._autohideIsEnabled && this._settings.get_boolean('require-pressure-to-show')) { ++ let x1, x2, y1, y2, direction; ++ let workArea = Main.layoutManager.getWorkAreaForMonitor(this._monitor.index) ++ ++ if (this._position == St.Side.LEFT) { ++ x1 = this._monitor.x + 1; ++ x2 = x1; ++ y1 = workArea.y + 1; ++ y2 = workArea.y + workArea.height - 1; ++ direction = Meta.BarrierDirection.POSITIVE_X; ++ } ++ else if (this._position == St.Side.RIGHT) { ++ x1 = this._monitor.x + this._monitor.width - 1; ++ x2 = x1; ++ y1 = workArea.y + 1; ++ y2 = workArea.y + workArea.height - 1; ++ direction = Meta.BarrierDirection.NEGATIVE_X; ++ } ++ else if (this._position == St.Side.TOP) { ++ x1 = workArea.x + 1; ++ x2 = workArea.x + workArea.width - 1; ++ y1 = this._monitor.y; ++ y2 = y1; ++ direction = Meta.BarrierDirection.POSITIVE_Y; ++ } ++ else if (this._position == St.Side.BOTTOM) { ++ x1 = workArea.x + 1; ++ x2 = workArea.x + workArea.width - 1; ++ y1 = this._monitor.y + this._monitor.height; ++ y2 = y1; ++ direction = Meta.BarrierDirection.NEGATIVE_Y; ++ } + -+ // skip first element of the arguments -+ for( let i = 1; i < arguments.length; i++ ) { -+ this._storage[label].push( this._create(arguments[i]) ); ++ this._barrier = new Meta.Barrier({ ++ display: global.display, ++ x1: x1, ++ x2: x2, ++ y1: y1, ++ y2: y2, ++ directions: direction ++ }); ++ if (this._pressureBarrier) ++ this._pressureBarrier.addBarrier(this._barrier); + } ++ }, + ++ _isPrimaryMonitor: function() { ++ return (this._monitorIndex == Main.layoutManager.primaryIndex); + }, + -+ removeWithLabel: function(label){ ++ _resetPosition: function() { ++ // Ensure variables linked to settings are updated. ++ this._updateVisibilityMode(); ++ ++ let extendHeight = this._settings.get_boolean('extend-height'); ++ ++ // Note: do not use the workarea coordinates in the direction on which the dock is placed, ++ // to avoid a loop [position change -> workArea change -> position change] with ++ // fixed dock. ++ let workArea = Main.layoutManager.getWorkAreaForMonitor(this._monitorIndex); + -+ if(this._storage[label]) { -+ for( let i = 0; i < this._storage[label].length; i++ ) { -+ this._remove(this._storage[label][i]); ++ // Reserve space for the dash on the overview ++ // if the dock is on the primary monitor ++ if (this._isPrimaryMonitor()) ++ this._dashSpacer.show(); ++ else ++ // No space is required in the overview of the dash ++ this._dashSpacer.hide(); ++ ++ let fraction = this._settings.get_double('height-fraction'); ++ ++ if (extendHeight) ++ fraction = 1; ++ else if ((fraction < 0) || (fraction > 1)) ++ fraction = 0.95; ++ ++ let anchor_point; ++ ++ if (this._isHorizontal) { ++ this.actor.width = Math.round( fraction * workArea.width); ++ ++ let pos_y; ++ if (this._position == St.Side.BOTTOM) { ++ pos_y = this._monitor.y + this._monitor.height; ++ anchor_point = Clutter.Gravity.SOUTH_WEST; ++ } ++ else { ++ pos_y = this._monitor.y; ++ anchor_point = Clutter.Gravity.NORTH_WEST; + } + -+ delete this._storage[label]; ++ this.actor.move_anchor_point_from_gravity(anchor_point); ++ this.actor.x = workArea.x + Math.round((1 - fraction) / 2 * workArea.width); ++ this.actor.y = pos_y; ++ ++ if (extendHeight) { ++ this.dash._container.set_width(this.actor.width); ++ this.actor.add_style_class_name('extended'); ++ } ++ else { ++ this.dash._container.set_width(-1); ++ this.actor.remove_style_class_name('extended'); ++ } ++ } ++ else { ++ this.actor.height = Math.round(fraction * workArea.height); ++ ++ let pos_x; ++ if (this._position == St.Side.RIGHT) { ++ pos_x = this._monitor.x + this._monitor.width; ++ anchor_point = Clutter.Gravity.NORTH_EAST; ++ } ++ else { ++ pos_x = this._monitor.x; ++ anchor_point = Clutter.Gravity.NORTH_WEST; ++ } ++ ++ this.actor.move_anchor_point_from_gravity(anchor_point); ++ this.actor.x = pos_x; ++ this.actor.y = workArea.y + Math.round((1 - fraction) / 2 * workArea.height); ++ ++ if (extendHeight) { ++ this.dash._container.set_height(this.actor.height); ++ this.actor.add_style_class_name('extended'); ++ } ++ else { ++ this.dash._container.set_height(-1); ++ this.actor.remove_style_class_name('extended'); ++ } + } ++ ++ this._y0 = this.actor.y; ++ ++ this._adjustLegacyTray(); + }, + -+ /* Virtual methods to be implemented by subclass */ -+ // create single element to be stored in the storage structure -+ _create: function(item){ -+ throw new Error('no implementation of _create in ' + this); ++ // Set the dash at the correct depth in z ++ _resetDepth: function() { ++ // Keep the dash below the modalDialogGroup and the legacyTray ++ if (Main.legacyTray && Main.legacyTray.actor) ++ Main.layoutManager.uiGroup.set_child_below_sibling(this.actor, Main.legacyTray.actor); ++ else ++ Main.layoutManager.uiGroup.set_child_below_sibling(this.actor, Main.layoutManager.modalDialogGroup); + }, + -+ // correctly delete single element -+ _remove: function(item){ -+ throw new Error('no implementation of _remove in ' + this); -+ } -+}); ++ _adjustLegacyTray: function() { ++ let use_work_area = true; + -+// Manage global signals -+const GlobalSignalsHandler = new Lang.Class({ -+ Name: 'DashToDock.GlobalSignalHandler', -+ Extends: BasicHandler, ++ if (this._fixedIsEnabled && !this._settings.get_boolean('extend-height') ++ && this._isPrimaryMonitor() ++ && ((this._position == St.Side.BOTTOM) || (this._position == St.Side.LEFT))) ++ use_work_area = false; + -+ _create: function(item) { ++ Main.legacyTray.actor.clear_constraints(); ++ let constraint = new Layout.MonitorConstraint({ ++ primary: true, ++ work_area: use_work_area ++ }); ++ Main.legacyTray.actor.add_constraint(constraint); ++ }, + -+ let object = item[0]; -+ let event = item[1]; -+ let callback = item[2] -+ let id = object.connect(event, callback); ++ _resetLegacyTray: function() { ++ Main.legacyTray.actor.clear_constraints(); ++ let constraint = new Layout.MonitorConstraint({ ++ primary: true, ++ work_area: true ++ }); ++ Main.legacyTray.actor.add_constraint(constraint); ++ }, ++ ++ _updateStaticBox: function() { ++ this.staticBox.init_rect( ++ this.actor.x + this._slider.actor.x - (this._position == St.Side.RIGHT ? this._box.width : 0), ++ this.actor.y + this._slider.actor.y - (this._position == St.Side.BOTTOM ? this._box.height : 0), ++ this._box.width, ++ this._box.height ++ ); + -+ return [object, id]; ++ this._intellihide.updateTargetBox(this.staticBox); + }, + -+ _remove: function(item){ -+ item[0].disconnect(item[1]); -+ } -+}); ++ _removeAnimations: function() { ++ Tweener.removeTweens(this._slider); ++ }, + -+// Manage function injection: both instances and prototype can be overridden -+// and restored -+const InjectionsHandler = new Lang.Class({ -+ Name: 'DashToDock.InjectionsHandler', -+ Extends: BasicHandler, ++ _onDragStart: function() { ++ // The dash need to be above the top_window_group, otherwise it doesn't ++ // accept dnd of app icons when not in overiew mode. ++ Main.layoutManager.uiGroup.set_child_above_sibling(this.actor, global.top_window_group); ++ this._oldignoreHover = this._ignoreHover; ++ this._ignoreHover = true; ++ this._animateIn(this._settings.get_double('animation-time'), 0); ++ }, + -+ _create: function(item) { ++ _onDragEnd: function() { ++ // Restore drag default dash stack order ++ this._resetDepth(); ++ if (this._oldignoreHover !== null) ++ this._ignoreHover = this._oldignoreHover; ++ this._oldignoreHover = null; ++ this._box.sync_hover(); ++ if (Main.overview._shown) ++ this._pageChanged(); ++ }, ++ ++ _pageChanged: function() { ++ let activePage = Main.overview.viewSelector.getActivePage(); ++ let dashVisible = (activePage == ViewSelector.ViewPage.WINDOWS || ++ activePage == ViewSelector.ViewPage.APPS); ++ ++ if (dashVisible) ++ this._animateIn(this._settings.get_double('animation-time'), 0); ++ else ++ this._animateOut(this._settings.get_double('animation-time'), 0); ++ }, ++ ++ _onPageEmpty: function() { ++ /* The dash spacer is required only in the WINDOWS view if in the default position. ++ * The 'page-empty' signal is emitted in between a change of view, ++ * signalling the spacer can be added and removed without visible effect, ++ * as it's done for the upstream dashSpacer. ++ * ++ * Moreover, hiding the spacer ensure the appGrid allocaton is triggered. ++ * This matter as the appview spring animation is triggered by to first reallocaton of the appGrid, ++ * (See appDisplay.js, line 202 on GNOME Shell 3.14: ++ * this._grid.actor.connect('notify::allocation', ...) ++ * which in turn seems to be triggered by changes in the other actors in the overview. ++ * Normally, as far as I could understand, either the dashSpacer being hidden or the workspacesThumbnails ++ * sliding out would trigger the allocation. However, with no stock dash ++ * and no thumbnails, which happen if the user configured only 1 and static workspace, ++ * the animation out of icons is not played. ++ */ ++ ++ let activePage = Main.overview.viewSelector.getActivePage(); ++ this._dashSpacer.visible = (this._isHorizontal || activePage == ViewSelector.ViewPage.WINDOWS); ++ }, ++ ++ /** ++ * Show dock and give key focus to it ++ */ ++ _onAccessibilityFocus: function() { ++ this._box.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); ++ this._animateIn(this._settings.get_double('animation-time'), 0); ++ }, ++ ++ /** ++ * Keep ShowAppsButton status in sync with the overview status ++ */ ++ _syncShowAppsButtonToggled: function() { ++ let status = Main.overview.viewSelector._showAppsButton.checked; ++ if (this.dash.showAppsButton.checked !== status) ++ this.dash.showAppsButton.checked = status; ++ }, ++ ++ // Optional features to be enabled only for the main Dock ++ _enableExtraFeatures: function() { ++ // Restore dash accessibility ++ Main.ctrlAltTabManager.addGroup( ++ this.dash.actor, _('Dash'), 'user-bookmarks-symbolic', ++ {focusCallback: Lang.bind(this, this._onAccessibilityFocus)}); ++ }, ++ ++ /** ++ * Switch workspace by scrolling over the dock ++ */ ++ _optionalScrollWorkspaceSwitch: function() { ++ let label = 'optionalScrollWorkspaceSwitch'; + -+ let object = item[0]; -+ let name = item[1]; -+ let injectedFunction = item[2]; -+ let original = object[name]; ++ function isEnabled() { ++ return this._settings.get_enum('scroll-action') === scrollAction.SWITCH_WORKSPACE; ++ } + -+ object[name] = injectedFunction; -+ return [object, name, injectedFunction, original]; -+ }, ++ this._settings.connect('changed::scroll-action', Lang.bind(this, function() { ++ if (Lang.bind(this, isEnabled)()) ++ Lang.bind(this, enable)(); ++ else ++ Lang.bind(this, disable)(); ++ })); + -+ _remove: function(item) { -+ let object = item[0]; -+ let name = item[1]; -+ let original = item[3]; -+ object[name] = original; -+ } -+}); -diff --git a/extensions/dash-to-dock/myDash.js b/extensions/dash-to-dock/myDash.js -new file mode 100644 -index 0000000..d44a41c ---- /dev/null -+++ b/extensions/dash-to-dock/myDash.js -@@ -0,0 +1,1672 @@ -+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- ++ if (Lang.bind(this, isEnabled)()) ++ Lang.bind(this, enable)(); + -+const Clutter = imports.gi.Clutter; -+const GLib = imports.gi.GLib; -+const Gtk = imports.gi.Gtk; -+const Signals = imports.signals; -+const Lang = imports.lang; -+const Meta = imports.gi.Meta; -+const Shell = imports.gi.Shell; -+const St = imports.gi.St; -+const Mainloop = imports.mainloop; ++ function enable() { ++ this._signalsHandler.removeWithLabel(label); + -+const AppDisplay = imports.ui.appDisplay; -+const AppFavorites = imports.ui.appFavorites; -+const Dash = imports.ui.dash; -+const DND = imports.ui.dnd; -+const IconGrid = imports.ui.iconGrid; -+const Main = imports.ui.main; -+const PopupMenu = imports.ui.popupMenu; -+const Tweener = imports.ui.tweener; -+const Util = imports.misc.util; -+const Workspace = imports.ui.workspace; ++ this._signalsHandler.addWithLabel(label, [ ++ this._box, ++ 'scroll-event', ++ Lang.bind(this, onScrollEvent) ++ ]); + -+const Me = imports.misc.extensionUtils.getCurrentExtension(); -+const Convenience = Me.imports.myConvenience; ++ this._optionalScrollWorkspaceSwitchDeadTimeId = 0; ++ } + -+let DASH_ANIMATION_TIME = Dash.DASH_ANIMATION_TIME; -+let DASH_ITEM_LABEL_SHOW_TIME = Dash.DASH_ITEM_LABEL_SHOW_TIME; -+let DASH_ITEM_LABEL_HIDE_TIME = Dash.DASH_ITEM_LABEL_HIDE_TIME; -+let DASH_ITEM_HOVER_TIMEOUT = Dash.DASH_ITEM_HOVER_TIMEOUT; ++ function disable() { ++ this._signalsHandler.removeWithLabel(label); + -+/* Return the actual position reverseing left and right in rtl */ -+function getPosition(settings) { -+ let position = settings.get_enum('dock-position'); -+ if(Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) { -+ if (position == St.Side.LEFT) -+ position = St.Side.RIGHT; -+ else if (position == St.Side.RIGHT) -+ position = St.Side.LEFT; -+ } -+ return position; -+} ++ if (this._optionalScrollWorkspaceSwitchDeadTimeId > 0) { ++ Mainloop.source_remove(this._optionalScrollWorkspaceSwitchDeadTimeId); ++ this._optionalScrollWorkspaceSwitchDeadTimeId = 0; ++ } ++ } + -+/** -+ * Extend AppIconMenu -+ * -+ * - Pass settings to the constructor -+ * - set popup arrow side based on dash orientation -+ * - Add close windows option based on quitfromdash extension -+ * (https://github.com/deuill/shell-extension-quitfromdash) -+ */ ++ // This was inspired to desktop-scroller@obsidien.github.com ++ function onScrollEvent(actor, event) { ++ // When in overview change workscape only in windows view ++ if (Main.overview.visible && Main.overview.viewSelector.getActivePage() !== ViewSelector.ViewPage.WINDOWS) ++ return false; + -+const myAppIconMenu = new Lang.Class({ -+ Name: 'myAppIconMenu', -+ Extends: AppDisplay.AppIconMenu, ++ let activeWs = global.screen.get_active_workspace(); ++ let direction = null; + -+ _init: function(source, settings) { ++ switch (event.get_scroll_direction()) { ++ case Clutter.ScrollDirection.UP: ++ direction = Meta.MotionDirection.UP; ++ break; ++ case Clutter.ScrollDirection.DOWN: ++ direction = Meta.MotionDirection.DOWN; ++ break; ++ case Clutter.ScrollDirection.SMOOTH: ++ let [dx, dy] = event.get_scroll_delta(); ++ if (dy < 0) ++ direction = Meta.MotionDirection.UP; ++ else if (dy > 0) ++ direction = Meta.MotionDirection.DOWN; ++ break; ++ } + -+ let side = getPosition(settings); ++ if (direction !== null) { ++ // Prevent scroll events from triggering too many workspace switches ++ // by adding a 250ms deadtime between each scroll event. ++ // Usefull on laptops when using a touchpad. + -+ // Damm it, there has to be a proper way of doing this... -+ // As I can't call the parent parent constructor (?) passing the side -+ // parameter, I overwite what I need later -+ this.parent(source); ++ // During the deadtime do nothing ++ if (this._optionalScrollWorkspaceSwitchDeadTimeId > 0) ++ return false; ++ else ++ this._optionalScrollWorkspaceSwitchDeadTimeId = Mainloop.timeout_add(250, Lang.bind(this, function() { ++ this._optionalScrollWorkspaceSwitchDeadTimeId = 0; ++ })); + -+ // Change the initialized side where required. -+ this._arrowSide = side; -+ this._boxPointer._arrowSide = side; -+ this._boxPointer._userArrowSide = side; -+ }, ++ let ws; + -+ // helper function for the quit windows abilities -+ _closeWindowInstance: function(metaWindow) { -+ metaWindow.delete(global.get_current_time()); -+ }, ++ ws = activeWs.get_neighbor(direction) + -+ _redisplay: function() { ++ if (Main.wm._workspaceSwitcherPopup == null) ++ Main.wm._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup(); ++ // Set the actor non reactive, so that it doesn't prevent the ++ // clicks events from reaching the dash actor. I can't see a reason ++ // why it should be reactive. ++ Main.wm._workspaceSwitcherPopup.actor.reactive = false; ++ Main.wm._workspaceSwitcherPopup.connect('destroy', function() { ++ Main.wm._workspaceSwitcherPopup = null; ++ }); + -+ this.parent(); ++ // Do not show wokspaceSwithcer in overview ++ if (!Main.overview.visible) ++ Main.wm._workspaceSwitcherPopup.display(direction, ws.index()); ++ Main.wm.actionMoveWorkspace(ws); + -+ // quit menu -+ let app = this._source.app; -+ let count = app.get_n_windows(); -+ if ( count > 0) { -+ this._appendSeparator(); -+ let quitFromDashMenuText = ""; -+ if (count == 1) -+ quitFromDashMenuText = _("Quit"); ++ return true; ++ } + else -+ quitFromDashMenuText = _("Quit " + count + " Windows"); -+ -+ this._quitfromDashMenuItem = this._appendMenuItem(quitFromDashMenuText); -+ this._quitfromDashMenuItem.connect('activate', Lang.bind(this, function() { -+ let app = this._source.app; -+ let windows = app.get_windows(); -+ for (let i = 0; i < windows.length; i++) { -+ this._closeWindowInstance(windows[i]) -+ } -+ })); ++ return false; + } -+ } -+}); ++ }, + -+/** -+ * Extend DashItemContainer -+ * -+ * - Pass settings to the constructor -+ * - set label position based on dash orientation -+ * -+ * I can't subclass the original object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973. -+ * thus use this ugly pattern. -+ */ ++ _activateApp: function(appIndex) { ++ let children = this.dash._box.get_children().filter(function(actor) { ++ return actor.child && ++ actor.child._delegate && ++ actor.child._delegate.app; ++ }); + -+// define first this function to use it in both extendShowAppsIcon and extendDashItemContainer -+function ItemShowLabel() { -+ if (!this._labelText) { -+ return; ++ // Apps currently in the dash ++ let apps = children.map(function(actor) { ++ return actor.child._delegate; ++ }); ++ ++ // Activate with button = 1, i.e. same as left click ++ let button = 1; ++ if (appIndex < apps.length) ++ apps[appIndex].activate(button); + } + -+ this.label.set_text(this._labelText); -+ this.label.opacity = 0; -+ this.label.show(); ++}); + -+ let [stageX, stageY] = this.get_transformed_position(); -+ let node = this.label.get_theme_node(); ++Signals.addSignalMethods(DockedDash.prototype); + -+ let itemWidth = this.allocation.x2 - this.allocation.x1; -+ let itemHeight = this.allocation.y2 - this.allocation.y1; ++/* ++ * Handle keybaord shortcuts ++ */ ++const KeyboardShortcuts = new Lang.Class({ + ++ Name: 'DashToDock.KeyboardShortcuts', + -+ let labelWidth = this.label.get_width(); -+ let labelHeight = this.label.get_height(); ++ _numHotkeys: 10, + -+ let x, y, xOffset, yOffset; ++ _init: function(settings, allDocks){ + -+ let position = getPosition(this._settings); -+ this._isHorizontal = ( position == St.Side.TOP || -+ position == St.Side.BOTTOM); -+ let labelOffset = node.get_length('-x-offset'); ++ this._settings = settings; ++ this._allDocks = allDocks; ++ this._signalsHandler = new Utils.GlobalSignalsHandler(); ++ ++ this._hotKeysEnabled = false; ++ if (this._settings.get_boolean('hot-keys')) ++ this._enableHotKeys(); ++ ++ this._signalsHandler.add([ ++ this._settings, ++ 'changed::hot-keys', ++ Lang.bind(this, function() { ++ if (this._settings.get_boolean('hot-keys')) ++ Lang.bind(this, this._enableHotKeys)(); ++ else ++ Lang.bind(this, this._disableHotKeys)(); ++ }) ++ ]); + -+ switch(position) { -+ case St.Side.LEFT: -+ yOffset = Math.floor((itemHeight - labelHeight) / 2); -+ y = stageY + yOffset; -+ xOffset = labelOffset; -+ x = stageX + this.get_width() + xOffset; -+ break; -+ case St.Side.RIGHT: -+ yOffset = Math.floor((itemHeight - labelHeight) / 2); -+ y = stageY + yOffset; -+ xOffset = labelOffset; -+ x = Math.round(stageX) - labelWidth - xOffset; -+ break; -+ case St.Side.TOP: -+ y = stageY + labelOffset + itemHeight; -+ xOffset = Math.floor((itemWidth - labelWidth) / 2); -+ x = stageX + xOffset; -+ break; -+ case St.Side.BOTTOM: -+ yOffset = labelOffset; -+ y = stageY - labelHeight - yOffset; -+ xOffset = Math.floor((itemWidth - labelWidth) / 2); -+ x = stageX + xOffset; -+ break; -+ } ++ this._optionalNumberOverlay(); ++ }, + -+ // keep the label inside the screen border -+ // Only needed fot the x coordinate. ++ destroy: function (){ ++ // Remove keybindings ++ this._disableHotKeys(); ++ this._disableExtraShortcut(); ++ this._signalsHandler.destroy(); ++ }, + -+ // Leave a few pixel gap -+ let gap = 5; -+ let monitor = Main.layoutManager.findMonitorForActor(this); -+ if ( x - monitor.x monitor.x + monitor.width - gap) -+ x-= x + labelWidth -( monitor.x + monitor.width) + gap; ++ _enableHotKeys: function() { ++ if (this._hotKeysEnabled) ++ return; + -+ this.label.set_position(x, y); -+ Tweener.addTween(this.label, -+ { opacity: 255, -+ time: DASH_ITEM_LABEL_SHOW_TIME, -+ transition: 'easeOutQuad', -+ }); -+}; ++ // Setup keyboard bindings for dash elements ++ let keys = ['app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-', // Regular numbers ++ 'app-hotkey-kp-', 'app-shift-hotkey-kp-', 'app-ctrl-hotkey-kp-']; // Key-pad numbers ++ keys.forEach( function(key) { ++ for (let i = 0; i < this._numHotkeys; i++) { ++ let appNum = i; ++ Main.wm.addKeybinding(key + (i + 1), this._settings, ++ Meta.KeyBindingFlags.NONE, ++ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW, ++ Lang.bind(this, function() { ++ this._allDocks[0]._activateApp(appNum); ++ this._showOverlay(); ++ })); ++ } ++ }, this); + -+function extendDashItemContainer(dashItemContainer, settings) { ++ this._hotKeysEnabled = true; ++ }, + -+ dashItemContainer._settings = settings; -+ dashItemContainer.showLabel = ItemShowLabel; -+}; ++ _disableHotKeys: function() { ++ if (!this._hotKeysEnabled) ++ return; + -+/* -+ * A menu for the showAppsIcon -+*/ -+const myShowAppsIconMenu = new Lang.Class({ ++ let keys = ['app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-', // Regular numbers ++ 'app-hotkey-kp-', 'app-shift-hotkey-kp-', 'app-ctrl-hotkey-kp-']; // Key-pad numbers ++ keys.forEach( function(key) { ++ for (let i = 0; i < this._numHotkeys; i++) ++ Main.wm.removeKeybinding(key + (i + 1)); ++ }, this); ++ ++ this._hotKeysEnabled = false; ++ }, ++ ++ _optionalNumberOverlay: function() { ++ this._shortcutIsSet = false; ++ // Enable extra shortcut if either 'overlay' or 'show-dock' are true ++ if (this._settings.get_boolean('hot-keys') && ++ (this._settings.get_boolean('hotkeys-overlay') || this._settings.get_boolean('hotkeys-show-dock'))) ++ this._enableExtraShortcut(); ++ ++ this._signalsHandler.add([ ++ this._settings, ++ 'changed::hot-keys', ++ Lang.bind(this, this._checkHotkeysOptions) ++ ], [ ++ this._settings, ++ 'changed::hotkeys-overlay', ++ Lang.bind(this, this._checkHotkeysOptions) ++ ], [ ++ this._settings, ++ 'changed::hotkeys-show-dock', ++ Lang.bind(this, this._checkHotkeysOptions) ++ ]); ++ }, ++ ++ _checkHotkeysOptions: function() { ++ if (this._settings.get_boolean('hot-keys') && ++ (this._settings.get_boolean('hotkeys-overlay') || this._settings.get_boolean('hotkeys-show-dock'))) ++ this._enableExtraShortcut(); ++ else ++ this._disableExtraShortcut(); ++ }, + -+ Name: 'dashToDockShowAppsIconMenu', -+ Extends: myAppIconMenu, ++ _enableExtraShortcut: function() { ++ if (!this._shortcutIsSet) { ++ Main.wm.addKeybinding('shortcut', this._settings, ++ Meta.KeyBindingFlags.NONE, ++ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW, ++ Lang.bind(this, this._showOverlay)); ++ this._shortcutIsSet = true; ++ } ++ }, + -+ _redisplay: function() { -+ this.removeAll(); ++ _disableExtraShortcut: function() { ++ if (this._shortcutIsSet) { ++ Main.wm.removeKeybinding('shortcut'); ++ this._shortcutIsSet = false; ++ } ++ }, + -+ let item = this._appendMenuItem(_("Dash to Dock Settings")); ++ _showOverlay: function() { ++ for (let i = 0; i < this._allDocks.length; i++) { ++ let dock = this._allDocks[i]; ++ if (dock._settings.get_boolean('hotkeys-overlay')) ++ dock.dash.toggleNumberOverlay(true); + -+ item.connect('activate', function () { -+ Util.spawn(["gnome-shell-extension-prefs", Me.metadata.uuid]); -+ }); ++ // Restart the counting if the shortcut is pressed again ++ if (dock._numberOverlayTimeoutId) { ++ Mainloop.source_remove(dock._numberOverlayTimeoutId); ++ dock._numberOverlayTimeoutId = 0; ++ } ++ ++ // Hide the overlay/dock after the timeout ++ let timeout = dock._settings.get_double('shortcut-timeout') * 1000; ++ dock._numberOverlayTimeoutId = Mainloop.timeout_add(timeout, Lang.bind(dock, function() { ++ dock._numberOverlayTimeoutId = 0; ++ dock.dash.toggleNumberOverlay(false); ++ // Hide the dock again if necessary ++ dock._updateDashVisibility(); ++ })); ++ ++ // Show the dock if it is hidden ++ if (dock._settings.get_boolean('hotkeys-show-dock')) { ++ let showDock = (dock._intellihideIsEnabled || dock._autohideIsEnabled); ++ if (showDock) ++ dock._show(); ++ } ++ } + } + +}); + +/** -+ * Extend ShowAppsIcon -+ * -+ * - Pass settings to the constructor -+ * - set label position based on dash orientation -+ * - implement a popupMenu based on the AppIcon code -+ * -+ * I can't subclass the original object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973. -+ * thus use this ugly pattern. ++ * Isolate overview to open new windows for inactive apps ++ * Note: the future implementaion is not fully contained here. Some bits are around in other methods of other classes. ++ * This class just take care of enabling/disabling the option. + */ ++const WorkspaceIsolation = new Lang.Class({ ++ ++ Name: 'DashToDock.WorkspaceIsolation', ++ ++ _init: function(settings, allDocks) { ++ ++ this._settings = settings; ++ this._allDocks = allDocks; + -+function extendShowAppsIcon(showAppsIcon, settings){ ++ this._signalsHandler = new Utils.GlobalSignalsHandler(); ++ this._injectionsHandler = new Utils.InjectionsHandler(); + ++ this._signalsHandler.add([ ++ this._settings, ++ 'changed::isolate-workspaces', ++ Lang.bind(this, function() { ++ this._allDocks.forEach(function(dock) { ++ dock.dash.resetAppIcons(); ++ }); ++ if (this._settings.get_boolean('isolate-workspaces') || ++ this._settings.get_boolean('isolate-monitors')) ++ Lang.bind(this, this._enable)(); ++ else ++ Lang.bind(this, this._disable)(); ++ }) ++ ],[ ++ this._settings, ++ 'changed::isolate-monitors', ++ Lang.bind(this, function() { ++ this._allDocks.forEach(function(dock) { ++ dock.dash.resetAppIcons(); ++ }); ++ if (this._settings.get_boolean('isolate-workspaces') || ++ this._settings.get_boolean('isolate-monitors')) ++ Lang.bind(this, this._enable)(); ++ else ++ Lang.bind(this, this._disable)(); ++ }) ++ ]); + -+ showAppsIcon._settings = settings; -+ /* the variable equivalent to toggleButton has a different name in the appIcon class -+ (actor): duplicate reference to easily reuse appIcon methods */ -+ showAppsIcon.actor = showAppsIcon.toggleButton; ++ if (this._settings.get_boolean('isolate-workspaces') || ++ this._settings.get_boolean('isolate-monitors')) ++ this._enable(); + -+ // Re-use appIcon methods -+ showAppsIcon._removeMenuTimeout = AppDisplay.AppIcon.prototype._removeMenuTimeout; -+ showAppsIcon._setPopupTimeout = AppDisplay.AppIcon.prototype._setPopupTimeout; -+ showAppsIcon._onButtonPress = AppDisplay.AppIcon.prototype._onButtonPress; -+ showAppsIcon._onKeyboardPopupMenu = AppDisplay.AppIcon.prototype._onKeyboardPopupMenu; -+ showAppsIcon._onLeaveEvent = AppDisplay.AppIcon.prototype._onLeaveEvent; -+ showAppsIcon._onTouchEvent = AppDisplay.AppIcon.prototype._onTouchEvent; -+ showAppsIcon._onMenuPoppedDown = AppDisplay.AppIcon.prototype._onMenuPoppedDown; ++ }, + ++ _enable: function() { + -+ // No action on clicked (showing of the appsview is controlled elsewhere) -+ showAppsIcon._onClicked = function(actor, button) { -+ showAppsIcon._removeMenuTimeout(); -+ }; ++ // ensure I never double-register/inject ++ // although it should never happen ++ this._disable(); + ++ this._allDocks.forEach(function(dock) { ++ this._signalsHandler.addWithLabel('isolation', [ ++ global.screen, ++ 'restacked', ++ Lang.bind(dock.dash, dock.dash._queueRedisplay) ++ ], [ ++ global.window_manager, ++ 'switch-workspace', ++ Lang.bind(dock.dash, dock.dash._queueRedisplay) ++ ]); ++ ++ // This last signal is only needed for monitor isolation, as windows ++ // might migrate from one monitor to another without triggering 'restacked' ++ if (this._settings.get_boolean('isolate-monitors')) ++ this._signalsHandler.addWithLabel('isolation', [ ++ global.screen, ++ 'window-entered-monitor', ++ Lang.bind(dock.dash, dock.dash._queueRedisplay) ++ ]); ++ ++ }, this); ++ ++ // here this is the Shell.App ++ function IsolatedOverview() { ++ // These lines take care of Nautilus for icons on Desktop ++ let windows = this.get_windows().filter(function(w) { ++ return w.get_workspace().index() == global.screen.get_active_workspace_index(); ++ }); ++ if (windows.length == 1) ++ if (windows[0].skip_taskbar) ++ return this.open_new_window(-1); + -+ showAppsIcon.actor.connect('leave-event', Lang.bind( showAppsIcon, showAppsIcon._onLeaveEvent)); -+ showAppsIcon.actor.connect('button-press-event', Lang.bind( showAppsIcon, showAppsIcon._onButtonPress)); -+ showAppsIcon.actor.connect('touch-event', Lang.bind( showAppsIcon, showAppsIcon._onTouchEvent)); -+ showAppsIcon.actor.connect('clicked', Lang.bind( showAppsIcon, showAppsIcon._onClicked)); -+ showAppsIcon.actor.connect('popup-menu', Lang.bind( showAppsIcon, showAppsIcon._onKeyboardPopupMenu)); ++ if (this.is_on_workspace(global.screen.get_active_workspace())) ++ return Main.activateWindow(windows[0]); ++ return this.open_new_window(-1); ++ } + -+ showAppsIcon._menu = null; -+ showAppsIcon._menuManager = new PopupMenu.PopupMenuManager(showAppsIcon); -+ showAppsIcon._menuTimeoutId = 0; ++ this._injectionsHandler.addWithLabel('isolation', [ ++ Shell.App.prototype, ++ 'activate', ++ IsolatedOverview ++ ]); ++ }, + -+ -+ showAppsIcon.showLabel = ItemShowLabel; ++ _disable: function () { ++ this._signalsHandler.removeWithLabel('isolation'); ++ this._injectionsHandler.removeWithLabel('isolation'); ++ }, + ++ destroy: function() { ++ this._signalsHandler.destroy(); ++ this._injectionsHandler.destroy(); ++ } + -+ showAppsIcon.popupMenu = function() { ++}); + -+ showAppsIcon._removeMenuTimeout(); -+ showAppsIcon.actor.fake_release(); + -+ if (!showAppsIcon._menu) { -+ showAppsIcon._menu = new myShowAppsIconMenu(showAppsIcon, showAppsIcon._settings); -+ showAppsIcon._menu.connect('open-state-changed', Lang.bind(showAppsIcon, function (menu, isPoppedUp) { -+ if (!isPoppedUp) -+ showAppsIcon._onMenuPoppedDown(); -+ })); -+ let id = Main.overview.connect('hiding', Lang.bind(showAppsIcon, function () { showAppsIcon._menu.close(); })); -+ showAppsIcon._menu.actor.connect('destroy', function() { -+ Main.overview.disconnect(id); -+ }); -+ showAppsIcon._menuManager.addMenu(showAppsIcon._menu); -+ } ++const DockManager = new Lang.Class({ ++ Name: 'DashToDock.DockManager', + -+ showAppsIcon.emit('menu-state-changed', true); ++ _init: function() { ++ this._settings = Convenience.getSettings('org.gnome.shell.extensions.dash-to-dock'); ++ this._oldDash = Main.overview._dash; ++ /* Array of all the docks created */ ++ this._allDocks = []; ++ this._createDocks(); + -+ showAppsIcon.actor.set_hover(true); -+ showAppsIcon._menu.popup(); -+ showAppsIcon._menuManager.ignoreRelease(); -+ showAppsIcon.emit('sync-tooltip'); ++ // status variable: true when the overview is shown through the dash ++ // applications button. ++ this._forcedOverview = false; + -+ return false; -+ }; ++ // Connect relevant signals to the toggling function ++ this._bindSettingsChanges(); ++ }, + -+ Signals.addSignalMethods(showAppsIcon); -+} ++ _toggle: function() { ++ this._deleteDocks(); ++ this._createDocks(); ++ this.emit('toggled'); ++ }, + -+/* This class is a fork of the upstream DashActor class (ui.dash.js) -+ * -+ * Summary of changes: -+ * - passed settings to class as parameter -+ * - modified chldBox calculations for when 'show-apps-at-top' option is checked -+ * - handle horizontal dash -+ */ -+const myDashActor = new Lang.Class({ -+ Name: 'DashToDockmyDashActor', ++ _bindSettingsChanges: function() { ++ // Connect relevant signals to the toggling function ++ this._signalsHandler = new Utils.GlobalSignalsHandler(); ++ this._signalsHandler.add([ ++ global.screen, ++ 'monitors-changed', ++ Lang.bind(this, this._toggle) ++ ], [ ++ this._settings, ++ 'changed::multi-monitor', ++ Lang.bind(this, this._toggle) ++ ], [ ++ this._settings, ++ 'changed::preferred-monitor', ++ Lang.bind(this, this._toggle) ++ ], [ ++ this._settings, ++ 'changed::dock-position', ++ Lang.bind(this, this._toggle) ++ ], [ ++ this._settings, ++ 'changed::extend-height', ++ Lang.bind(this, this._adjustPanelCorners) ++ ], [ ++ this._settings, ++ 'changed::dock-fixed', ++ Lang.bind(this, this._adjustPanelCorners) ++ ]); ++ }, ++ ++ _createDocks: function() { ++ ++ this._preferredMonitorIndex = this._settings.get_int('preferred-monitor'); ++ // In case of multi-monitor, we consider the dock on the primary monitor to be the preferred (main) one ++ // regardless of the settings ++ // The dock goes on the primary monitor also if the settings are incosistent (e.g. desired monitor not connected). ++ if (this._settings.get_boolean('multi-monitor') || ++ this._preferredMonitorIndex < 0 || this._preferredMonitorIndex > Main.layoutManager.monitors.length - 1 ++ ) { ++ this._preferredMonitorIndex = Main.layoutManager.primaryIndex; ++ } else { ++ // Gdk and shell monitors numbering differ at least under wayland: ++ // While the primary monitor appears to be always index 0 in Gdk, ++ // the shell can assign a different number (Main.layoutManager.primaryMonitor) ++ // This ensure the indexing in the settings (Gdk) and in the shell are matched, ++ // i.e. that we start counting from the primaryMonitorIndex ++ this._preferredMonitorIndex = (Main.layoutManager.primaryIndex + this._preferredMonitorIndex) % Main.layoutManager.monitors.length ; ++ } + -+ _init: function(settings) { -+ this._settings = settings; -+ this._rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL; ++ // First we create the main Dock, to get the extra features to bind to this one ++ let dock = new DockedDash(this._settings, this._preferredMonitorIndex); ++ this._mainShowAppsButton = dock.dash.showAppsButton; ++ this._allDocks.push(dock); + -+ this._position = getPosition(settings); -+ this._isHorizontal = ( this._position == St.Side.TOP || -+ this._position == St.Side.BOTTOM ); ++ // connect app icon into the view selector ++ dock.dash.showAppsButton.connect('notify::checked', Lang.bind(this, this._onShowAppsButtonToggled)); + -+ let layout = new Clutter.BoxLayout({ orientation: -+ this._isHorizontal?Clutter.Orientation.HORIZONTAL:Clutter.Orientation.VERTICAL }); ++ // Make the necessary changes to Main.overview._dash ++ this._prepareMainDash(); + -+ this.actor = new Shell.GenericContainer({ name: 'dash', -+ layout_manager: layout, -+ clip_to_allocation: true }); -+ this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); -+ this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); -+ this.actor.connect('allocate', Lang.bind(this, this._allocate)); ++ // Adjust corners if necessary ++ this._adjustPanelCorners(); + -+ this.actor._delegate = this; ++ if (this._settings.get_boolean('multi-monitor')) { ++ let nMon = Main.layoutManager.monitors.length; ++ for (let iMon = 0; iMon < nMon; iMon++) { ++ if (iMon == this._preferredMonitorIndex) ++ continue; ++ let dock = new DockedDash(this._settings, iMon); ++ this._allDocks.push(dock); ++ // connect app icon into the view selector ++ dock.dash.showAppsButton.connect('notify::checked', Lang.bind(this, this._onShowAppsButtonToggled)); ++ } ++ } + ++ // Load optional features. We load *after* the docks are created, since ++ // we need to connect the signals to all dock instances. ++ this._workspaceIsolation = new WorkspaceIsolation(this._settings, this._allDocks); ++ this._keyboardShortcuts = new KeyboardShortcuts(this._settings, this._allDocks); + }, + -+ _allocate: function(actor, box, flags) { -+ let contentBox = box; -+ let availWidth = contentBox.x2 - contentBox.x1; -+ let availHeight = contentBox.y2 - contentBox.y1; ++ _prepareMainDash: function() { ++ // Pretend I'm the dash: meant to make appgrd swarm animation come from the ++ // right position of the appShowButton. ++ Main.overview._dash = this._allDocks[0].dash; + -+ let [appIcons, showAppsButton] = actor.get_children(); -+ let [showAppsMinHeight, showAppsNatHeight] = showAppsButton.get_preferred_height(availWidth); -+ let [showAppsMinWidth, showAppsNatWidth] = showAppsButton.get_preferred_width(availHeight); ++ // set stored icon size to the new dash ++ Main.overview.dashIconSize = this._allDocks[0].dash.iconSize; + -+ let offset_x = this._isHorizontal?showAppsNatWidth:0; -+ let offset_y = this._isHorizontal?0:showAppsNatHeight; ++ // Hide usual Dash ++ Main.overview._controls.dash.actor.hide(); + -+ let childBox = new Clutter.ActorBox(); -+ if( (this._settings.get_boolean('show-apps-at-top') && !this._isHorizontal) -+ || (this._settings.get_boolean('show-apps-at-top') && !this._rtl) -+ || (!this._settings.get_boolean('show-apps-at-top') && this._isHorizontal && this._rtl) -+ ) { -+ childBox.x1 = contentBox.x1 + offset_x; -+ childBox.y1 = contentBox.y1 + offset_y; -+ childBox.x2 = contentBox.x2; -+ childBox.y2 = contentBox.y2; -+ appIcons.allocate(childBox, flags); ++ // Also set dash width to 1, so it's almost not taken into account by code ++ // calculaing the reserved space in the overview. The reason to keep it at 1 is ++ // to allow its visibility change to trigger an allocaion of the appGrid which ++ // in turn is triggergin the appsIcon spring animation, required when no other ++ // actors has this effect, i.e in horizontal mode and without the workspaceThumnails ++ // 1 static workspace only) ++ Main.overview._controls.dash.actor.set_width(1); ++ }, + -+ childBox.y1 = contentBox.y1; -+ childBox.x1 = contentBox.x1; -+ childBox.x2 = contentBox.x1 + showAppsNatWidth; -+ childBox.y2 = contentBox.y1 + showAppsNatHeight; -+ showAppsButton.allocate(childBox, flags); -+ } else { -+ childBox.x1 = contentBox.x1; -+ childBox.y1 = contentBox.y1; -+ childBox.x2 = contentBox.x2 - offset_x; -+ childBox.y2 = contentBox.y2 - offset_y; -+ appIcons.allocate(childBox, flags); ++ _deleteDocks: function() { ++ // Remove extra features ++ this._workspaceIsolation.destroy(); ++ this._keyboardShortcuts.destroy(); + -+ childBox.x2 = contentBox.x2; -+ childBox.y2 = contentBox.y2; -+ childBox.x1 = contentBox.x2 - showAppsNatWidth; -+ childBox.y1 = contentBox.y2 - showAppsNatHeight; -+ showAppsButton.allocate(childBox, flags); ++ // Delete all docks ++ let nDocks = this._allDocks.length; ++ for (let i = nDocks-1; i >= 0; i--) { ++ this._allDocks[i].destroy(); ++ this._allDocks.pop(); + } + }, + -+ _getPreferredWidth: function(actor, forHeight, alloc) { -+ // We want to request the natural height of all our children -+ // as our natural height, so we chain up to StWidget (which -+ // then calls BoxLayout), but we only request the showApps -+ // button as the minimum size ++ _restoreDash: function() { ++ Main.overview._controls.dash.actor.show(); ++ Main.overview._controls.dash.actor.set_width(-1); //reset default dash size ++ // This force the recalculation of the icon size ++ Main.overview._controls.dash._maxHeight = -1; + -+ let [, natWidth] = this.actor.layout_manager.get_preferred_width(this.actor, forHeight); ++ // reset stored icon size to the default dash ++ Main.overview.dashIconSize = Main.overview._controls.dash.iconSize; + -+ let themeNode = this.actor.get_theme_node(); -+ let [, showAppsButton] = this.actor.get_children(); -+ let [minWidth, ] = showAppsButton.get_preferred_height(forHeight); ++ Main.overview._dash = this._oldDash; ++ }, + -+ alloc.min_size = minWidth; -+ alloc.natural_size = natWidth; ++ _onShowAppsButtonToggled: function(button) { ++ // Sync the status of the default appButtons. Only if the two statuses are ++ // different, that means the user interacted with the extension provided ++ // application button, cutomize the behaviour. Otherwise the shell has changed the ++ // status (due to the _syncShowAppsButtonToggled function below) and it ++ // has already performed the desired action. + -+ }, ++ let animate = this._settings.get_boolean('animate-show-apps'); ++ let selector = Main.overview.viewSelector; + -+ _getPreferredHeight: function(actor, forWidth, alloc) { -+ // We want to request the natural height of all our children -+ // as our natural height, so we chain up to StWidget (which -+ // then calls BoxLayout), but we only request the showApps -+ // button as the minimum size ++ if (selector._showAppsButton.checked !== button.checked) { ++ // find visible view ++ let visibleView; ++ Main.overview.viewSelector.appDisplay._views.every(function(v, index) { ++ if (v.view.actor.visible) { ++ visibleView = index; ++ return false; ++ } ++ else ++ return true; ++ }); + -+ let [, natHeight] = this.actor.layout_manager.get_preferred_height(this.actor, forWidth); ++ if (button.checked) { ++ // force spring animation triggering.By default the animation only ++ // runs if we are already inside the overview. ++ if (!Main.overview._shown) { ++ this._forcedOverview = true; ++ let view = Main.overview.viewSelector.appDisplay._views[visibleView].view; ++ let grid = view._grid; ++ if (animate) { ++ // Animate in the the appview, hide the appGrid to avoiud flashing ++ // Go to the appView before entering the overview, skipping the workspaces. ++ // Do this manually avoiding opacity in transitions so that the setting of the opacity ++ // to 0 doesn't get overwritten. ++ Main.overview.viewSelector._activePage.opacity = 0; ++ Main.overview.viewSelector._activePage.hide(); ++ Main.overview.viewSelector._activePage = Main.overview.viewSelector._appsPage; ++ Main.overview.viewSelector._activePage.show(); ++ grid.actor.opacity = 0; ++ ++ // The animation has to be trigered manually because the AppDisplay.animate ++ // method is waiting for an allocation not happening, as we skip the workspace view ++ // and the appgrid could already be allocated from previous shown. ++ // It has to be triggered after the overview is shown as wrong coordinates are obtained ++ // otherwise. ++ let overviewShownId = Main.overview.connect('shown', Lang.bind(this, function() { ++ Main.overview.disconnect(overviewShownId); ++ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { ++ grid.actor.opacity = 255; ++ grid.animateSpring(IconGrid.AnimationDirection.IN, this._allDocks[0].dash.showAppsButton); ++ })); ++ })); ++ } ++ else { ++ Main.overview.viewSelector._activePage = Main.overview.viewSelector._appsPage; ++ Main.overview.viewSelector._activePage.show(); ++ grid.actor.opacity = 255; ++ } + -+ let themeNode = this.actor.get_theme_node(); -+ let [, showAppsButton] = this.actor.get_children(); -+ let [minHeight, ] = showAppsButton.get_preferred_height(forWidth); ++ } + -+ alloc.min_size = minHeight; -+ alloc.natural_size = natHeight; -+ } -+}); ++ // Finally show the overview ++ selector._showAppsButton.checked = true; ++ Main.overview.show(); ++ } ++ else { ++ if (this._forcedOverview) { ++ // force exiting overview if needed + -+/* This class is a fork of the upstream dash class (ui.dash.js) -+ * -+ * Summary of changes: -+ * - disconnect global signals adding a destroy method; -+ * - play animations even when not in overview mode -+ * - set a maximum icon size -+ * - show running and/or favorite applications -+ * - emit a custom signal when an app icon is added -+ * - hide showApps label when the custom menu is shown. -+ * - add cleanUpLabels method emitting a signal to hide labels -+ * - Add scrollview -+ * Ensure actor is visible on keyfocus inseid the scrollview -+ * - add 128px icon size, might be usefull for hidpi display -+ * - Sync minimization application target position. -+ */ ++ if (animate) { ++ // Manually trigger springout animation without activating the ++ // workspaceView to avoid the zoomout animation. Hide the appPage ++ // onComplete to avoid ugly flashing of original icons. ++ let view = Main.overview.viewSelector.appDisplay._views[visibleView].view; ++ let grid = view._grid; ++ view.animate(IconGrid.AnimationDirection.OUT, Lang.bind(this, function() { ++ Main.overview.viewSelector._appsPage.hide(); ++ Main.overview.hide(); ++ selector._showAppsButton.checked = false; ++ this._forcedOverview = false; ++ })); ++ } ++ else { ++ Main.overview.hide(); ++ this._forcedOverview = false; ++ } ++ } ++ else { ++ selector._showAppsButton.checked = false; ++ this._forcedOverview = false; ++ } ++ } ++ } ++ ++ // whenever the button is unactivated even if not by the user still reset the ++ // forcedOverview flag ++ if (button.checked == false) ++ this._forcedOverview = false; ++ }, + -+const baseIconSizes = [ 16, 22, 24, 32, 48, 64, 96, 128 ]; ++ destroy: function() { ++ this._signalsHandler.destroy(); ++ this._deleteDocks(); ++ this._revertPanelCorners(); ++ this._restoreDash(); ++ }, + -+const myDash = new Lang.Class({ -+ Name: 'dashToDock.myDash', ++ /** ++ * Adjust Panel corners ++ */ ++ _adjustPanelCorners: function() { ++ let position = Utils.getPosition(this._settings); ++ let isHorizontal = ((position == St.Side.TOP) || (position == St.Side.BOTTOM)); ++ let extendHeight = this._settings.get_boolean('extend-height'); ++ let fixedIsEnabled = this._settings.get_boolean('dock-fixed'); ++ let dockOnPrimary = this._settings.get_boolean('multi-monitor') || ++ this._preferredMonitorIndex == Main.layoutManager.primaryIndex; ++ ++ if (!isHorizontal && dockOnPrimary && extendHeight && fixedIsEnabled) { ++ Main.panel._rightCorner.actor.hide(); ++ Main.panel._leftCorner.actor.hide(); ++ } ++ else ++ this._revertPanelCorners(); ++ }, + -+ _init : function(settings) { -+ this._maxHeight = -1; -+ this.iconSize = 64; -+ this._availableIconSizes = baseIconSizes; -+ this._shownInitially = false; ++ _revertPanelCorners: function() { ++ Main.panel._leftCorner.actor.show(); ++ Main.panel._rightCorner.actor.show(); ++ } ++}); ++Signals.addSignalMethods(DockManager.prototype); +diff --git a/extensions/dash-to-dock/extension.js b/extensions/dash-to-dock/extension.js +new file mode 100644 +index 0000000..86ef700 +--- /dev/null ++++ b/extensions/dash-to-dock/extension.js +@@ -0,0 +1,21 @@ ++// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + -+ this._settings = settings; -+ this._position = getPosition(settings); -+ this._isHorizontal = ( this._position == St.Side.TOP || -+ this._position == St.Side.BOTTOM ); -+ this._signalsHandler = new Convenience.GlobalSignalsHandler(); ++const Me = imports.misc.extensionUtils.getCurrentExtension(); ++const Docking = Me.imports.docking; ++const Convenience = Me.imports.convenience; + -+ this._dragPlaceholder = null; -+ this._dragPlaceholderPos = -1; -+ this._animatingPlaceholdersCount = 0; -+ this._showLabelTimeoutId = 0; -+ this._resetHoverTimeoutId = 0; -+ this._ensureAppIconVisibilityTimeoutId = 0; -+ this._labelShowing = false; ++let dockManager; + -+ this._containerObject = new myDashActor(settings); -+ this._container = this._containerObject.actor; -+ this._scrollView = new St.ScrollView({ name: 'dashtodockDashScrollview', -+ hscrollbar_policy: Gtk.PolicyType.NEVER, -+ vscrollbar_policy: Gtk.PolicyType.NEVER, -+ enable_mouse_scrolling: false }); ++function init() { ++ Convenience.initTranslations('dashtodock'); ++} + -+ this._scrollView.connect('scroll-event', Lang.bind(this, this._onScrollEvent )); ++function enable() { ++ dockManager = new Docking.DockManager(); ++} + -+ this._box = new St.BoxLayout({ vertical: !this._isHorizontal, -+ clip_to_allocation: false, -+ x_align: Clutter.ActorAlign.START, -+ y_align: Clutter.ActorAlign.START }); -+ this._box._delegate = this; -+ this._container.add_actor(this._scrollView); -+ this._scrollView.add_actor(this._box); ++function disable() { ++ dockManager.destroy(); + -+ this._showAppsIcon = new Dash.ShowAppsIcon(); -+ extendShowAppsIcon(this._showAppsIcon, this._settings); -+ this._showAppsIcon.childScale = 1; -+ this._showAppsIcon.childOpacity = 255; -+ this._showAppsIcon.icon.setIconSize(this.iconSize); -+ this._hookUpLabel(this._showAppsIcon); ++ dockManager=null; ++} +diff --git a/extensions/dash-to-dock/intellihide.js b/extensions/dash-to-dock/intellihide.js +new file mode 100644 +index 0000000..0d9fabd +--- /dev/null ++++ b/extensions/dash-to-dock/intellihide.js +@@ -0,0 +1,323 @@ ++// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + ++const GLib = imports.gi.GLib; ++const Lang = imports.lang; ++const Mainloop = imports.mainloop; ++const Meta = imports.gi.Meta; ++const Shell = imports.gi.Shell; + -+ let appsIcon = this._showAppsIcon; -+ appsIcon.connect('menu-state-changed', -+ Lang.bind(this, function(appsIcon, opened) { -+ this._itemMenuStateChanged(appsIcon, opened); -+ })); ++const Main = imports.ui.main; ++const Signals = imports.signals; + -+ this.showAppsButton = this._showAppsIcon.toggleButton; ++const Me = imports.misc.extensionUtils.getCurrentExtension(); ++const Utils = Me.imports.utils; + -+ this._container.add_actor(this._showAppsIcon); ++// A good compromise between reactivity and efficiency; to be tuned. ++const INTELLIHIDE_CHECK_INTERVAL = 100; + -+ let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL; -+ this.actor = new St.Bin({ child: this._container, -+ y_align: St.Align.START, x_align:rtl?St.Align.END:St.Align.START -+ }); ++const OverlapStatus = { ++ UNDEFINED: -1, ++ FALSE: 0, ++ TRUE: 1 ++}; + -+ if(this._isHorizontal) { -+ this.actor.connect('notify::width', Lang.bind(this, -+ function() { -+ if (this._maxHeight != this.actor.width) -+ this._queueRedisplay(); -+ this._maxHeight = this.actor.width; -+ })); -+ } else { -+ this.actor.connect('notify::height', Lang.bind(this, -+ function() { -+ if (this._maxHeight != this.actor.height) -+ this._queueRedisplay(); -+ this._maxHeight = this.actor.height; -+ })); -+ } ++const IntellihideMode = { ++ ALL_WINDOWS: 0, ++ FOCUS_APPLICATION_WINDOWS: 1, ++ MAXIMIZED_WINDOWS : 2 ++}; + -+ // Update minimization animation target position on allocation of the -+ // container and on scrollview change. -+ this._box.connect('notify::allocation', Lang.bind(this, this._updateAppIconsGeometry)); -+ let scrollViewAdjustment = this._isHorizontal?this._scrollView.hscroll.adjustment:this._scrollView.vscroll.adjustment; -+ scrollViewAdjustment.connect('notify::value', Lang.bind(this, this._updateAppIconsGeometry)); ++// List of windows type taken into account. Order is important (keep the original ++// enum order). ++const handledWindowTypes = [ ++ Meta.WindowType.NORMAL, ++ Meta.WindowType.DOCK, ++ Meta.WindowType.DIALOG, ++ Meta.WindowType.MODAL_DIALOG, ++ Meta.WindowType.TOOLBAR, ++ Meta.WindowType.MENU, ++ Meta.WindowType.UTILITY, ++ Meta.WindowType.SPLASHSCREEN ++]; + -+ this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay)); ++/** ++ * A rough and ugly implementation of the intellihide behaviour. ++ * Intallihide object: emit 'status-changed' signal when the overlap of windows ++ * with the provided targetBoxClutter.ActorBox changes; ++ */ ++const Intellihide = new Lang.Class({ ++ Name: 'DashToDock.Intellihide', + -+ this._appSystem = Shell.AppSystem.get_default(); ++ _init: function(settings, monitorIndex) { ++ // Load settings ++ this._settings = settings; ++ this._monitorIndex = monitorIndex; + -+ this._signalsHandler.add( -+ [ -+ this._appSystem, -+ 'installed-changed', -+ Lang.bind(this, function() { -+ AppFavorites.getAppFavorites().reload(); -+ this._queueRedisplay(); -+ }) -+ ], -+ [ -+ AppFavorites.getAppFavorites(), -+ 'changed', -+ Lang.bind(this, this._queueRedisplay) -+ ], -+ [ -+ this._appSystem, -+ 'app-state-changed', -+ Lang.bind(this, this._queueRedisplay) -+ ], -+ [ -+ Main.overview, -+ 'item-drag-begin', -+ Lang.bind(this, this._onDragBegin) -+ ], -+ [ -+ Main.overview, -+ 'item-drag-end', -+ Lang.bind(this, this._onDragEnd) -+ ], -+ [ -+ Main.overview, -+ 'item-drag-cancelled', -+ Lang.bind(this, this._onDragCancelled) -+ ] -+ ); ++ this._signalsHandler = new Utils.GlobalSignalsHandler(); ++ this._tracker = Shell.WindowTracker.get_default(); ++ this._focusApp = null; // The application whose window is focused. ++ this._topApp = null; // The application whose window is on top on the monitor with the dock. + ++ this._isEnabled = false; ++ this.status = OverlapStatus.UNDEFINED; ++ this._targetBox = null; ++ ++ this._checkOverlapTimeoutContinue = false; ++ this._checkOverlapTimeoutId = 0; ++ ++ this._trackedWindows = new Map(); ++ ++ // Connect global signals ++ this._signalsHandler.add([ ++ // Add signals on windows created from now on ++ global.display, ++ 'window-created', ++ Lang.bind(this, this._windowCreated) ++ ], [ ++ // triggered for instance when the window list order changes, ++ // included when the workspace is switched ++ global.screen, ++ 'restacked', ++ Lang.bind(this, this._checkOverlap) ++ ], [ ++ // when windows are alwasy on top, the focus window can change ++ // without the windows being restacked. Thus monitor window focus change. ++ this._tracker, ++ 'notify::focus-app', ++ Lang.bind(this, this._checkOverlap) ++ ], [ ++ // update wne monitor changes, for instance in multimonitor when monitor are attached ++ global.screen, ++ 'monitors-changed', ++ Lang.bind(this, this._checkOverlap ) ++ ]); + }, + + destroy: function() { ++ // Disconnect global signals + this._signalsHandler.destroy(); -+ }, + -+ _onScrollEvent: function(actor, event) { ++ // Remove residual windows signals ++ this.disable(); ++ }, + -+ // reset timeout to avid conflicts with the mousehover event -+ if (this._ensureAppIconVisibilityTimeoutId>0) { -+ Mainloop.source_remove(this._ensureAppIconVisibilityTimeoutId); -+ this._ensureAppIconVisibilityTimeoutId = 0; -+ } ++ enable: function() { ++ this._isEnabled = true; ++ this._status = OverlapStatus.UNDEFINED; ++ global.get_window_actors().forEach(function(wa) { ++ this._addWindowSignals(wa); ++ }, this); ++ this._doCheckOverlap(); ++ }, + -+ // Skip to avoid double events mouse -+ if (event.is_pointer_emulated()) -+ return Clutter.EVENT_STOP; ++ disable: function() { ++ this._isEnabled = false; + -+ let adjustment, delta; ++ for (let wa of this._trackedWindows.keys()) { ++ this._removeWindowSignals(wa); ++ } ++ this._trackedWindows.clear(); + -+ if (this._isHorizontal) -+ adjustment = this._scrollView.get_hscroll_bar().get_adjustment(); -+ else -+ adjustment = this._scrollView.get_vscroll_bar().get_adjustment(); ++ if (this._checkOverlapTimeoutId > 0) { ++ Mainloop.source_remove(this._checkOverlapTimeoutId); ++ this._checkOverlapTimeoutId = 0; ++ } ++ }, + -+ let increment = adjustment.step_increment; ++ _windowCreated: function(display, metaWindow) { ++ this._addWindowSignals(metaWindow.get_compositor_private()); ++ }, + -+ switch ( event.get_scroll_direction() ) { -+ case Clutter.ScrollDirection.UP: -+ delta = -increment; -+ break; -+ case Clutter.ScrollDirection.DOWN: -+ delta = +increment; -+ break; -+ case Clutter.ScrollDirection.SMOOTH: -+ let [dx, dy] = event.get_scroll_delta(); -+ delta = dy*increment; -+ // Also consider horizontal component, for instance touchpad -+ if (this._isHorizontal) -+ delta += dx*increment; -+ break; ++ _addWindowSignals: function(wa) { ++ if (!this._handledWindow(wa)) ++ return; ++ let signalId = wa.connect('allocation-changed', Lang.bind(this, this._checkOverlap, wa.get_meta_window())); ++ this._trackedWindows.set(wa, signalId); ++ wa.connect('destroy', Lang.bind(this, this._removeWindowSignals)); ++ }, + ++ _removeWindowSignals: function(wa) { ++ if (this._trackedWindows.get(wa)) { ++ wa.disconnect(this._trackedWindows.get(wa)); ++ this._trackedWindows.delete(wa); + } + -+ adjustment.set_value(adjustment.get_value() + delta); ++ }, + -+ return Clutter.EVENT_STOP; ++ updateTargetBox: function(box) { ++ this._targetBox = box; ++ this._checkOverlap(); ++ }, + ++ forceUpdate: function() { ++ this._status = OverlapStatus.UNDEFINED; ++ this._doCheckOverlap(); + }, + -+ _onDragBegin: function() { -+ this._dragCancelled = false; -+ this._dragMonitor = { -+ dragMotion: Lang.bind(this, this._onDragMotion) -+ }; -+ DND.addDragMonitor(this._dragMonitor); ++ getOverlapStatus: function() { ++ return (this._status == OverlapStatus.TRUE); ++ }, + -+ if (this._box.get_n_children() == 0) { -+ this._emptyDropTarget = new Dash.EmptyDropTargetItem(); -+ this._box.insert_child_at_index(this._emptyDropTarget, 0); -+ this._emptyDropTarget.show(true); ++ _checkOverlap: function() { ++ if (!this._isEnabled || (this._targetBox == null)) ++ return; ++ ++ /* Limit the number of calls to the doCheckOverlap function */ ++ if (this._checkOverlapTimeoutId) { ++ this._checkOverlapTimeoutContinue = true; ++ return + } -+ }, + -+ _onDragCancelled: function() { -+ this._dragCancelled = true; -+ this._endDrag(); ++ this._doCheckOverlap(); ++ ++ this._checkOverlapTimeoutId = Mainloop.timeout_add(INTELLIHIDE_CHECK_INTERVAL, Lang.bind(this, function() { ++ this._doCheckOverlap(); ++ if (this._checkOverlapTimeoutContinue) { ++ this._checkOverlapTimeoutContinue = false; ++ return GLib.SOURCE_CONTINUE; ++ } else { ++ this._checkOverlapTimeoutId = 0; ++ return GLib.SOURCE_REMOVE; ++ } ++ })); + }, + -+ _onDragEnd: function() { -+ if (this._dragCancelled) ++ _doCheckOverlap: function() { ++ ++ if (!this._isEnabled || (this._targetBox == null)) + return; + -+ this._endDrag(); -+ }, ++ let overlaps = OverlapStatus.FALSE; ++ let windows = global.get_window_actors(); ++ ++ if (windows.length > 0) { ++ /* ++ * Get the top window on the monitor where the dock is placed. ++ * The idea is that we dont want to overlap with the windows of the topmost application, ++ * event is it's not the focused app -- for instance because in multimonitor the user ++ * select a window in the secondary monitor. ++ */ ++ ++ let topWindow = null; ++ for (let i = windows.length - 1; i >= 0; i--) { ++ let meta_win = windows[i].get_meta_window(); ++ if (this._handledWindow(windows[i]) && (meta_win.get_monitor() == this._monitorIndex)) { ++ topWindow = meta_win; ++ break; ++ } ++ } ++ ++ if (topWindow !== null) { ++ this._topApp = this._tracker.get_window_app(topWindow); ++ // If there isn't a focused app, use that of the window on top ++ this._focusApp = this._tracker.focus_app || this._topApp ++ ++ windows = windows.filter(this._intellihideFilterInteresting, this); ++ ++ for (let i = 0; i < windows.length; i++) { ++ let win = windows[i].get_meta_window(); ++ ++ if (win) { ++ let rect = win.get_frame_rect(); ++ ++ let test = (rect.x < this._targetBox.x2) && ++ (rect.x + rect.width > this._targetBox.x1) && ++ (rect.y < this._targetBox.y2) && ++ (rect.y + rect.height > this._targetBox.y1); ++ ++ if (test) { ++ overlaps = OverlapStatus.TRUE; ++ break; ++ } ++ } ++ } ++ } ++ } ++ ++ if (this._status !== overlaps) { ++ this._status = overlaps; ++ this.emit('status-changed', this._status); ++ } + -+ _endDrag: function() { -+ this._clearDragPlaceholder(); -+ this._clearEmptyDropTarget(); -+ this._showAppsIcon.setDragApp(null); -+ DND.removeDragMonitor(this._dragMonitor); + }, + -+ _onDragMotion: function(dragEvent) { -+ let app = Dash.getAppFromSource(dragEvent.source); -+ if (app == null) -+ return DND.DragMotionResult.CONTINUE; ++ // Filter interesting windows to be considered for intellihide. ++ // Consider all windows visible on the current workspace. ++ // Optionally skip windows of other applications ++ _intellihideFilterInteresting: function(wa) { ++ let meta_win = wa.get_meta_window(); ++ if (!this._handledWindow(wa)) ++ return false; + -+ let showAppsHovered = -+ this._showAppsIcon.contains(dragEvent.targetActor); ++ let currentWorkspace = global.screen.get_active_workspace_index(); ++ let wksp = meta_win.get_workspace(); ++ let wksp_index = wksp.index(); + -+ if (!this._box.contains(dragEvent.targetActor) || showAppsHovered) -+ this._clearDragPlaceholder(); ++ // Depending on the intellihide mode, exclude non-relevent windows ++ switch (this._settings.get_enum('intellihide-mode')) { ++ case IntellihideMode.ALL_WINDOWS: ++ // Do nothing ++ break; + -+ if (showAppsHovered) -+ this._showAppsIcon.setDragApp(app); ++ case IntellihideMode.FOCUS_APPLICATION_WINDOWS: ++ // Skip windows of other apps ++ if (this._focusApp) { ++ // The DropDownTerminal extension is not an application per se ++ // so we match its window by wm class instead ++ if (meta_win.get_wm_class() == 'DropDownTerminalWindow') ++ return true; ++ ++ let currentApp = this._tracker.get_window_app(meta_win); ++ let focusWindow = global.display.get_focus_window() ++ ++ // Consider half maximized windows side by side ++ // and windows which are alwayson top ++ if((currentApp != this._focusApp) && (currentApp != this._topApp) ++ && !((focusWindow && focusWindow.maximized_vertically && !focusWindow.maximized_horizontally) ++ && (meta_win.maximized_vertically && !meta_win.maximized_horizontally) ++ && meta_win.get_monitor() == focusWindow.get_monitor()) ++ && !meta_win.is_above()) ++ return false; ++ } ++ break; ++ ++ case IntellihideMode.MAXIMIZED_WINDOWS: ++ // Skip unmaximized windows ++ if (!meta_win.maximized_vertically && !meta_win.maximized_horizontally) ++ return false; ++ break; ++ } ++ ++ if ( wksp_index == currentWorkspace && meta_win.showing_on_its_workspace() ) ++ return true; + else -+ this._showAppsIcon.setDragApp(null); ++ return false; + -+ return DND.DragMotionResult.CONTINUE; + }, + -+ _appIdListToHash: function(apps) { -+ let ids = {}; -+ for (let i = 0; i < apps.length; i++) -+ ids[apps[i].get_id()] = apps[i]; -+ return ids; -+ }, ++ // Filter windows by type ++ // inspired by Opacify@gnome-shell.localdomain.pl ++ _handledWindow: function(wa) { ++ let metaWindow = wa.get_meta_window(); + -+ _queueRedisplay: function () { -+ Main.queueDeferredWork(this._workId); -+ }, ++ if (!metaWindow) ++ return false; ++ ++ // The DropDownTerminal extension uses the POPUP_MENU window type hint ++ // so we match its window by wm class instead ++ if (metaWindow.get_wm_class() == 'DropDownTerminalWindow') ++ return true; ++ ++ let wtype = metaWindow.get_window_type(); ++ for (let i = 0; i < handledWindowTypes.length; i++) { ++ var hwtype = handledWindowTypes[i]; ++ if (hwtype == wtype) ++ return true; ++ else if (hwtype > wtype) ++ return false; ++ } ++ return false; ++ } ++}); ++ ++Signals.addSignalMethods(Intellihide.prototype); +diff --git a/extensions/dash-to-dock/media/logo.svg b/extensions/dash-to-dock/media/logo.svg +new file mode 100644 +index 0000000..eebd0b1 +--- /dev/null ++++ b/extensions/dash-to-dock/media/logo.svg +@@ -0,0 +1,528 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ image/svg+xml ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Dash to Dock ++ Michele ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/extensions/dash-to-dock/metadata.json.in b/extensions/dash-to-dock/metadata.json.in +new file mode 100644 +index 0000000..a090272 +--- /dev/null ++++ b/extensions/dash-to-dock/metadata.json.in +@@ -0,0 +1,12 @@ ++{ ++"extension-id": "@extension_id@", ++"uuid": "@uuid@", ++"settings-schema": "@gschemaname@", ++"gettext-domain": "@gettext_domain@", ++"original-author": "micxgx@gmail.com", ++"name": "Dash to Dock", ++"description": "A dock for the Gnome Shell. This extension moves the dash out of the overview transforming it in a dock for an easier launching of applications and a faster switching between windows and desktops. Side and bottom placement options are available.", ++"shell-version": [ "@shell_current@" ], ++"version": 45, ++"url": "https://micheleg.github.io/dash-to-dock/" ++} +diff --git a/extensions/dash-to-dock/org.gnome.shell.extensions.dash-to-dock.gschema.xml b/extensions/dash-to-dock/org.gnome.shell.extensions.dash-to-dock.gschema.xml +new file mode 100644 +index 0000000..0f43bd7 +--- /dev/null ++++ b/extensions/dash-to-dock/org.gnome.shell.extensions.dash-to-dock.gschema.xml +@@ -0,0 +1,713 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ 'LEFT' ++ Dock position ++ Dock is shown on the Left, Right, Top or Bottom side of the screen. ++ ++ ++ 0.2 ++ Animation time ++ Sets the time duration of the autohide effect. ++ ++ ++ 0.25 ++ Show delay ++ Sets the delay after the mouse reaches the screen border before showing the dock. ++ ++ ++ 0.20 ++ Show delay ++ Sets the delay after the mouse left the dock before hiding it. ++ ++ ++ false ++ Set a custom dash background background color ++ Sets the color for the dash background. ++ ++ ++ "#ffffff" ++ Dash background color. ++ Customize the background color of the dash. ++ ++ ++ false ++ Dash background is opaque ++ Makes the background of the dash opaque improving readability when in autohide mode. ++ ++ ++ 0.8 ++ Opacity of the dash background ++ Sets the opacity of the dash background when in autohide mode. ++ ++ ++ true ++ Dock dodges windows ++ Enable or disable intellihide mode ++ ++ ++ 'FOCUS_APPLICATION_WINDOWS' ++ Define which windows are considered for intellihide. ++ ++ ++ ++ true ++ Dock shown on mouse over ++ Enable or disable autohide mode ++ ++ ++ true ++ Require pressure to show dash ++ Enable or disable requiring pressure to show the dash ++ ++ ++ 100 ++ Pressure threshold ++ Sets how much pressure is needed to show the dash. ++ ++ ++ false ++ Enable autohide in fullscreen mode. ++ Enable autohide in fullscreen mode. ++ ++ ++ false ++ Dock always visible ++ Dock is always visible ++ ++ ++ true ++ Switch workspace by scrolling over the dock ++ Add the possibility to switch workspace by mouse scrolling over the dock. ++ ++ ++ 48 ++ Maximum dash icon size ++ Set the allowed maximum dash icon size. Allowed range: 16..64. ++ ++ ++ false ++ Fixed icon size ++ Keep the icon size fived by scrolling the dock. ++ ++ ++ false ++ Apply custom theme ++ Apply customization to the dash appearance ++ ++ ++ false ++ TODO ++ TODO ++ ++ ++ false ++ TODO ++ TODO ++ ++ ++ false ++ Customize the style of the running application indicators. ++ Customize the style of the running application indicators. ++ ++ ++ "#ffffff" ++ Running application indicators color ++ Customize the color of the running application indicators. ++ ++ ++ "#ffffff" ++ Running application indicators border color. ++ Customize the border color of the running application indicators. ++ ++ ++ 0 ++ Running application indicators border width. ++ Customize the border width of the running application indicators. ++ ++ ++ true ++ Show running apps ++ Show or hide running appplications icons in the dash ++ ++ ++ false ++ Provide workspace isolation ++ Dash shows only windows from the currentworkspace ++ ++ ++ false ++ Provide monitor isolation ++ Dash shows only windows from the monitor ++ ++ ++ true ++ Show preview of the open windows ++ Replace open windows list with windows previews ++ ++ ++ true ++ Show favorites apps ++ Show or hide favorite appplications icons in the dash ++ ++ ++ true ++ Show applications button ++ Show appplications button in the dash ++ ++ ++ false ++ Show application button at top ++ Show appplication button at top of the dash ++ ++ ++ true ++ Animate Show Applications from the desktop ++ Animate Show Applications from the desktop ++ ++ ++ true ++ Basic compatibility with bolt extensions ++ Make the extension work properly when bolt extensions is enabled ++ ++ ++ 0.90 ++ Dock max height (fraction of available space) ++ ++ ++ false ++ Extend the dock container to all the available height ++ ++ ++ -1 ++ Monitor on which putting the dock ++ Set on which monitor to put the dock, use -1 for the primary one ++ ++ ++ false ++ Enable multi-monitor docks ++ Show a dock on every monitor ++ ++ ++ true ++ Customize click behaviour ++ Customize action on various mouse events ++ ++ ++ true ++ Minimize on shift+click ++ ++ ++ true ++ Activate only one window ++ ++ ++ 'cycle-windows' ++ Action when clicking on a running app ++ Set the action that is executed when clicking on the icon of a running application ++ ++ ++ 'do-nothing' ++ Action when scrolling app ++ Set the action that is executed when scrolling on the application icon ++ ++ ++ 'minimize' ++ Action when shit+clicking on a running app ++ Set the action that is executed when shift+clicking on the icon of a running application ++ ++ ++ 'launch' ++ Action when clicking on a running app ++ Set the action that is executed when middle-clicking on the icon of a running application ++ ++ ++ 'launch' ++ Action when clicking on a running app ++ Set the action that is executed when shift+middle-clicking on the icon of a running application ++ ++ ++ true ++ Super Hot-Keys ++ Launch and switch between dash items using Super+(0-9) ++ ++ ++ true ++ Show the dock when using the hotkeys ++ The dock will be quickly shown so that the number-overlay is visible and app activation is easier ++ ++ ++ "<Super>q" ++ Keybinding to show the dock and the number overlay. ++ Behavior depends on hotkeys-show-dock and hotkeys-overlay. ++ ++ ++ q']]]> ++ Keybinding to show the dock and the number overlay. ++ Behavior depends on hotkeys-show-dock and hotkeys-overlay. ++ ++ ++ 2 ++ Timeout to hide the dock ++ Sets the time duration before the dock is hidden again. ++ ++ ++ true ++ Show the dock when using the hotkeys ++ The dock will be quickly shown so that the number-overlay is visible and app activation is easier ++ ++ ++ 1']]]> ++ Keybinding to launch 1st dash app ++ ++ Keybinding to launch 1st app. ++ ++ ++ ++ 2']]]> ++ Keybinding to launch 2nd dash app ++ ++ Keybinding to launch 2nd app. ++ ++ ++ ++ 3']]]> ++ Keybinding to launch 3rd dash app ++ ++ Keybinding to launch 3rd app. ++ ++ ++ ++ 4']]]> ++ Keybinding to launch 4th dash app ++ ++ Keybinding to launch 4th app. ++ ++ ++ ++ 5']]]> ++ Keybinding to launch 5th dash app ++ ++ Keybinding to launch 5th app. ++ ++ ++ ++ 6']]]> ++ Keybinding to launch 6th dash app ++ ++ Keybinding to launch 6th app. ++ ++ ++ ++ 7']]]> ++ Keybinding to launch 7th dash app ++ ++ Keybinding to launch 7th app. ++ ++ ++ ++ 8']]]> ++ Keybinding to launch 8th dash app ++ ++ Keybinding to launch 8th app. ++ ++ ++ ++ 9']]]> ++ Keybinding to launch 9th dash app ++ ++ Keybinding to launch 9th app. ++ ++ ++ ++ 0']]]> ++ Keybinding to launch 10th dash app ++ ++ Keybinding to launch 10th app. ++ ++ ++ ++ 1']]]> ++ Keybinding to trigger 1st dash app with shift behavior ++ ++ Keybinding to trigger 1st app with shift behavior. ++ ++ ++ ++ 2']]]> ++ Keybinding to trigger 2nd dash app with shift behavior ++ ++ Keybinding to trigger 2nd app with shift behavior. ++ ++ ++ ++ 3']]]> ++ Keybinding to trigger 3rd dash app with shift behavior ++ ++ Keybinding to trigger 3rd app with shift behavior. ++ ++ ++ ++ 4']]]> ++ Keybinding to trigger 4th dash app with shift behavior ++ ++ Keybinding to trigger 4th app with shift behavior. ++ ++ ++ ++ 5']]]> ++ Keybinding to trigger 5th dash app with shift behavior ++ ++ Keybinding to trigger 5th app with shift behavior. ++ ++ ++ ++ 6']]]> ++ Keybinding to trigger 6th dash app with shift behavior ++ ++ Keybinding to trigger 6th app with shift behavior. ++ ++ ++ ++ 7']]]> ++ Keybinding to trigger 7th dash app with shift behavior ++ ++ Keybinding to trigger 7th app with shift behavior. ++ ++ ++ ++ 8']]]> ++ Keybinding to trigger 8th dash app with shift behavior ++ ++ Keybinding to trigger 8th app with shift behavior. ++ ++ ++ ++ 9']]]> ++ Keybinding to trigger 9th dash app with shift behavior ++ ++ Keybinding to trigger 9th app with shift behavior. ++ ++ ++ ++ 0']]]> ++ Keybinding to trigger 10th dash app with shift behavior ++ ++ Keybinding to trigger 10th app with shift behavior. ++ ++ ++ ++ 1']]]> ++ Keybinding to trigger 1st dash app ++ ++ Keybinding to either show or launch the 1st application in the dash. ++ ++ ++ ++ 2']]]> ++ Keybinding to trigger 2nd dash app ++ ++ Keybinding to either show or launch the 2nd application in the dash. ++ ++ ++ ++ 3']]]> ++ Keybinding to trigger 3rd dash app ++ ++ Keybinding to either show or launch the 3rd application in the dash. ++ ++ ++ ++ 4']]]> ++ Keybinding to trigger 4th dash app ++ ++ Keybinding to either show or launch the 4th application in the dash. ++ ++ ++ ++ 5']]]> ++ Keybinding to trigger 5th dash app ++ ++ Keybinding to either show or launch the 5th application in the dash. ++ ++ ++ ++ 6']]]> ++ Keybinding to trigger 6th dash app ++ ++ Keybinding to either show or launch the 6th application in the dash. ++ ++ ++ ++ 7']]]> ++ Keybinding to trigger 7th dash app ++ ++ Keybinding to either show or launch the 7th application in the dash. ++ ++ ++ ++ 8']]]> ++ Keybinding to trigger 8th dash app ++ ++ Keybinding to either show or launch the 8th application in the dash. ++ ++ ++ ++ 9']]]> ++ Keybinding to trigger 9th dash app ++ ++ Keybinding to either show or launch the 9th application in the dash. ++ ++ ++ ++ 0']]]> ++ Keybinding to trigger 10th dash app ++ ++ Keybinding to either show or launch the 10th application in the dash. ++ ++ ++ ++ KP_1']]]> ++ Keybinding to launch 1st dash app ++ ++ Keybinding to launch 1st app. ++ ++ ++ ++ KP_2']]]> ++ Keybinding to launch 2nd dash app ++ ++ Keybinding to launch 2nd app. ++ ++ ++ ++ KP_3']]]> ++ Keybinding to launch 3rd dash app ++ ++ Keybinding to launch 3rd app. ++ ++ ++ ++ KP_4']]]> ++ Keybinding to launch 4th dash app ++ ++ Keybinding to launch 4th app. ++ ++ ++ ++ KP_5']]]> ++ Keybinding to launch 5th dash app ++ ++ Keybinding to launch 5th app. ++ ++ ++ ++ KP_6']]]> ++ Keybinding to launch 6th dash app ++ ++ Keybinding to launch 6th app. ++ ++ ++ ++ KP_7']]]> ++ Keybinding to launch 7th dash app ++ ++ Keybinding to launch 7th app. ++ ++ ++ ++ KP_8']]]> ++ Keybinding to launch 8th dash app ++ ++ Keybinding to launch 8th app. ++ ++ ++ ++ KP_9']]]> ++ Keybinding to launch 9th dash app ++ ++ Keybinding to launch 9th app. ++ ++ ++ ++ KP_0']]]> ++ Keybinding to launch 10th dash app ++ ++ Keybinding to launch 10th app. ++ ++ ++ ++ KP_1']]]> ++ Keybinding to trigger 1st dash app with shift behavior ++ ++ Keybinding to trigger 1st app with shift behavior. ++ ++ ++ ++ KP_2']]]> ++ Keybinding to trigger 2nd dash app with shift behavior ++ ++ Keybinding to trigger 2nd app with shift behavior. ++ ++ ++ ++ KP_3']]]> ++ Keybinding to trigger 3rd dash app with shift behavior ++ ++ Keybinding to trigger 3rd app with shift behavior. ++ ++ ++ ++ KP_4']]]> ++ Keybinding to trigger 4th dash app with shift behavior ++ ++ Keybinding to trigger 4th app with shift behavior. ++ ++ ++ ++ KP_5']]]> ++ Keybinding to trigger 5th dash app with shift behavior ++ ++ Keybinding to trigger 5th app with shift behavior. ++ ++ ++ ++ KP_6']]]> ++ Keybinding to trigger 6th dash app with shift behavior ++ ++ Keybinding to trigger 6th app with shift behavior. ++ ++ ++ ++ KP_7']]]> ++ Keybinding to trigger 7th dash app with shift behavior ++ ++ Keybinding to trigger 7th app with shift behavior. ++ ++ ++ ++ KP_8']]]> ++ Keybinding to trigger 8th dash app with shift behavior ++ ++ Keybinding to trigger 8th app with shift behavior. ++ ++ ++ ++ KP_9']]]> ++ Keybinding to trigger 9th dash app with shift behavior ++ ++ Keybinding to trigger 9th app with shift behavior. ++ ++ ++ ++ KP_0']]]> ++ Keybinding to trigger 10th dash app with shift behavior ++ ++ Keybinding to trigger 10th app with shift behavior. ++ ++ ++ ++ KP_1']]]> ++ Keybinding to trigger 1st dash app ++ ++ Keybinding to either show or launch the 1st application in the dash. ++ ++ ++ ++ KP_2']]]> ++ Keybinding to trigger 2nd dash app ++ ++ Keybinding to either show or launch the 2nd application in the dash. ++ ++ ++ ++ KP_3']]]> ++ Keybinding to trigger 3rd dash app ++ ++ Keybinding to either show or launch the 3rd application in the dash. ++ ++ ++ ++ KP_4']]]> ++ Keybinding to trigger 4th dash app ++ ++ Keybinding to either show or launch the 4th application in the dash. ++ ++ ++ ++ KP_5']]]> ++ Keybinding to trigger 5th dash app ++ ++ Keybinding to either show or launch the 5th application in the dash. ++ ++ ++ ++ KP_6']]]> ++ Keybinding to trigger 6th dash app ++ ++ Keybinding to either show or launch the 6th application in the dash. ++ ++ ++ ++ KP_7']]]> ++ Keybinding to trigger 7th dash app ++ ++ Keybinding to either show or launch the 7th application in the dash. ++ ++ ++ ++ KP_8']]]> ++ Keybinding to trigger 8th dash app ++ ++ Keybinding to either show or launch the 8th application in the dash. ++ ++ ++ ++ KP_9']]]> ++ Keybinding to trigger 9th dash app ++ ++ Keybinding to either show or launch the 9th application in the dash. ++ ++ ++ ++ KP_0']]]> ++ Keybinding to trigger 10th dash app ++ ++ Keybinding to either show or launch the 10th application in the dash. ++ ++ ++ ++ false ++ Force straight corners in dash ++ Make the borders in the dash non rounded ++ ++ ++ +diff --git a/extensions/dash-to-dock/prefs.js b/extensions/dash-to-dock/prefs.js +new file mode 100644 +index 0000000..5f1f378 +--- /dev/null ++++ b/extensions/dash-to-dock/prefs.js +@@ -0,0 +1,705 @@ ++// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + -+ _hookUpLabel: function(item, appIcon) { -+ item.child.connect('notify::hover', Lang.bind(this, function() { -+ this._syncLabel(item, appIcon); -+ })); ++const Gio = imports.gi.Gio; ++const GLib = imports.gi.GLib; ++const GObject = imports.gi.GObject; ++const Gtk = imports.gi.Gtk; ++const Gdk = imports.gi.Gdk; ++const Lang = imports.lang; ++const Mainloop = imports.mainloop; + -+ let id = Main.overview.connect('hiding', Lang.bind(this, function() { -+ this._labelShowing = false; -+ item.hideLabel(); -+ })); ++// Use __ () and N__() for the extension gettext domain, and reuse ++// the shell domain with the default _() and N_() ++const Gettext = imports.gettext.domain('dashtodock'); ++const __ = Gettext.gettext; ++const N__ = function(e) { return e }; + -+ let id2 = this.connect('cleanup-labels', Lang.bind(this, function() { -+ // setting the hover to false also hide the label -+ item.child.hover = false; -+ })); ++const ExtensionUtils = imports.misc.extensionUtils; ++const Me = ExtensionUtils.getCurrentExtension(); ++const Convenience = Me.imports.convenience; + -+ item.child.connect('destroy', Lang.bind(this, function() { -+ Main.overview.disconnect(id); -+ this.disconnect(id2); -+ })); ++const SCALE_UPDATE_TIMEOUT = 500; ++const DEFAULT_ICONS_SIZES = [ 128, 96, 64, 48, 32, 24, 16 ]; + -+ if (appIcon) { -+ appIcon.connect('sync-tooltip', Lang.bind(this, function() { -+ this._syncLabel(item, appIcon); -+ })); ++/** ++ * This function was copied from the activities-config extension ++ * https://github.com/nls1729/acme-code/tree/master/activities-config ++ * by Norman L. Smith. ++ */ ++function cssHexString(css) { ++ let rrggbb = '#'; ++ let start; ++ for (let loop = 0; loop < 3; loop++) { ++ let end = 0; ++ let xx = ''; ++ for (let loop = 0; loop < 2; loop++) { ++ while (true) { ++ let x = css.slice(end, end + 1); ++ if ((x == '(') || (x == ',') || (x == ')')) ++ break; ++ end++; ++ } ++ if (loop == 0) { ++ end++; ++ start = end; ++ } + } -+ }, -+ -+ cleanUpLabels: function() { -+ this.emit('cleanup-labels'); -+ }, -+ -+ _createAppItem: function(app) { -+ let appIcon = new myAppIcon(this._settings, app, -+ { setSizeManually: true, -+ showLabel: false }); -+ appIcon._draggable.connect('drag-begin', -+ Lang.bind(this, function() { -+ appIcon.actor.opacity = 50; -+ })); -+ appIcon._draggable.connect('drag-end', -+ Lang.bind(this, function() { -+ appIcon.actor.opacity = 255; -+ })); -+ appIcon.connect('menu-state-changed', -+ Lang.bind(this, function(appIcon, opened) { -+ this._itemMenuStateChanged(item, opened); -+ })); ++ xx = parseInt(css.slice(start, end)).toString(16); ++ if (xx.length == 1) ++ xx = '0' + xx; ++ rrggbb += xx; ++ css = css.slice(end); ++ } ++ return rrggbb; ++} + -+ let item = new Dash.DashItemContainer(); -+ extendDashItemContainer(item, this._settings); ++function setShortcut(settings) { ++ let shortcut_text = settings.get_string('shortcut-text'); ++ let [key, mods] = Gtk.accelerator_parse(shortcut_text); + ++ if (Gtk.accelerator_valid(key, mods)) { ++ let shortcut = Gtk.accelerator_name(key, mods); ++ settings.set_strv('shortcut', [shortcut]); ++ } ++ else { ++ settings.set_strv('shortcut', []); ++ } ++} + -+ item.setChild(appIcon.actor); -+ appIcon.actor.connect('notify::hover', Lang.bind(this, function() { -+ if (appIcon.actor.hover){ -+ this._ensureAppIconVisibilityTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function(){ -+ ensureActorVisibleInScrollView(this._scrollView, appIcon.actor); -+ this._ensureAppIconVisibilityTimeoutId = 0; -+ return GLib.SOURCE_REMOVE; -+ })); -+ } else { -+ if (this._ensureAppIconVisibilityTimeoutId>0) { -+ Mainloop.source_remove(this._ensureAppIconVisibilityTimeoutId); -+ this._ensureAppIconVisibilityTimeoutId = 0; -+ } -+ } -+ })); ++const Settings = new Lang.Class({ ++ Name: 'DashToDock.Settings', + -+ appIcon.actor.connect('clicked', -+ Lang.bind(this, function(actor) { -+ ensureActorVisibleInScrollView(this._scrollView, actor); -+ })); ++ _init: function() { ++ this._settings = Convenience.getSettings('org.gnome.shell.extensions.dash-to-dock'); + -+ appIcon.actor.connect('key-focus-in', -+ Lang.bind(this, function(actor) { ++ this._rtl = (Gtk.Widget.get_default_direction() == Gtk.TextDirection.RTL); + -+ let [x_shift, y_shift] = ensureActorVisibleInScrollView(this._scrollView, actor); ++ this._builder = new Gtk.Builder(); ++ this._builder.set_translation_domain(Me.metadata['gettext-domain']); ++ this._builder.add_from_file(Me.path + '/Settings.ui'); + -+ // This signal is triggered also by mouse click. The popup menu is opened at the original -+ // coordinates. Thus correct for the shift which is going to be applied to the scrollview. -+ if (appIcon._menu) { -+ appIcon._menu._boxPointer.xOffset = -x_shift; -+ appIcon._menu._boxPointer.yOffset = -y_shift; -+ } -+ })); ++ this.widget = this._builder.get_object('settings_notebook'); + -+ // Override default AppIcon label_actor, now the -+ // accessible_name is set at DashItemContainer.setLabelText -+ appIcon.actor.label_actor = null; -+ item.setLabelText(app.get_name()); ++ // Timeout to delay the update of the settings ++ this._dock_size_timeout = 0; ++ this._icon_size_timeout = 0; ++ this._opacity_timeout = 0; + -+ appIcon.icon.setIconSize(this.iconSize); -+ this._hookUpLabel(item, appIcon); ++ this._bindSettings(); + -+ return item; ++ this._builder.connect_signals_full(Lang.bind(this, this._connector)); + }, + -+ // Return an array with the "proper" appIcons currently in the dash -+ _getAppIcons: function() { -+ // Only consider children which are "proper" -+ // icons (i.e. ignoring drag placeholders) and which are not -+ // animating out (which means they will be destroyed at the end of -+ // the animation) -+ let iconChildren = this._box.get_children().filter(function(actor) { -+ return actor.child && -+ actor.child._delegate && -+ actor.child._delegate.icon && -+ !actor.animatingOut; -+ }); -+ -+ let appIcons = iconChildren.map(function(actor){ -+ return actor.child._delegate; -+ }); -+ -+ return appIcons; ++ /** ++ * Connect signals ++ */ ++ _connector: function(builder, object, signal, handler) { ++ object.connect(signal, Lang.bind(this, this._SignalHandler[handler])); + }, + -+ _updateAppIconsGeometry: function() { -+ let appIcons = this._getAppIcons(); -+ appIcons.forEach(function(icon){ -+ icon.updateIconGeometry(); -+ }); -+ }, ++ _bindSettings: function() { ++ // Position and size panel + -+ _itemMenuStateChanged: function(item, opened) { -+ // When the menu closes, it calls sync_hover, which means -+ // that the notify::hover handler does everything we need to. -+ if (opened) { -+ if (this._showLabelTimeoutId > 0) { -+ Mainloop.source_remove(this._showLabelTimeoutId); -+ this._showLabelTimeoutId = 0; -+ } ++ // Monitor options + -+ item.hideLabel(); -+ } else { -+ // I want to listen from outside when a menu is closed. I used to -+ // add a custom signal to the appIcon, since gnome 3.8 the signal -+ // calling this callback was added upstream. -+ this.emit('menu-closed'); -+ } -+ }, ++ this._monitors = []; ++ // Build options based on the number of monitors and the current settings. ++ let n_monitors = Gdk.Screen.get_default().get_n_monitors(); ++ let primary_monitor = Gdk.Screen.get_default().get_primary_monitor(); + -+ _syncLabel: function (item, appIcon) { -+ let shouldShow = appIcon ? appIcon.shouldShowTooltip() : item.child.get_hover(); ++ let monitor = this._settings.get_int('preferred-monitor'); + -+ if (shouldShow) { -+ if (this._showLabelTimeoutId == 0) { -+ let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT; -+ this._showLabelTimeoutId = Mainloop.timeout_add(timeout, -+ Lang.bind(this, function() { -+ this._labelShowing = true; -+ item.showLabel(); -+ this._showLabelTimeoutId = 0; -+ return GLib.SOURCE_REMOVE; -+ })); -+ GLib.Source.set_name_by_id(this._showLabelTimeoutId, '[gnome-shell] item.showLabel'); -+ if (this._resetHoverTimeoutId > 0) { -+ Mainloop.source_remove(this._resetHoverTimeoutId); -+ this._resetHoverTimeoutId = 0; -+ } -+ } -+ } else { -+ if (this._showLabelTimeoutId > 0) -+ Mainloop.source_remove(this._showLabelTimeoutId); -+ this._showLabelTimeoutId = 0; -+ item.hideLabel(); -+ if (this._labelShowing) { -+ this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT, -+ Lang.bind(this, function() { -+ this._labelShowing = false; -+ this._resetHoverTimeoutId = 0; -+ return GLib.SOURCE_REMOVE; -+ })); -+ GLib.Source.set_name_by_id(this._resetHoverTimeoutId, '[gnome-shell] this._labelShowing'); ++ // Add primary monitor with index 0, because in GNOME Shell the primary monitor is always 0 ++ this._builder.get_object('dock_monitor_combo').append_text(__('Primary monitor')); ++ this._monitors.push(0); ++ ++ // Add connected monitors ++ let ctr = 0; ++ for (let i = 0; i < n_monitors; i++) { ++ if (i !== primary_monitor) { ++ ctr++; ++ this._monitors.push(ctr); ++ this._builder.get_object('dock_monitor_combo').append_text(__('Secondary monitor ') + ctr); + } + } -+ }, -+ -+ _adjustIconSize: function() { -+ // For the icon size, we only consider children which are "proper" -+ // icons (i.e. ignoring drag placeholders) and which are not -+ // animating out (which means they will be destroyed at the end of -+ // the animation) -+ let iconChildren = this._box.get_children().filter(function(actor) { -+ return actor.child && -+ actor.child._delegate && -+ actor.child._delegate.icon && -+ !actor.animatingOut; -+ }); -+ -+ iconChildren.push(this._showAppsIcon); -+ -+ if (this._maxHeight == -1) -+ return; -+ -+ let themeNode = this._container.get_theme_node(); -+ let maxAllocation = new Clutter.ActorBox({ x1: 0, y1: 0, -+ x2: this._isHorizontal?this._maxHeight:42 /* whatever */, -+ y2: this._isHorizontal?42:this._maxHeight }); -+ let maxContent = themeNode.get_content_box(maxAllocation); -+ let availHeight; -+ if (this._isHorizontal) -+ availHeight = maxContent.x2 - maxContent.x1; -+ else -+ availHeight = maxContent.y2 - maxContent.y1; -+ let spacing = themeNode.get_length('spacing'); -+ -+ let firstButton = iconChildren[0].child; -+ let firstIcon = firstButton._delegate.icon; -+ -+ let minHeight, natHeight, maxWidth, natWidth; -+ -+ // Enforce the current icon size during the size request -+ firstIcon.setIconSize(this.iconSize); -+ [minHeight, natHeight] = firstButton.get_preferred_height(-1); -+ [minWidth, natWidth] = firstButton.get_preferred_width(-1); -+ -+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; -+ let iconSizes = this._availableIconSizes.map(function(s) { -+ return s * scaleFactor; -+ }); + -+ // Subtract icon padding and box spacing from the available height -+ if(this._isHorizontal){ -+ availHeight -= iconChildren.length * (natWidth - this.iconSize * scaleFactor) + -+ (iconChildren.length - 1) * spacing; -+ } else { -+ availHeight -= iconChildren.length * (natHeight - this.iconSize * scaleFactor) + -+ (iconChildren.length - 1) * spacing; ++ // If one of the external monitor is set as preferred, show it even if not attached ++ if ((monitor >= n_monitors) && (monitor !== primary_monitor)) { ++ this._monitors.push(monitor) ++ this._builder.get_object('dock_monitor_combo').append_text(__('Secondary monitor ') + ++ctr); + } + -+ let availSize = availHeight / iconChildren.length; ++ this._builder.get_object('dock_monitor_combo').set_active(this._monitors.indexOf(monitor)); + ++ // Position option ++ let position = this._settings.get_enum('dock-position'); + -+ let newIconSize = this._availableIconSizes[0]; -+ for (let i = 0; i < iconSizes.length; i++) { -+ if (iconSizes[i] < availSize) -+ newIconSize = this._availableIconSizes[i]; ++ switch (position) { ++ case 0: ++ this._builder.get_object('position_top_button').set_active(true); ++ break; ++ case 1: ++ this._builder.get_object('position_right_button').set_active(true); ++ break; ++ case 2: ++ this._builder.get_object('position_bottom_button').set_active(true); ++ break; ++ case 3: ++ this._builder.get_object('position_left_button').set_active(true); ++ break; + } + -+ if (newIconSize == this.iconSize) -+ return; ++ if (this._rtl) { ++ /* Left is Right in rtl as a setting */ ++ this._builder.get_object('position_left_button').set_label(__('Right')); ++ this._builder.get_object('position_right_button').set_label(__('Left')); ++ } + -+ let oldIconSize = this.iconSize; -+ this.iconSize = newIconSize; -+ this.emit('icon-size-changed'); ++ // Intelligent autohide options ++ this._settings.bind('dock-fixed', ++ this._builder.get_object('intelligent_autohide_switch'), ++ 'active', ++ Gio.SettingsBindFlags.INVERT_BOOLEAN); ++ this._settings.bind('dock-fixed', ++ this._builder.get_object('intelligent_autohide_button'), ++ 'sensitive', ++ Gio.SettingsBindFlags.INVERT_BOOLEAN); ++ this._settings.bind('autohide', ++ this._builder.get_object('autohide_switch'), ++ 'active', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('autohide-in-fullscreen', ++ this._builder.get_object('autohide_enable_in_fullscreen_checkbutton'), ++ 'active', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('require-pressure-to-show', ++ this._builder.get_object('require_pressure_checkbutton'), ++ 'active', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('intellihide', ++ this._builder.get_object('intellihide_switch'), ++ 'active', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('animation-time', ++ this._builder.get_object('animation_duration_spinbutton'), ++ 'value', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('hide-delay', ++ this._builder.get_object('hide_timeout_spinbutton'), ++ 'value', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('show-delay', ++ this._builder.get_object('show_timeout_spinbutton'), ++ 'value', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('pressure-threshold', ++ this._builder.get_object('pressure_threshold_spinbutton'), ++ 'value', ++ Gio.SettingsBindFlags.DEFAULT); + -+ let scale = oldIconSize / newIconSize; -+ for (let i = 0; i < iconChildren.length; i++) { -+ let icon = iconChildren[i].child._delegate.icon; ++ //this._builder.get_object('animation_duration_spinbutton').set_value(this._settings.get_double('animation-time')); + -+ // Set the new size immediately, to keep the icons' sizes -+ // in sync with this.iconSize -+ icon.setIconSize(this.iconSize); ++ // Create dialog for intelligent autohide advanced settings ++ this._builder.get_object('intelligent_autohide_button').connect('clicked', Lang.bind(this, function() { + -+ // Don't animate the icon size change when the overview -+ // is transitioning, or when initially filling -+ // the dash -+ if (Main.overview.animationInProgress || -+ !this._shownInitially) -+ continue; ++ let dialog = new Gtk.Dialog({ title: __('Intelligent autohide customization'), ++ transient_for: this.widget.get_toplevel(), ++ use_header_bar: true, ++ modal: true }); + -+ let [targetWidth, targetHeight] = icon.icon.get_size(); ++ // GTK+ leaves positive values for application-defined response ids. ++ // Use +1 for the reset action ++ dialog.add_button(__('Reset to defaults'), 1); + -+ // Scale the icon's texture to the previous size and -+ // tween to the new size -+ icon.icon.set_size(icon.icon.width * scale, -+ icon.icon.height * scale); ++ let box = this._builder.get_object('intelligent_autohide_advanced_settings_box'); ++ dialog.get_content_area().add(box); + -+ Tweener.addTween(icon.icon, -+ { width: targetWidth, -+ height: targetHeight, -+ time: DASH_ANIMATION_TIME, -+ transition: 'easeOutQuad', -+ }); -+ } -+ }, ++ this._settings.bind('intellihide', ++ this._builder.get_object('intellihide_mode_box'), ++ 'sensitive', ++ Gio.SettingsBindFlags.GET); + -+ _redisplay: function () { -+ let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); ++ // intellihide mode + -+ let running = this._appSystem.get_running(); ++ let intellihideModeRadioButtons = [ ++ this._builder.get_object('all_windows_radio_button'), ++ this._builder.get_object('focus_application_windows_radio_button'), ++ this._builder.get_object('maximized_windows_radio_button') ++ ]; + -+ let children = this._box.get_children().filter(function(actor) { -+ return actor.child && -+ actor.child._delegate && -+ actor.child._delegate.app; -+ }); -+ // Apps currently in the dash -+ let oldApps = children.map(function(actor) { -+ return actor.child._delegate.app; -+ }); -+ // Apps supposed to be in the dash -+ let newApps = []; ++ intellihideModeRadioButtons[this._settings.get_enum('intellihide-mode')].set_active(true); + -+ for (let id in favorites) -+ newApps.push(favorites[id]); ++ this._settings.bind('autohide', ++ this._builder.get_object('require_pressure_checkbutton'), ++ 'sensitive', ++ Gio.SettingsBindFlags.GET); + -+ if( this._settings.get_boolean('show-running') ) { -+ for (let i = 0; i < running.length; i++) { -+ let app = running[i]; -+ if (app.get_id() in favorites) -+ continue; -+ newApps.push(app); -+ } -+ } ++ this._settings.bind('autohide', ++ this._builder.get_object('autohide_enable_in_fullscreen_checkbutton'), ++ 'sensitive', ++ Gio.SettingsBindFlags.GET); + -+ // Figure out the actual changes to the list of items; we iterate -+ // over both the list of items currently in the dash and the list -+ // of items expected there, and collect additions and removals. -+ // Moves are both an addition and a removal, where the order of -+ // the operations depends on whether we encounter the position -+ // where the item has been added first or the one from where it -+ // was removed. -+ // There is an assumption that only one item is moved at a given -+ // time; when moving several items at once, everything will still -+ // end up at the right position, but there might be additional -+ // additions/removals (e.g. it might remove all the launchers -+ // and add them back in the new order even if a smaller set of -+ // additions and removals is possible). -+ // If above assumptions turns out to be a problem, we might need -+ // to use a more sophisticated algorithm, e.g. Longest Common -+ // Subsequence as used by diff. -+ let addedItems = []; -+ let removedActors = []; ++ this._settings.bind('require-pressure-to-show', ++ this._builder.get_object('show_timeout_spinbutton'), ++ 'sensitive', ++ Gio.SettingsBindFlags.INVERT_BOOLEAN); ++ this._settings.bind('require-pressure-to-show', ++ this._builder.get_object('show_timeout_label'), ++ 'sensitive', ++ Gio.SettingsBindFlags.INVERT_BOOLEAN); ++ this._settings.bind('require-pressure-to-show', ++ this._builder.get_object('pressure_threshold_spinbutton'), ++ 'sensitive', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('require-pressure-to-show', ++ this._builder.get_object('pressure_threshold_label'), ++ 'sensitive', ++ Gio.SettingsBindFlags.DEFAULT); + -+ let newIndex = 0; -+ let oldIndex = 0; -+ while (newIndex < newApps.length || oldIndex < oldApps.length) { -+ // No change at oldIndex/newIndex -+ if (oldApps[oldIndex] == newApps[newIndex]) { -+ oldIndex++; -+ newIndex++; -+ continue; -+ } ++ dialog.connect('response', Lang.bind(this, function(dialog, id) { ++ if (id == 1) { ++ // restore default settings for the relevant keys ++ let keys = ['intellihide', 'autohide', 'intellihide-mode', 'autohide-in-fullscreen', 'require-pressure-to-show', ++ 'animation-time', 'show-delay', 'hide-delay', 'pressure-threshold']; ++ keys.forEach(function(val) { ++ this._settings.set_value(val, this._settings.get_default_value(val)); ++ }, this); ++ intellihideModeRadioButtons[this._settings.get_enum('intellihide-mode')].set_active(true); ++ } else { ++ // remove the settings box so it doesn't get destroyed; ++ dialog.get_content_area().remove(box); ++ dialog.destroy(); ++ } ++ return; ++ })); + -+ // App removed at oldIndex -+ if (oldApps[oldIndex] && -+ newApps.indexOf(oldApps[oldIndex]) == -1) { -+ removedActors.push(children[oldIndex]); -+ oldIndex++; -+ continue; -+ } ++ dialog.show_all(); + -+ // App added at newIndex -+ if (newApps[newIndex] && -+ oldApps.indexOf(newApps[newIndex]) == -1) { -+ addedItems.push({ app: newApps[newIndex], -+ item: this._createAppItem(newApps[newIndex]), -+ pos: newIndex }); -+ newIndex++; -+ continue; -+ } ++ })); + -+ // App moved -+ let insertHere = newApps[newIndex + 1] && -+ newApps[newIndex + 1] == oldApps[oldIndex]; -+ let alreadyRemoved = removedActors.reduce(function(result, actor) { -+ let removedApp = actor.child._delegate.app; -+ return result || removedApp == newApps[newIndex]; -+ }, false); ++ // size options ++ this._builder.get_object('dock_size_scale').set_value(this._settings.get_double('height-fraction')); ++ this._builder.get_object('dock_size_scale').add_mark(0.9, Gtk.PositionType.TOP, null); ++ let icon_size_scale = this._builder.get_object('icon_size_scale'); ++ icon_size_scale.set_range(8, DEFAULT_ICONS_SIZES[0]); ++ icon_size_scale.set_value(this._settings.get_int('dash-max-icon-size')); ++ DEFAULT_ICONS_SIZES.forEach(function(val) { ++ icon_size_scale.add_mark(val, Gtk.PositionType.TOP, val.toString()); ++ }); + -+ if (insertHere || alreadyRemoved) { -+ let newItem = this._createAppItem(newApps[newIndex]); -+ addedItems.push({ app: newApps[newIndex], -+ item: newItem, -+ pos: newIndex + removedActors.length }); -+ newIndex++; -+ } else { -+ removedActors.push(children[oldIndex]); -+ oldIndex++; -+ } ++ // Corrent for rtl languages ++ if (this._rtl) { ++ // Flip value position: this is not done automatically ++ this._builder.get_object('dock_size_scale').set_value_pos(Gtk.PositionType.LEFT); ++ icon_size_scale.set_value_pos(Gtk.PositionType.LEFT); ++ // I suppose due to a bug, having a more than one mark and one above a value of 100 ++ // makes the rendering of the marks wrong in rtl. This doesn't happen setting the scale as not flippable ++ // and then manually inverting it ++ icon_size_scale.set_flippable(false); ++ icon_size_scale.set_inverted(true); + } + -+ for (let i = 0; i < addedItems.length; i++) -+ this._box.insert_child_at_index(addedItems[i].item, -+ addedItems[i].pos); ++ this._settings.bind('icon-size-fixed', this._builder.get_object('icon_size_fixed_checkbutton'), 'active', Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('extend-height', this._builder.get_object('dock_size_extend_checkbutton'), 'active', Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('extend-height', this._builder.get_object('dock_size_scale'), 'sensitive', Gio.SettingsBindFlags.INVERT_BOOLEAN); + -+ for (let i = 0; i < removedActors.length; i++) { -+ let item = removedActors[i]; + -+ // Don't animate item removal when the overview is transitioning -+ if (!Main.overview.animationInProgress) -+ item.animateOutAndDestroy(); -+ else -+ item.destroy(); -+ } ++ // Apps panel ++ ++ this._settings.bind('show-running', ++ this._builder.get_object('show_running_switch'), ++ 'active', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('isolate-workspaces', ++ this._builder.get_object('application_button_isolation_button'), ++ 'active', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('isolate-monitors', ++ this._builder.get_object('application_button_monitor_isolation_button'), ++ 'active', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('show-windows-preview', ++ this._builder.get_object('windows_preview_button'), ++ 'active', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('multi-monitor', ++ this._builder.get_object('multi_monitor_button'), ++ 'active', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('show-favorites', ++ this._builder.get_object('show_favorite_switch'), ++ 'active', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('show-show-apps-button', ++ this._builder.get_object('show_applications_button_switch'), ++ 'active', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('show-apps-at-top', ++ this._builder.get_object('application_button_first_button'), ++ 'active', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('show-show-apps-button', ++ this._builder.get_object('application_button_first_button'), ++ 'sensitive', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('animate-show-apps', ++ this._builder.get_object('application_button_animation_button'), ++ 'active', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('show-show-apps-button', ++ this._builder.get_object('application_button_animation_button'), ++ 'sensitive', ++ Gio.SettingsBindFlags.DEFAULT); + -+ this._adjustIconSize(); + -+ for (let i = 0; i < addedItems.length; i++){ -+ // Emit a custom signal notifying that a new item has been added -+ this.emit('item-added', addedItems[i]); -+ } ++ // Behavior panel + -+ // Skip animations on first run when adding the initial set -+ // of items, to avoid all items zooming in at once ++ this._settings.bind('hot-keys', ++ this._builder.get_object('hot_keys_switch'), ++ 'active', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('hot-keys', ++ this._builder.get_object('overlay_button'), ++ 'sensitive', ++ Gio.SettingsBindFlags.DEFAULT); + -+ let animate = this._shownInitially && -+ !Main.overview.animationInProgress; ++ this._builder.get_object('click_action_combo').set_active(this._settings.get_enum('click-action')); ++ this._builder.get_object('click_action_combo').connect('changed', Lang.bind (this, function(widget) { ++ this._settings.set_enum('click-action', widget.get_active()); ++ })); + -+ if (!this._shownInitially) -+ this._shownInitially = true; ++ this._builder.get_object('scroll_action_combo').set_active(this._settings.get_enum('scroll-action')); ++ this._builder.get_object('scroll_action_combo').connect('changed', Lang.bind (this, function(widget) { ++ this._settings.set_enum('scroll-action', widget.get_active()); ++ })); + -+ for (let i = 0; i < addedItems.length; i++) { -+ addedItems[i].item.show(animate); -+ } ++ this._builder.get_object('shift_click_action_combo').connect('changed', Lang.bind (this, function(widget) { ++ this._settings.set_enum('shift-click-action', widget.get_active()); ++ })); + -+ // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744 -+ // Without it, StBoxLayout may use a stale size cache -+ this._box.queue_relayout(); ++ this._builder.get_object('middle_click_action_combo').connect('changed', Lang.bind (this, function(widget) { ++ this._settings.set_enum('middle-click-action', widget.get_active()); ++ })); ++ this._builder.get_object('shift_middle_click_action_combo').connect('changed', Lang.bind (this, function(widget) { ++ this._settings.set_enum('shift-middle-click-action', widget.get_active()); ++ })); + -+ // This is required for icon reordering when the scrollview is used. -+ this._updateAppIconsGeometry(); -+ }, ++ // Create dialog for number overlay options ++ this._builder.get_object('overlay_button').connect('clicked', Lang.bind(this, function() { ++ ++ let dialog = new Gtk.Dialog({ title: __('Show dock and application numbers'), ++ transient_for: this.widget.get_toplevel(), ++ use_header_bar: true, ++ modal: true }); + -+ setIconSize: function (max_size, doNotAnimate) { ++ // GTK+ leaves positive values for application-defined response ids. ++ // Use +1 for the reset action ++ dialog.add_button(__('Reset to defaults'), 1); + -+ let max_allowed = baseIconSizes[baseIconSizes.length-1]; -+ max_size = Math.min(max_size, max_allowed); ++ let box = this._builder.get_object('box_overlay_shortcut'); ++ dialog.get_content_area().add(box); + -+ if (this._settings.get_boolean('icon-size-fixed')) { -+ this._availableIconSizes = [ max_size ]; -+ } else { -+ this._availableIconSizes = baseIconSizes.filter( -+ function(val){ -+ return (val numChildren) -+ pos = numChildren; -+ } else -+ pos = 0; // always insert at the top when dash is empty ++ this._settings.bind('custom-theme-running-dots', ++ this._builder.get_object('running_dots_switch'), ++ 'active', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('custom-theme-running-dots', ++ this._builder.get_object('running_dots_advance_settings_button'), ++ 'sensitive', ++ Gio.SettingsBindFlags.DEFAULT); + -+ /* Take into account childredn position in rtl*/ -+ if (this._isHorizontal && -+ Clutter.get_default_text_direction() == Clutter.TextDirection.RTL -+ ) -+ pos = numChildren - pos; ++ // Create dialog for running dots advanced settings ++ this._builder.get_object('running_dots_advance_settings_button').connect('clicked', Lang.bind(this, function() { + -+ if (pos != this._dragPlaceholderPos && pos <= numFavorites && this._animatingPlaceholdersCount == 0) { -+ this._dragPlaceholderPos = pos; ++ let dialog = new Gtk.Dialog({ title: __('Customize running indicators'), ++ transient_for: this.widget.get_toplevel(), ++ use_header_bar: true, ++ modal: true }); + -+ // Don't allow positioning before or after self -+ if (favPos != -1 && (pos == favPos || pos == favPos + 1)) { -+ this._clearDragPlaceholder(); -+ return DND.DragMotionResult.CONTINUE; -+ } ++ let box = this._builder.get_object('running_dots_advance_settings_box'); ++ dialog.get_content_area().add(box); + -+ // If the placeholder already exists, we just move -+ // it, but if we are adding it, expand its size in -+ // an animation -+ let fadeIn; -+ if (this._dragPlaceholder) { -+ this._dragPlaceholder.destroy(); -+ fadeIn = false; -+ } else { -+ fadeIn = true; -+ } ++ this._settings.bind('custom-theme-customize-running-dots', ++ this._builder.get_object('dot_style_switch'), ++ 'active', ++ Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('custom-theme-customize-running-dots', ++ this._builder.get_object('dot_style_settings_box'), ++ 'sensitive', Gio.SettingsBindFlags.DEFAULT); ++ ++ let rgba = new Gdk.RGBA(); ++ rgba.parse(this._settings.get_string('custom-theme-running-dots-color')); ++ this._builder.get_object('dot_color_colorbutton').set_rgba(rgba); ++ ++ this._builder.get_object('dot_color_colorbutton').connect('notify::color', Lang.bind(this, function(button) { ++ let rgba = button.get_rgba(); ++ let css = rgba.to_string(); ++ let hexString = cssHexString(css); ++ this._settings.set_string('custom-theme-running-dots-color', hexString); ++ })); + -+ this._dragPlaceholder = new Dash.DragPlaceholderItem(); -+ this._dragPlaceholder.child.set_width (this.iconSize); -+ this._dragPlaceholder.child.set_height (this.iconSize / 2); -+ this._box.insert_child_at_index(this._dragPlaceholder, -+ this._dragPlaceholderPos); -+ this._dragPlaceholder.show(fadeIn); -+ // Ensure the next and previous icon are visible when moving the placeholder -+ // (I assume there's room for both of them) -+ if (this._dragPlaceholderPos > 1) -+ ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[this._dragPlaceholderPos-1]); -+ if (this._dragPlaceholderPos < this._box.get_children().length-1) -+ ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[this._dragPlaceholderPos+1]); -+ } ++ rgba.parse(this._settings.get_string('custom-theme-running-dots-border-color')); ++ this._builder.get_object('dot_border_color_colorbutton').set_rgba(rgba); + -+ // Remove the drag placeholder if we are not in the -+ // "favorites zone" -+ if (pos > numFavorites) -+ this._clearDragPlaceholder(); ++ this._builder.get_object('dot_border_color_colorbutton').connect('notify::color', Lang.bind(this, function(button) { ++ let rgba = button.get_rgba(); ++ let css = rgba.to_string(); ++ let hexString = cssHexString(css); ++ this._settings.set_string('custom-theme-running-dots-border-color', hexString); ++ })); + -+ if (!this._dragPlaceholder) -+ return DND.DragMotionResult.NO_DROP; ++ this._settings.bind('custom-theme-running-dots-border-width', ++ this._builder.get_object('dot_border_width_spin_button'), ++ 'value', ++ Gio.SettingsBindFlags.DEFAULT); + -+ let srcIsFavorite = (favPos != -1); + -+ if (srcIsFavorite) -+ return DND.DragMotionResult.MOVE_DROP; ++ dialog.connect('response', Lang.bind(this, function(dialog, id) { ++ // remove the settings box so it doesn't get destroyed; ++ dialog.get_content_area().remove(box); ++ dialog.destroy(); ++ return; ++ })); + -+ return DND.DragMotionResult.COPY_DROP; -+ }, ++ dialog.show_all(); + -+ // Draggable target interface -+ acceptDrop : function(source, actor, x, y, time) { -+ let app = Dash.getAppFromSource(source); ++ })); + -+ // Don't allow favoriting of transient apps -+ if (app == null || app.is_window_backed()) { -+ return false; -+ } ++ this._settings.bind('custom-background-color', this._builder.get_object('custom_background_color_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); ++ this._settings.bind('custom-background-color', this._builder.get_object('custom_background_color'), 'sensitive', Gio.SettingsBindFlags.DEFAULT); + -+ let id = app.get_id(); ++ let rgba = new Gdk.RGBA(); ++ rgba.parse(this._settings.get_string('background-color')); ++ this._builder.get_object('custom_background_color').set_rgba(rgba); + -+ let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); ++ this._builder.get_object('custom_background_color').connect('notify::color', Lang.bind(this, function(button) { ++ let rgba = button.get_rgba(); ++ let css = rgba.to_string(); ++ let hexString = cssHexString(css); ++ this._settings.set_string('background-color', hexString); ++ })); + -+ let srcIsFavorite = (id in favorites); ++ this._settings.bind('opaque-background', this._builder.get_object('customize_opacity_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); ++ this._builder.get_object('custom_opacity_scale').set_value(this._settings.get_double('background-opacity')); ++ this._settings.bind('opaque-background', this._builder.get_object('custom_opacity'), 'sensitive', Gio.SettingsBindFlags.DEFAULT); + -+ let favPos = 0; -+ let children = this._box.get_children(); -+ for (let i = 0; i < this._dragPlaceholderPos; i++) { -+ if (this._dragPlaceholder && -+ children[i] == this._dragPlaceholder) -+ continue; ++ this._settings.bind('force-straight-corner', ++ this._builder.get_object('force_straight_corner_switch'), ++ 'active', Gio.SettingsBindFlags.DEFAULT); + -+ let childId = children[i].child._delegate.app.get_id(); -+ if (childId == id) -+ continue; -+ if (childId in favorites) -+ favPos++; -+ } ++ // About Panel + -+ // No drag placeholder means we don't wan't to favorite the app -+ // and we are dragging it to its original position -+ if (!this._dragPlaceholder) -+ return true; ++ this._builder.get_object('extension_version').set_label(Me.metadata.version.toString()); ++ }, + -+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, -+ function () { -+ let appFavorites = AppFavorites.getAppFavorites(); -+ if (srcIsFavorite) -+ appFavorites.moveFavoriteToPos(id, favPos); -+ else -+ appFavorites.addFavoriteAtPos(id, favPos); -+ return false; ++ /** ++ * Object containing all signals defined in the glade file ++ */ ++ _SignalHandler: { ++ dock_display_combo_changed_cb: function(combo) { ++ this._settings.set_int('preferred-monitor', this._monitors[combo.get_active()]); ++ }, ++ ++ position_top_button_toggled_cb: function(button) { ++ if (button.get_active()) ++ this._settings.set_enum('dock-position', 0); ++ }, ++ ++ position_right_button_toggled_cb: function(button) { ++ if (button.get_active()) ++ this._settings.set_enum('dock-position', 1); ++ }, ++ ++ position_bottom_button_toggled_cb: function(button) { ++ if (button.get_active()) ++ this._settings.set_enum('dock-position', 2); ++ }, ++ ++ position_left_button_toggled_cb: function(button) { ++ if (button.get_active()) ++ this._settings.set_enum('dock-position', 3); ++ }, ++ ++ icon_size_combo_changed_cb: function(combo) { ++ this._settings.set_int('dash-max-icon-size', this._allIconSizes[combo.get_active()]); ++ }, ++ ++ dock_size_scale_format_value_cb: function(scale, value) { ++ return Math.round(value*100)+ ' %'; ++ }, ++ ++ dock_size_scale_value_changed_cb: function(scale) { ++ // Avoid settings the size consinuosly ++ if (this._dock_size_timeout > 0) ++ Mainloop.source_remove(this._dock_size_timeout); ++ ++ this._dock_size_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, Lang.bind(this, function() { ++ this._settings.set_double('height-fraction', scale.get_value()); ++ this._dock_size_timeout = 0; ++ return GLib.SOURCE_REMOVE; + })); ++ }, + -+ return true; -+ } -+}); ++ icon_size_scale_format_value_cb: function(scale, value) { ++ return value+ ' px'; ++ }, + -+Signals.addSignalMethods(myDash.prototype); ++ icon_size_scale_value_changed_cb: function(scale) { ++ // Avoid settings the size consinuosly ++ if (this._icon_size_timeout > 0) ++ Mainloop.source_remove(this._icon_size_timeout); + ++ this._icon_size_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, Lang.bind(this, function() { ++ this._settings.set_int('dash-max-icon-size', scale.get_value()); ++ this._icon_size_timeout = 0; ++ return GLib.SOURCE_REMOVE; ++ })); ++ }, + -+/** -+ * Extend AppIcon -+ * -+ * - Pass settings to the constructor and bind settings changes -+ * - Apply a css class based on the number of windows of each application (#N); -+ * a class of the form "running#N" is applied to the AppWellIcon actor. -+ * like the original .running one. -+ * - add a .focused style to the focused app -+ * - Customize click actions. -+ * - Update minimization animation target -+ * -+ */ ++ custom_opacity_scale_value_changed_cb: function(scale) { ++ // Avoid settings the opacity consinuosly as it's change is animated ++ if (this._opacity_timeout > 0) ++ Mainloop.source_remove(this._opacity_timeout); + -+let tracker = Shell.WindowTracker.get_default(); ++ this._opacity_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, Lang.bind(this, function() { ++ this._settings.set_double('background-opacity', scale.get_value()); ++ this._opacity_timeout = 0; ++ return GLib.SOURCE_REMOVE; ++ })); ++ }, + -+const clickAction = { -+ SKIP: 0, -+ MINIMIZE: 1, -+ LAUNCH: 2, -+ CYCLE_WINDOWS: 3 -+}; ++ custom_opacity_scale_format_value_cb: function(scale, value) { ++ return Math.round(value*100) + ' %'; ++ }, + -+let recentlyClickedAppLoopId = 0; -+let recentlyClickedApp = null; -+let recentlyClickedAppWindows = null; -+let recentlyClickedAppIndex = 0; ++ all_windows_radio_button_toggled_cb: function(button) { ++ if (button.get_active()) ++ this._settings.set_enum('intellihide-mode', 0); ++ }, + -+const myAppIcon = new Lang.Class({ -+ Name: 'dashToDock.AppIcon', -+ Extends: AppDisplay.AppIcon, ++ focus_application_windows_radio_button_toggled_cb: function(button) { ++ if (button.get_active()) ++ this._settings.set_enum('intellihide-mode', 1); ++ }, + -+ // settings are required inside. -+ _init: function(settings, app, iconParams, onActivateOverride) { ++ maximized_windows_radio_button_toggled_cb: function(button) { ++ if (button.get_active()) ++ this._settings.set_enum('intellihide-mode', 2); ++ } ++ } ++}); + -+ this._settings = settings; -+ this._maxN =4; ++function init() { ++ Convenience.initTranslations(); ++} + -+ this.parent(app, iconParams, onActivateOverride); ++function buildPrefsWidget() { ++ let settings = new Settings(); ++ let widget = settings.widget; ++ widget.show_all(); ++ return widget; ++} +diff --git a/extensions/dash-to-dock/stylesheet.css b/extensions/dash-to-dock/stylesheet.css +new file mode 100644 +index 0000000..6e9bf38 +--- /dev/null ++++ b/extensions/dash-to-dock/stylesheet.css +@@ -0,0 +1,109 @@ ++/* Shrink the dash by reducing padding and border radius */ ++#dashtodockContainer.shrink #dash, ++#dashtodockContainer.dashtodock #dash { ++ border:1px; ++ padding:0px; ++} + -+ // Monitor windows-changes instead of app state. -+ // Keep using the same Id and function callback (that is extended) -+ if(this._stateChangedId>0){ -+ this.app.disconnect(this._stateChangedId); -+ this._stateChangedId=0; -+ } ++#dashtodockContainer.shrink.left #dash, ++#dashtodockContainer.dashtodock.left #dash { ++ border-left: 0px; ++ border-radius: 0px 9px 9px 0px; ++} + -+ this._stateChangedId = this.app.connect('windows-changed', -+ Lang.bind(this, -+ this.onWindowsChanged)); -+ this._focuseAppChangeId = tracker.connect('notify::focus-app', -+ Lang.bind(this, -+ this._onFocusAppChanged)); -+ -+ /* To keep compatibility with 3.14.0 and 3.14.1 -+ * after upstream commit 24c0a1a1d458c8d1ba1b9d3e728a27d347f7833f -+ * (https://bugzilla.gnome.org/show_bug.cgi?id=739497), -+ * temporary call _updateRunningStyle(). This ensure windows counter updates -+ * on 3.14 and 3.14.1 where the parent not-extended method, which have -+ * a different name, is called instead. -+ */ -+ this._updateRunningStyle(); + -+ }, ++#dashtodockContainer.shrink.right #dash, ++#dashtodockContainer.dashtodock.right #dash { ++ border-right: 0px; ++ border-radius: 9px 0px 0px 9px; ++} + -+ _onDestroy: function() { -+ this.parent(); + -+ // Disconect global signals -+ // stateChangedId is already handled by parent) -+ if(this._focusAppId>0) -+ tracker.disconnect(this._focusAppId); -+ }, ++#dashtodockContainer.shrink.top #dash, ++#dashtodockContainer.dashtodock.top #dash { ++ border-top: 0px; ++ border-radius: 0px 0px 9px 9px; ++} + -+ onWindowsChanged: function() { ++#dashtodockContainer.shrink.bottom #dash, ++#dashtodockContainer.dashtodock.bottom #dash { ++ border-bottom: 0px; ++ border-radius: 9px 9px 0px 0px; ++} + -+ this._updateRunningStyle(); -+ this.updateIconGeometry(); ++#dashtodockContainer.straight-corner #dash, ++#dashtodockContainer.shrink.straight-corner #dash { ++ border-radius: 0px; ++} + -+ }, ++/* Scrollview style */ ++.bottom #dashtodockDashScrollview, ++.top #dashtodockDashScrollview { ++ -st-hfade-offset: 24px; ++} + -+ // Update taraget for minimization animation -+ updateIconGeometry: function() { ++.left #dashtodockDashScrollview, ++.right #dashtodockDashScrollview { ++ -st-vfade-offset: 24px; ++} + -+ let rect = new Meta.Rectangle(); ++#dashtodockContainer.running-dots .dash-item-container > StButton, ++#dashtodockContainer.dashtodock .dash-item-container > StButton { ++ transition-duration: 250; ++ background-size: contain; ++} + -+ [rect.x, rect.y] = this.actor.get_transformed_position(); -+ [rect.width, rect.height] = this.actor.get_transformed_size(); ++#dashtodockContainer.shrink .dash-item-container > StButton, ++#dashtodockContainer.dashtodock .dash-item-container > StButton { ++ padding: 1px 2px; ++} + -+ let windows = this.app.get_windows(); -+ windows.forEach(function(w) { -+ w.set_icon_geometry(rect); -+ }); ++/* Dash height extended to the whole available vertical space */ ++#dashtodockContainer.extended.top #dash, ++#dashtodockContainer.extended.right #dash, ++#dashtodockContainer.extended.bottom #dash, ++#dashtodockContainer.extended.left #dash { ++ border-radius: 0; ++} + -+ }, ++#dashtodockContainer.extended.top #dash, ++#dashtodockContainer.extended.bottom #dash { ++ border-left:0px; ++ border-right:0px; ++} + -+ _updateRunningStyle: function() { ++#dashtodockContainer.extended.right #dash, ++#dashtodockContainer.extended.left #dash { ++ border-top:0px; ++ border-bottom:0px; ++} + -+ /* To keep compatibility with 3.14.0 and 3.14.1 -+ * after upstream commit 24c0a1a1d458c8d1ba1b9d3e728a27d347f7833f -+ * (https://bugzilla.gnome.org/show_bug.cgi?id=739497), -+ * check for which method is defined -+ */ -+ if(AppDisplay.AppIcon.prototype._updateRunningStyle) -+ this.parent(); -+ else -+ AppDisplay.AppIcon.prototype._onStateChanged.call(this); ++/* Running and focused application style */ + -+ this._updateCounterClass(); -+ }, ++#dashtodockContainer.running-dots .app-well-app.running > .overview-icon, ++#dashtodockContainer.dashtodock .app-well-app.running > .overview-icon { ++ background-image:none; ++} + -+ popupMenu: function() { -+ this._removeMenuTimeout(); -+ this.actor.fake_release(); -+ this._draggable.fakeRelease(); + -+ if (!this._menu) { -+ this._menu = new myAppIconMenu(this, this._settings); -+ this._menu.connect('activate-window', Lang.bind(this, function (menu, window) { -+ this.activateWindow(window); -+ })); -+ this._menu.connect('open-state-changed', Lang.bind(this, function (menu, isPoppedUp) { -+ if (!isPoppedUp) -+ this._onMenuPoppedDown(); -+ })); -+ let id = Main.overview.connect('hiding', Lang.bind(this, function () { this._menu.close(); })); -+ this._menu.actor.connect('destroy', function() { -+ Main.overview.disconnect(id); -+ }); ++#dashtodockContainer.running-dots .app-well-app.focused .overview-icon, ++#dashtodockContainer.dashtodock .app-well-app.focused .overview-icon { ++ background-color: rgba(238, 238, 236, 0.1); ++} + -+ this._menuManager.addMenu(this._menu); -+ } ++#dashtodockContainer.dashtodock #dash { ++ background: #2e3436; ++} + -+ this.emit('menu-state-changed', true); ++#dashtodockContainer .number-overlay { ++ color: rgba(255,255,255,1); ++ background-color: rgba(0,0,0,0.8); ++ text-align: center; ++} + -+ this.actor.set_hover(true); -+ this._menu.popup(); -+ this._menuManager.ignoreRelease(); -+ this.emit('sync-tooltip'); ++#dashtodockPreviewSeparator.popup-separator-menu-item-horizontal { ++ width: 1px; ++ height: auto; ++ border-right-width: 1px; ++ margin: 32px 0px; ++} +diff --git a/extensions/dash-to-dock/theming.js b/extensions/dash-to-dock/theming.js +new file mode 100644 +index 0000000..0a306b4 +--- /dev/null ++++ b/extensions/dash-to-dock/theming.js +@@ -0,0 +1,293 @@ ++// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + -+ return false; -+ }, ++const Clutter = imports.gi.Clutter; ++const Gio = imports.gi.Gio; ++const GLib = imports.gi.GLib; ++const Gtk = imports.gi.Gtk; ++const Signals = imports.signals; ++const Lang = imports.lang; ++const Meta = imports.gi.Meta; ++const Shell = imports.gi.Shell; ++const St = imports.gi.St; ++const Mainloop = imports.mainloop; + -+ _onFocusAppChanged: function() { -+ if(tracker.focus_app == this.app) -+ this.actor.add_style_class_name('focused'); -+ else -+ this.actor.remove_style_class_name('focused'); -+ }, ++const AppDisplay = imports.ui.appDisplay; ++const AppFavorites = imports.ui.appFavorites; ++const Dash = imports.ui.dash; ++const DND = imports.ui.dnd; ++const IconGrid = imports.ui.iconGrid; ++const Main = imports.ui.main; ++const PopupMenu = imports.ui.popupMenu; ++const Tweener = imports.ui.tweener; ++const Util = imports.misc.util; ++const Workspace = imports.ui.workspace; + -+ activate: function(button) { ++const Me = imports.misc.extensionUtils.getCurrentExtension(); ++const Utils = Me.imports.utils; + -+ if ( !this._settings.get_boolean('customize-click') ){ -+ this.parent(button); -+ return; -+ } ++/** ++ * Manage theme customization and custom theme support ++ */ ++const ThemeManager = new Lang.Class({ ++ Name: 'DashToDock.ThemeManager', + -+ let event = Clutter.get_current_event(); -+ let modifiers = event ? event.get_state() : 0; -+ let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK && -+ this.app.state == Shell.AppState.RUNNING || -+ button && button == 2; -+ let focusedApp = tracker.focus_app; ++ _init: function(settings, actor, dash) { ++ this._settings = settings; ++ this._signalsHandler = new Utils.GlobalSignalsHandler(); ++ this._bindSettingsChanges(); ++ this._actor = actor; ++ this._dash = dash; + -+ if (this.app.state == Shell.AppState.STOPPED || openNewWindow) -+ this.animateLaunch(); ++ // initialize colors with generic values ++ this._customizedBackground = {red: 0, green: 0, blue: 0, alpha: 0}; ++ this._customizedBorder = {red: 0, green: 0, blue: 0, alpha: 0}; + -+ if(button && button == 1 && this.app.state == Shell.AppState.RUNNING) { ++ this._signalsHandler.add([ ++ // When theme changes re-obtain default background color ++ St.ThemeContext.get_for_stage (global.stage), ++ 'changed', ++ Lang.bind(this, this.updateCustomTheme) ++ ], [ ++ // update :overview pseudoclass ++ Main.overview, ++ 'showing', ++ Lang.bind(this, this._onOverviewShowing) ++ ], [ ++ Main.overview, ++ 'hiding', ++ Lang.bind(this, this._onOverviewHiding) ++ ]); + -+ if(modifiers & Clutter.ModifierType.CONTROL_MASK){ -+ // Keep default behaviour: launch new window -+ // By calling the parent method I make it compatible -+ // with other extensions tweaking ctrl + click -+ this.parent(button); -+ return; ++ this._updateCustomStyleClasses(); + -+ } else if (this._settings.get_boolean('minimize-shift') && modifiers & Clutter.ModifierType.SHIFT_MASK){ -+ // On double click, minimize all windows in the current workspace -+ minimizeWindow(this.app, event.get_click_count() > 1); ++ // destroy themeManager when the managed actor is destroyed (e.g. extension unload) ++ // in order to disconnect signals ++ this._actor.connect('destroy', Lang.bind(this, this.destroy)); + -+ } else if(this.app == focusedApp && !Main.overview._shown){ ++ }, + -+ if(this._settings.get_enum('click-action') == clickAction.CYCLE_WINDOWS) -+ cycleThroughWindows(this.app); -+ else if(this._settings.get_enum('click-action') == clickAction.MINIMIZE) -+ minimizeWindow(this.app, true); -+ else if(this._settings.get_enum('click-action') == clickAction.LAUNCH) -+ this.app.open_new_window(-1); ++ destroy: function() { ++ this._signalsHandler.destroy(); ++ }, + -+ } else { -+ // Activate all window of the app or only le last used -+ if (this._settings.get_enum('click-action') == clickAction.CYCLE_WINDOWS && !Main.overview._shown){ -+ // If click cycles through windows I can activate one windows at a time -+ let windows = getAppInterestingWindows(this.app); -+ let w = windows[0]; -+ Main.activateWindow(w); -+ } else if(this._settings.get_enum('click-action') == clickAction.LAUNCH) -+ this.app.open_new_window(-1); -+ else if(this._settings.get_enum('click-action') == clickAction.MINIMIZE){ -+ // If click minimizes all, then one expects all windows to be reshown -+ activateAllWindows(this.app); -+ } else -+ this.app.activate(); -+ } -+ } else { -+ // Default behaviour -+ if (openNewWindow) -+ this.app.open_new_window(-1); -+ else -+ this.app.activate(); -+ } ++ _onOverviewShowing: function() { ++ this._actor.add_style_pseudo_class('overview'); ++ }, + -+ Main.overview.hide(); ++ _onOverviewHiding: function() { ++ this._actor.remove_style_pseudo_class('overview'); + }, + -+ _updateCounterClass: function() { ++ _updateDashOpacity: function() { ++ let newAlpha = this._settings.get_double('background-opacity'); + -+ let n = getAppInterestingWindows(this.app).length; ++ let [backgroundColor, borderColor] = this._getDefaultColors(); + -+ if(n>this._maxN) -+ n = this._maxN; ++ if (backgroundColor==null) ++ return; + -+ for(let i = 1; i<=this._maxN; i++){ -+ let className = 'running'+i; -+ if(i!=n) -+ this.actor.remove_style_class_name(className); -+ else -+ this.actor.add_style_class_name(className); -+ } -+ } -+}); ++ // Get the background and border alphas. We check the background alpha ++ // for a minimum of .001 to prevent division by 0 errors ++ let backgroundAlpha = Math.max(Math.round(backgroundColor.alpha/2.55)/100, .001); ++ let borderAlpha = Math.round(borderColor.alpha/2.55)/100; + -+function minimizeWindow(app, param){ -+ // Param true make all app windows minimize -+ let windows = getAppInterestingWindows(app); -+ let current_workspace = global.screen.get_active_workspace(); -+ for (let i = 0; i < windows.length; i++) { -+ let w = windows[i]; -+ if (w.get_workspace() == current_workspace && w.showing_on_its_workspace()){ -+ w.minimize(); -+ // Just minimize one window. By specification it should be the -+ // focused window on the current workspace. -+ if(!param) -+ break; -+ } -+ } -+} ++ // The border and background alphas should remain in sync ++ // We also limit the borderAlpha to a maximum of 1 (full opacity) ++ borderAlpha = Math.min((borderAlpha/backgroundAlpha)*newAlpha, 1); + -+/* -+ * By default only non minimized windows are activated. -+ * This activates all windows in the current workspace. -+ */ -+function activateAllWindows(app){ ++ this._customizedBackground = 'rgba(' + ++ backgroundColor.red + ',' + ++ backgroundColor.green + ',' + ++ backgroundColor.blue + ',' + ++ newAlpha + ')'; + -+ // First activate first window so workspace is switched if needed. -+ app.activate(); ++ this._customizedBorder = 'rgba(' + ++ borderColor.red + ',' + ++ borderColor.green + ',' + ++ borderColor.blue + ',' + ++ borderAlpha + ')'; + -+ // then activate all other app windows in the current workspace -+ let windows = getAppInterestingWindows(app); -+ let activeWorkspace = global.screen.get_active_workspace_index(); ++ }, + -+ if( windows.length<=0) -+ return; ++ _getDefaultColors: function() { ++ // Prevent shell crash if the actor is not on the stage. ++ // It happens enabling/disabling repeatedly the extension ++ if (!this._dash._container.get_stage()) ++ return [null, null]; + -+ let activatedWindows = 0; ++ // Remove custom style ++ let oldStyle = this._dash._container.get_style(); ++ this._dash._container.set_style(null); + -+ for (let i=windows.length-1; i>=0; i--){ -+ if(windows[i].get_workspace().index() == activeWorkspace){ -+ Main.activateWindow(windows[i]); -+ activatedWindows++; -+ } -+ } -+} ++ let themeNode = this._dash._container.get_theme_node(); ++ this._dash._container.set_style(oldStyle); + -+function cycleThroughWindows(app) { ++ let backgroundColor = themeNode.get_background_color(); + -+ // Store for a little amount of time last clicked app and its windows -+ // since the order changes upon window interaction -+ let MEMORY_TIME=3000; ++ // Just in case the theme has different border colors .. ++ // We want to find the inside border-color of the dock because it is ++ // the side most visible to the user. We do this by finding the side ++ // opposite the position ++ let position = Utils.getPosition(this._settings); ++ let side = position + 2; ++ if (side > 3) ++ side = Math.abs(side - 4); + -+ let app_windows = getAppInterestingWindows(app); ++ let borderColor = themeNode.get_border_color(side); + -+ if(recentlyClickedAppLoopId>0) -+ Mainloop.source_remove(recentlyClickedAppLoopId); -+ recentlyClickedAppLoopId = Mainloop.timeout_add(MEMORY_TIME, resetRecentlyClickedApp); ++ return [backgroundColor, borderColor]; ++ }, + -+ // If there isn't already a list of windows for the current app, -+ // or the stored list is outdated, use the current windows list. -+ if( !recentlyClickedApp || -+ recentlyClickedApp.get_id() != app.get_id() || -+ recentlyClickedAppWindows.length != app_windows.length -+ ){ ++ _updateDashColor: function() { ++ if (this._settings.get_boolean('custom-background-color')) { ++ let [backgroundColor, borderColor] = this._getDefaultColors(); + -+ recentlyClickedApp = app; -+ recentlyClickedAppWindows = app_windows; -+ recentlyClickedAppIndex = 0; -+ } ++ if (backgroundColor==null) ++ return; + -+ recentlyClickedAppIndex++; -+ let index = recentlyClickedAppIndex % recentlyClickedAppWindows.length; -+ let window = recentlyClickedAppWindows[index]; ++ let newAlpha = Math.round(backgroundColor.alpha/2.55)/100; ++ if (this._settings.get_boolean('opaque-background')) ++ newAlpha = this._settings.get_double('background-opacity'); + -+ Main.activateWindow(window); -+} ++ let newColor = Clutter.color_from_string(this._settings.get_string('background-color'))[1]; ++ this._customizedBackground = 'rgba(' + ++ newColor.red + ',' + ++ newColor.green + ',' + ++ newColor.blue + ',' + ++ newAlpha + ')'; + -+function resetRecentlyClickedApp() { ++ this._customizedBorder = this._customizedBackground; ++ } ++ }, + -+ if(recentlyClickedAppLoopId>0) -+ Mainloop.source_remove(recentlyClickedAppLoopId); -+ recentlyClickedAppLoopId=0; -+ recentlyClickedApp =null; -+ recentlyClickedAppWindows = null; -+ recentlyClickedAppIndex = 0; ++ _updateCustomStyleClasses: function() { ++ if (this._settings.get_boolean('apply-custom-theme')) ++ this._actor.add_style_class_name('dashtodock'); ++ else ++ this._actor.remove_style_class_name('dashtodock'); + -+ return false; -+} ++ if (this._settings.get_boolean('custom-theme-shrink')) ++ this._actor.add_style_class_name('shrink'); ++ else ++ this._actor.remove_style_class_name('shrink'); + -+function getAppInterestingWindows(app) { -+ // Filter out unnecessary windows, for instance -+ // nautilus desktop window. -+ let windows = app.get_windows().filter(function(w) { -+ return !w.skip_taskbar; -+ }); ++ if (this._settings.get_boolean('custom-theme-running-dots')) ++ this._actor.add_style_class_name('running-dots'); ++ else ++ this._actor.remove_style_class_name('running-dots'); + -+ return windows; -+} ++ // If not the built-in theme option is not selected ++ if (!this._settings.get_boolean('apply-custom-theme')) { ++ if (this._settings.get_boolean('force-straight-corner')) ++ this._actor.add_style_class_name('straight-corner'); ++ else ++ this._actor.remove_style_class_name('straight-corner'); ++ } else { ++ this._actor.remove_style_class_name('straight-corner'); ++ } ++ }, + ++ updateCustomTheme: function() { ++ this._updateCustomStyleClasses(); ++ this._updateDashOpacity(); ++ this._updateDashColor(); ++ this._adjustTheme(); ++ this._dash._redisplay(); ++ }, + -+/* -+ * This is a copy of the same function in utils.js, but also adjust horizontal scrolling -+ * and perform few further cheks on the current value to avoid changing the values when -+ * it would be clamp to the current one in any case. -+ * Return the amount of shift applied -+*/ -+function ensureActorVisibleInScrollView(scrollView, actor) { ++ /** ++ * Reimported back and adapted from atomdock ++ */ ++ _adjustTheme: function() { ++ // Prevent shell crash if the actor is not on the stage. ++ // It happens enabling/disabling repeatedly the extension ++ if (!this._dash._container.get_stage()) ++ return; + -+ let adjust_v = true; -+ let adjust_h = true; ++ // Remove prior style edits ++ this._dash._container.set_style(null); + -+ let vadjustment = scrollView.vscroll.adjustment; -+ let hadjustment = scrollView.hscroll.adjustment; -+ let [vvalue, vlower, vupper, vstepIncrement, vpageIncrement, vpageSize] = vadjustment.get_values(); -+ let [hvalue, hlower, hupper, hstepIncrement, hpageIncrement, hpageSize] = hadjustment.get_values(); ++ // If built-in theme is enabled do nothing else ++ if (this._settings.get_boolean('apply-custom-theme')) ++ return; + -+ let [hvalue0, vvalue0] = [hvalue, vvalue]; ++ let newStyle = ''; ++ let position = Utils.getPosition(this._settings); + -+ let voffset = 0; -+ let hoffset = 0; -+ let fade = scrollView.get_effect("fade"); -+ if (fade){ -+ voffset = fade.vfade_offset; -+ hoffset = fade.hfade_offset; -+ } ++ if (!this._settings.get_boolean('custom-theme-shrink')) { ++ // obtain theme border settings ++ let themeNode = this._dash._container.get_theme_node(); ++ let borderColor = themeNode.get_border_color(St.Side.TOP); ++ let borderWidth = themeNode.get_border_width(St.Side.TOP); ++ let borderRadius = themeNode.get_border_radius(St.Corner.TOPRIGHT); + -+ let box = actor.get_allocation_box(); -+ let y1 = box.y1, y2 = box.y2, x1 = box.x1, x2 = box.x2; ++ // We're copying border and corner styles to left border and top-left ++ // corner, also removing bottom border and bottom-right corner styles ++ let borderInner = ''; ++ let borderRadiusValue = ''; ++ let borderMissingStyle = ''; + -+ let parent = actor.get_parent(); -+ while (parent != scrollView) { -+ if (!parent) -+ throw new Error("actor not in scroll view"); ++ if (this._rtl && (position != St.Side.RIGHT)) ++ borderMissingStyle = 'border-right: ' + borderWidth + 'px solid ' + ++ borderColor.to_string() + ';'; ++ else if (!this._rtl && (position != St.Side.LEFT)) ++ borderMissingStyle = 'border-left: ' + borderWidth + 'px solid ' + ++ borderColor.to_string() + ';'; + -+ let box = parent.get_allocation_box(); -+ y1 += box.y1; -+ y2 += box.y1; -+ x1 += box.x1; -+ x2 += box.x1; -+ parent = parent.get_parent(); -+ } ++ switch (position) { ++ case St.Side.LEFT: ++ borderInner = 'border-left'; ++ borderRadiusValue = '0 ' + borderRadius + 'px ' + borderRadius + 'px 0;'; ++ break; ++ case St.Side.RIGHT: ++ borderInner = 'border-right'; ++ borderRadiusValue = borderRadius + 'px 0 0 ' + borderRadius + 'px;'; ++ break; ++ case St.Side.TOP: ++ borderInner = 'border-top'; ++ borderRadiusValue = '0 0 ' + borderRadius + 'px ' + borderRadius + 'px;'; ++ break; ++ case St.Side.BOTTOM: ++ borderInner = 'border-bottom'; ++ borderRadiusValue = borderRadius + 'px ' + borderRadius + 'px 0 0;'; ++ break; ++ } + -+ if (y1 < vvalue + voffset) -+ vvalue = Math.max(0, y1 - voffset); -+ else if (vvalue < vupper - vpageSize && y2 > vvalue + vpageSize - voffset) -+ vvalue = Math.min(vupper -vpageSize, y2 + voffset - vpageSize); ++ newStyle = borderInner + ': none;' + ++ 'border-radius: ' + borderRadiusValue + ++ borderMissingStyle; + -+ if (x1 < hvalue + hoffset) -+ hvalue = Math.max(0, x1 - hoffset); -+ else if (hvalue < hupper - hpageSize && x2 > hvalue + hpageSize - hoffset) -+ hvalue = Math.min(hupper - hpageSize, x2 + hoffset - hpageSize); ++ // I do call set_style possibly twice so that only the background gets the transition. ++ // The transition-property css rules seems to be unsupported ++ this._dash._container.set_style(newStyle); ++ } + -+ if (vvalue !== vvalue0) { -+ Tweener.addTween(vadjustment, -+ { value: vvalue, -+ time: Util.SCROLL_TIME, -+ transition: 'easeOutQuad' }); -+ } ++ // Customize background ++ if (this._settings.get_boolean('opaque-background') || this._settings.get_boolean('custom-background-color')) { ++ newStyle = newStyle + 'background-color:'+ this._customizedBackground + '; ' + ++ 'border-color:'+ this._customizedBorder + '; ' + ++ 'transition-delay: 0s; transition-duration: 0.250s;'; ++ this._dash._container.set_style(newStyle); ++ } ++ }, + -+ if (hvalue !== hvalue0) { -+ Tweener.addTween(hadjustment, -+ { value: hvalue, -+ time: Util.SCROLL_TIME, -+ transition: 'easeOutQuad' }); ++ _bindSettingsChanges: function() { ++ let keys = ['opaque-background', ++ 'background-opacity', ++ 'custom-background-color', ++ 'background-color', ++ 'apply-custom-theme', ++ 'custom-theme-shrink', ++ 'custom-theme-running-dots', ++ 'extend-height', ++ 'force-straight-corner']; ++ ++ keys.forEach(function(key) { ++ this._signalsHandler.add([ ++ this._settings, ++ 'changed::' + key, ++ Lang.bind(this, this.updateCustomTheme) ++ ]); ++ }, this); + } -+ -+ return [hvalue- hvalue0, vvalue - vvalue0]; -+} -diff --git a/extensions/dash-to-dock/org.gnome.shell.extensions.dash-to-dock.gschema.xml.in b/extensions/dash-to-dock/org.gnome.shell.extensions.dash-to-dock.gschema.xml.in ++}); +diff --git a/extensions/dash-to-dock/utils.js b/extensions/dash-to-dock/utils.js new file mode 100644 -index 0000000..e59f35a +index 0000000..b98fe45 --- /dev/null -+++ b/extensions/dash-to-dock/org.gnome.shell.extensions.dash-to-dock.gschema.xml.in -@@ -0,0 +1,159 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ 'LEFT' -+ Dock position -+ Dock is shown on the Left, Right, Top or Bottom side of the screen. -+ -+ -+ 0.25 -+ Animation time -+ Sets the time duration of the autohide effect. -+ -+ -+ 0.25 -+ Show delay -+ Sets the delay after the mouse reaches the screen border before showing the dock. -+ -+ -+ 0.20 -+ Show delay -+ Sets the delay after the mouse left the dock before hiding it. -+ -+ -+ false -+ Dash background is opaque -+ Makes the background of the dash opaque improving readability when in autohide mode. -+ -+ -+ 0.8 -+ Opacity of the dash background -+ Sets the opacity of the dash background when in autohide mode. -+ -+ -+ true -+ Dock dodges windows -+ Enable or disable intellihide mode -+ -+ -+ true -+ Dock dodges only same app windows -+ -+ -+ -+ true -+ Dock shown on mouse over -+ Enable or disable autohide mode -+ -+ -+ true -+ Require pressure to show dash -+ Enable or disable requiring pressure to show the dash -+ -+ -+ 100 -+ Pressure threshold -+ Sets how much pressure is needed to show the dash. -+ -+ -+ false -+ Dock always visible -+ Dock is always visible -+ -+ -+ true -+ Switch workspace by scrolling over the dock -+ Add the possibility to switch workspace by mouse scrolling over the dock. -+ -+ -+ 48 -+ Maximum dash icon size -+ Set the allowed maximum dash icon size. Allowed range: 16..64. -+ -+ -+ false -+ Fixed icon size -+ Keep the icon size fived by scrolling the dock. -+ -+ -+ false -+ Apply custom theme -+ Apply customization to the dash appearance -+ -+ -+ false -+ TODO -+ TODO -+ -+ -+ false -+ TODO -+ TODO -+ -+ -+ true -+ Show running apps -+ Show or hide running appplications icons in the dash -+ -+ -+ false -+ Show application button at top -+ Show appplication button at top of the dash -+ -+ -+ true -+ Basic compatibility with bolt extensions -+ Make the extension work properly when bolt extensions is enabled -+ -+ -+ 0.95 -+ Dock max height (fraction of available space) -+ -+ -+ false -+ Extend the dock container to all the available height -+ -+ -+ -1 -+ Monitor on which putting the dock -+ Set on which monitor to put the dock, use -1 for the primary one -+ -+ -+ true -+ Customize click behaviour -+ Customize action on various mouse events -+ -+ -+ true -+ Minimize on shift+click -+ -+ -+ true -+ Activate only one window -+ -+ -+ 'cycle-windows' -+ Action when clicking on a running app -+ Set the action that is executed when clicking on the icon of a running application -+ -+ -+ false -+ Make message tray not show on mouse-over. -+ -+ -+ -diff --git a/extensions/dash-to-dock/prefs.js b/extensions/dash-to-dock/prefs.js ++++ b/extensions/dash-to-dock/utils.js +@@ -0,0 +1,123 @@ ++const Clutter = imports.gi.Clutter; ++const Lang = imports.lang; ++const St = imports.gi.St; ++ ++/** ++ * Simplify global signals and function injections handling ++ * abstract class ++ */ ++const BasicHandler = new Lang.Class({ ++ Name: 'DashToDock.BasicHandler', ++ ++ _init: function() { ++ this._storage = new Object(); ++ }, ++ ++ add: function(/* unlimited 3-long array arguments */) { ++ // Convert arguments object to array, concatenate with generic ++ let args = Array.concat('generic', Array.slice(arguments)); ++ // Call addWithLabel with ags as if they were passed arguments ++ this.addWithLabel.apply(this, args); ++ }, ++ ++ destroy: function() { ++ for( let label in this._storage ) ++ this.removeWithLabel(label); ++ }, ++ ++ addWithLabel: function(label /* plus unlimited 3-long array arguments*/) { ++ if (this._storage[label] == undefined) ++ this._storage[label] = new Array(); ++ ++ // Skip first element of the arguments ++ for (let i = 1; i < arguments.length; i++) { ++ this._storage[label].push( this._create(arguments[i])); ++ } ++ }, ++ ++ removeWithLabel: function(label) { ++ if (this._storage[label]) { ++ for (let i = 0; i < this._storage[label].length; i++) ++ this._remove(this._storage[label][i]); ++ ++ delete this._storage[label]; ++ } ++ }, ++ ++ // Virtual methods to be implemented by subclass ++ ++ /** ++ * Create single element to be stored in the storage structure ++ */ ++ _create: function(item) { ++ throw new Error('no implementation of _create in ' + this); ++ }, ++ ++ /** ++ * Correctly delete single element ++ */ ++ _remove: function(item) { ++ throw new Error('no implementation of _remove in ' + this); ++ } ++}); ++ ++/** ++ * Manage global signals ++ */ ++const GlobalSignalsHandler = new Lang.Class({ ++ Name: 'DashToDock.GlobalSignalHandler', ++ Extends: BasicHandler, ++ ++ _create: function(item) { ++ let object = item[0]; ++ let event = item[1]; ++ let callback = item[2] ++ let id = object.connect(event, callback); ++ ++ return [object, id]; ++ }, ++ ++ _remove: function(item) { ++ item[0].disconnect(item[1]); ++ } ++}); ++ ++/** ++ * Manage function injection: both instances and prototype can be overridden ++ * and restored ++ */ ++const InjectionsHandler = new Lang.Class({ ++ Name: 'DashToDock.InjectionsHandler', ++ Extends: BasicHandler, ++ ++ _create: function(item) { ++ let object = item[0]; ++ let name = item[1]; ++ let injectedFunction = item[2]; ++ let original = object[name]; ++ ++ object[name] = injectedFunction; ++ return [object, name, injectedFunction, original]; ++ }, ++ ++ _remove: function(item) { ++ let object = item[0]; ++ let name = item[1]; ++ let original = item[3]; ++ object[name] = original; ++ } ++}); ++ ++/** ++ * Return the actual position reverseing left and right in rtl ++ */ ++function getPosition(settings) { ++ let position = settings.get_enum('dock-position'); ++ if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) { ++ if (position == St.Side.LEFT) ++ position = St.Side.RIGHT; ++ else if (position == St.Side.RIGHT) ++ position = St.Side.LEFT; ++ } ++ return position; ++} +diff --git a/extensions/dash-to-dock/windowPreview.js b/extensions/dash-to-dock/windowPreview.js new file mode 100644 -index 0000000..4afecec +index 0000000..4f84b4d --- /dev/null -+++ b/extensions/dash-to-dock/prefs.js -@@ -0,0 +1,367 @@ -+// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- -+ -+const Gio = imports.gi.Gio; ++++ b/extensions/dash-to-dock/windowPreview.js +@@ -0,0 +1,595 @@ ++/* ++ * Credits: ++ * This file is based on code from the Dash to Panel extension by Jason DeRose ++ * and code from the Taskbar extension by Zorin OS ++ * Some code was also adapted from the upstream Gnome Shell source code. ++ */ ++const Clutter = imports.gi.Clutter; +const GLib = imports.gi.GLib; -+const GObject = imports.gi.GObject; -+const Gtk = imports.gi.Gtk; -+const Gdk = imports.gi.Gdk; +const Lang = imports.lang; ++const St = imports.gi.St; +const Mainloop = imports.mainloop; ++const Main = imports.ui.main; ++const Gtk = imports.gi.Gtk; + ++const Params = imports.misc.params; ++const PopupMenu = imports.ui.popupMenu; ++const Tweener = imports.ui.tweener; ++const Workspace = imports.ui.workspace; + -+const Gettext = imports.gettext.domain('dashtodock'); -+const _ = Gettext.gettext; -+const N_ = function(e) { return e }; ++const Me = imports.misc.extensionUtils.getCurrentExtension(); ++const Utils = Me.imports.utils; + -+const ExtensionUtils = imports.misc.extensionUtils; -+const Me = ExtensionUtils.getCurrentExtension(); -+const Convenience = Me.imports.convenience; ++const PREVIEW_MAX_WIDTH = 250; ++const PREVIEW_MAX_HEIGHT = 150; + -+const SCALE_UPDATE_TIMEOUT = 500; -+const DEFAULT_ICONS_SIZES = [ 128, 96, 64, 48, 32, 24, 16 ]; ++const WindowPreviewMenu = new Lang.Class({ ++ Name: 'WindowPreviewMenu', ++ Extends: PopupMenu.PopupMenu, + -+const Settings = new Lang.Class({ -+ Name: 'DashToDockSettings', ++ _init: function(source, settings) { ++ this._dtdSettings = settings; + ++ let side = Utils.getPosition(settings); + -+ _init: function() { ++ this.parent(source.actor, 0.5, side); + -+ this._settings = Convenience.getSettings('org.gnome.shell.extensions.dash-to-dock'); ++ // We want to keep the item hovered while the menu is up ++ this.blockSourceEvents = true; + -+ this._rtl = Gtk.Widget.get_default_direction()==Gtk.TextDirection.RTL; ++ this._source = source; ++ this._app = this._source.app; ++ let monitorIndex = this._source.monitorIndex; + -+ this._builder = new Gtk.Builder(); -+ this._builder.set_translation_domain(Me.metadata['gettext-domain']); -+ this._builder.add_from_file(Me.path + '/Settings.ui'); ++ this.actor.add_style_class_name('app-well-menu'); ++ this.actor.set_style('max-width: ' + (Main.layoutManager.monitors[monitorIndex].width - 22) + 'px; ' + ++ 'max-height: ' + (Main.layoutManager.monitors[monitorIndex].height - 22) + 'px;'); ++ this.actor.hide(); + -+ this.widget = this._builder.get_object('settings_notebook'); ++ // Chain our visibility and lifecycle to that of the source ++ this._mappedId = this._source.actor.connect('notify::mapped', Lang.bind(this, function () { ++ if (!this._source.actor.mapped) ++ this.close(); ++ })); ++ this._destroyId = this._source.actor.connect('destroy', Lang.bind(this, this.destroy)); + -+ // Timeout to delay the update of the settings -+ this._dock_size_timeout = 0; -+ this._icon_size_timeout = 0; -+ this._opacity_timeout = 0; ++ Main.uiGroup.add_actor(this.actor); + -+ this._bindSettings(); ++ // Change the initialized side where required. ++ this._arrowSide = side; ++ this._boxPointer._arrowSide = side; ++ this._boxPointer._userArrowSide = side; + -+ this._builder.connect_signals_full(Lang.bind(this, this._connector)); ++ this._previewBox = new WindowPreviewList(this._source, this._dtdSettings); ++ this.addMenuItem(this._previewBox); ++ }, + ++ _redisplay: function() { ++ this._previewBox._shownInitially = false; ++ this._previewBox._redisplay(); + }, + -+ // Connect signals -+ _connector: function(builder, object, signal, handler) { -+ object.connect(signal, Lang.bind(this, this._SignalHandler[handler])); ++ popup: function() { ++ let windows = this._source.getInterestingWindows(); ++ if (windows.length > 0) { ++ this._redisplay(); ++ this.open(); ++ this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); ++ this._source.emit('sync-tooltip'); ++ } + }, + -+ _bindSettings: function() { ++ destroy: function () { ++ if (this._mappedId) ++ this._source.actor.disconnect(this._mappedId); + -+ /* Position and size panel */ ++ if (this._destroyId) ++ this._source.actor.disconnect(this._destroyId); + -+ // Monitor options ++ this.parent(); ++ } + -+ this._monitors = []; -+ // Build options based on the number of monitors and the current settings. -+ let n_monitors = Gdk.Screen.get_default().get_n_monitors(); -+ let primary_monitor = Gdk.Screen.get_default().get_primary_monitor(); ++}); + -+ let monitor = this._settings.get_int('preferred-monitor'); ++const WindowPreviewList = new Lang.Class({ ++ Name: 'WindowPreviewMenuSection', ++ Extends: PopupMenu.PopupMenuSection, + -+ // Add primary monitor with index 0, because in GNOME Shell the primary monitor is always 0 -+ this._builder.get_object('dock_monitor_combo').append_text(_("Primary monitor")); -+ this._monitors.push(0); ++ _init: function(source, settings) { ++ this._dtdSettings = settings; + -+ // Add connected monitors -+ let ctr = 0; -+ for (let i = 0; i < n_monitors; i++) { -+ if (i !== primary_monitor){ -+ ctr++; -+ this._monitors.push(ctr); -+ this._builder.get_object('dock_monitor_combo').append_text(_("Secondary monitor ") + ctr); -+ } -+ } ++ this.parent(); + -+ // If one of the external monitor is set as preferred, show it even if not attached -+ if ( monitor >= n_monitors && monitor !== primary_monitor) { -+ this._monitors.push(monitor) -+ this._builder.get_object('dock_monitor_combo').append_text(_("Secondary monitor ") + ++ctr); -+ } ++ this.actor = new St.ScrollView({ name: 'dashtodockWindowScrollview', ++ hscrollbar_policy: Gtk.PolicyType.AUTOMATIC, ++ vscrollbar_policy: Gtk.PolicyType.AUTOMATIC, ++ enable_mouse_scrolling: true }); + -+ this._builder.get_object('dock_monitor_combo').set_active(this._monitors.indexOf(monitor)); ++ this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent )); + -+ // Position option -+ let position = this._settings.get_enum('dock-position'); ++ let position = Utils.getPosition(this._dtdSettings); ++ this.isHorizontal = position == St.Side.BOTTOM || position == St.Side.TOP; ++ this.box.set_vertical(!this.isHorizontal); ++ this.box.set_name('dashtodockWindowList'); ++ this.actor.add_actor(this.box); ++ this.actor._delegate = this; + -+ switch (position) { -+ case 0: -+ this._builder.get_object('position_top_button').set_active(true); -+ break; -+ case 1: -+ this._builder.get_object('position_right_button').set_active(true); -+ break; -+ case 2: -+ this._builder.get_object('position_bottom_button').set_active(true); -+ break; -+ case 3: -+ this._builder.get_object('position_left_button').set_active(true); -+ break; -+ } ++ this._shownInitially = false; + -+ if (this._rtl) { -+ /* Left is Right in rtl as a setting */ -+ this._builder.get_object('position_left_button').set_label(_("Right")); -+ this._builder.get_object('position_right_button').set_label(_("Left")); -+ } ++ this._source = source; ++ this.app = source.app; + -+ // Intelligent autohide options -+ this._settings.bind('dock-fixed', -+ this._builder.get_object('intelligent_autohide_switch'), -+ 'active', -+ Gio.SettingsBindFlags.INVERT_BOOLEAN); -+ this._settings.bind('dock-fixed', -+ this._builder.get_object('intelligent_autohide_button'), -+ 'sensitive', -+ Gio.SettingsBindFlags.INVERT_BOOLEAN); -+ this._settings.bind('insensitive-message-tray', -+ this._builder.get_object('insensitive_messagetray_checkbutton'), -+ 'active', -+ Gio.SettingsBindFlags.DEFAULT); -+ this._settings.bind('autohide', -+ this._builder.get_object('autohide_switch'), -+ 'active', -+ Gio.SettingsBindFlags.DEFAULT); -+ this._settings.bind('require-pressure-to-show', -+ this._builder.get_object('require_pressure_checkbutton'), -+ 'active', -+ Gio.SettingsBindFlags.DEFAULT); -+ this._settings.bind('intellihide', -+ this._builder.get_object('intellihide_switch'), -+ 'active', -+ Gio.SettingsBindFlags.DEFAULT); -+ this._settings.bind('intellihide-perapp', -+ this._builder.get_object('per_app_intellihide_checkbutton'), -+ 'active', -+ Gio.SettingsBindFlags.DEFAULT); -+ this._settings.bind('animation-time', -+ this._builder.get_object('animation_duration_spinbutton'), -+ 'value', -+ Gio.SettingsBindFlags.DEFAULT); -+ this._settings.bind('hide-delay', -+ this._builder.get_object('hide_timeout_spinbutton'), -+ 'value', -+ Gio.SettingsBindFlags.DEFAULT); -+ this._settings.bind('show-delay', -+ this._builder.get_object('show_timeout_spinbutton'), -+ 'value', -+ Gio.SettingsBindFlags.DEFAULT); -+ this._settings.bind('pressure-threshold', -+ this._builder.get_object('pressure_threshold_spinbutton'), -+ 'value', -+ Gio.SettingsBindFlags.DEFAULT); ++ this._redisplayId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay)); + -+ //this._builder.get_object('animation_duration_spinbutton').set_value(this._settings.get_double('animation-time')); ++ this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); ++ this._stateChangedId = this.app.connect('windows-changed', ++ Lang.bind(this, ++ this._queueRedisplay)); ++ }, + -+ // Create dialog for intelligent autohide advanced settings -+ this._builder.get_object('intelligent_autohide_button').connect('clicked', Lang.bind(this, function() { ++ _queueRedisplay: function () { ++ Main.queueDeferredWork(this._redisplayId); ++ }, + -+ let dialog = new Gtk.Dialog({ title: _("Intelligent autohide customization"), -+ transient_for: this.widget.get_toplevel(), -+ use_header_bar: true, -+ modal: true }); ++ _onScrollEvent: function(actor, event) { ++ // Event coordinates are relative to the stage but can be transformed ++ // as the actor will only receive events within his bounds. ++ let stage_x, stage_y, ok, event_x, event_y, actor_w, actor_h; ++ [stage_x, stage_y] = event.get_coords(); ++ [ok, event_x, event_y] = actor.transform_stage_point(stage_x, stage_y); ++ [actor_w, actor_h] = actor.get_size(); ++ ++ // If the scroll event is within a 1px margin from ++ // the relevant edge of the actor, let the event propagate. ++ if (event_y >= actor_h - 2) ++ return Clutter.EVENT_PROPAGATE; + -+ // GTK+ leaves positive values for application-defined response ids. -+ // Use +1 for the reset action -+ dialog.add_button(_("Reset to defaults"), 1); ++ // Skip to avoid double events mouse ++ if (event.is_pointer_emulated()) ++ return Clutter.EVENT_STOP; + -+ let box = this._builder.get_object('intelligent_autohide_advanced_settings_box'); -+ dialog.get_content_area().add(box); ++ let adjustment, delta; + -+ this._settings.bind('intellihide', -+ this._builder.get_object('per_app_intellihide_checkbutton'), -+ 'sensitive', -+ Gio.SettingsBindFlags.GET); ++ if (this.isHorizontal) ++ adjustment = this.actor.get_hscroll_bar().get_adjustment(); ++ else ++ adjustment = this.actor.get_vscroll_bar().get_adjustment(); ++ ++ let increment = adjustment.step_increment; ++ ++ switch ( event.get_scroll_direction() ) { ++ case Clutter.ScrollDirection.UP: ++ delta = -increment; ++ break; ++ case Clutter.ScrollDirection.DOWN: ++ delta = +increment; ++ break; ++ case Clutter.ScrollDirection.SMOOTH: ++ let [dx, dy] = event.get_scroll_delta(); ++ delta = dy*increment; ++ delta += dx*increment; ++ break; + -+ this._settings.bind('autohide', -+ this._builder.get_object('require_pressure_checkbutton'), -+ 'sensitive', -+ Gio.SettingsBindFlags.GET); ++ } + -+ dialog.connect('response', Lang.bind(this, function(dialog, id) { -+ if (id == 1) { -+ // restore default settings for the relevant keys -+ let keys = ['intellihide', 'autohide', 'intellihide-perapp', 'require-pressure-to-show', -+ 'animation-time', 'show-delay', 'hide-delay']; -+ keys.forEach(function(val){ -+ this._settings.set_value(val, this._settings.get_default_value(val)); -+ }, this); -+ } else { -+ // remove the settings box so it doesn't get destroyed; -+ dialog.get_content_area().remove(box); -+ dialog.destroy(); -+ } -+ return; -+ })); ++ adjustment.set_value(adjustment.get_value() + delta); + -+ dialog.show_all(); ++ return Clutter.EVENT_STOP; ++ }, + -+ })); ++ _onDestroy: function() { ++ this.app.disconnect(this._stateChangedId); ++ this._stateChangedId = 0; ++ }, + -+ // size options -+ this._builder.get_object('dock_size_scale').set_value(this._settings.get_double('height-fraction')); -+ this._builder.get_object('dock_size_scale').add_mark(0.9, Gtk.PositionType.TOP, null); -+ let icon_size_scale = this._builder.get_object('icon_size_scale'); -+ icon_size_scale.set_range(DEFAULT_ICONS_SIZES[DEFAULT_ICONS_SIZES.length -1], DEFAULT_ICONS_SIZES[0]); -+ icon_size_scale.set_value(this._settings.get_int('dash-max-icon-size')); -+ DEFAULT_ICONS_SIZES.forEach(function(val){ -+ icon_size_scale.add_mark(val, Gtk.PositionType.TOP, val.toString()); -+ }); ++ _createPreviewItem: function(window) { ++ let preview = new WindowPreviewMenuItem(window); ++ return preview; ++ }, + -+ // Corrent for rtl languages -+ if (this._rtl) { -+ // Flip value position: this is not done automatically -+ this._builder.get_object('dock_size_scale').set_value_pos(Gtk.PositionType.LEFT); -+ icon_size_scale.set_value_pos(Gtk.PositionType.LEFT); -+ /* I suppose due to a bug, having a more than one mark and one above a value of 100 -+ * makes the rendering of the marks wrong in rtl. This doesn't happen setting the scale as not flippable -+ * and then manually inverting it -+ */ -+ icon_size_scale.set_flippable(false); -+ icon_size_scale.set_inverted(true); ++ _redisplay: function () { ++ // Remove separator ++ let nonWinItem = this._getMenuItems().filter(function(actor) { ++ return !actor._window; ++ }); ++ for (let i = 0; i < nonWinItem.length; i++) { ++ let item = nonWinItem[i]; ++ item.destroy(); + } + -+ this._settings.bind('icon-size-fixed', this._builder.get_object('icon_size_fixed_checkbutton'), 'active', Gio.SettingsBindFlags.DEFAULT); -+ this._settings.bind('extend-height', this._builder.get_object('dock_size_extend_checkbutton'), 'active', Gio.SettingsBindFlags.DEFAULT); -+ this._settings.bind('extend-height', this._builder.get_object('dock_size_scale'), 'sensitive', Gio.SettingsBindFlags.INVERT_BOOLEAN); ++ let children = this._getMenuItems().filter(function(actor) { ++ return actor._window; ++ }); + ++ // Windows currently on the menu ++ let oldWin = children.map(function(actor) { ++ return actor._window; ++ }); + -+ /* Behavior panel */ ++ // All app windows ++ let newWin = this._source.getInterestingWindows().sort(this.sortWindowsCompareFunction); + -+ this._settings.bind('show-running', -+ this._builder.get_object('show_running_switch'), -+ 'active', -+ Gio.SettingsBindFlags.DEFAULT); -+ this._settings.bind('show-apps-at-top', -+ this._builder.get_object('application_button_first_switch'), -+ 'active', -+ Gio.SettingsBindFlags.DEFAULT); ++ let addedItems = []; ++ let removedActors = []; + -+ this._builder.get_object('click_action_combo').set_active(this._settings.get_enum('click-action')); -+ this._builder.get_object('click_action_combo').connect('changed', Lang.bind (this, function(widget) { -+ this._settings.set_enum('click-action', widget.get_active()); -+ })); ++ let newIndex = 0; ++ let oldIndex = 0; + -+ this._builder.get_object('shift_click_action_combo').set_active(this._settings.get_boolean('minimize-shift')?0:1); ++ while (newIndex < newWin.length || oldIndex < oldWin.length) { ++ // No change at oldIndex/newIndex ++ if (oldWin[oldIndex] && ++ oldWin[oldIndex] == newWin[newIndex]) { ++ oldIndex++; ++ newIndex++; ++ continue; ++ } + -+ this._builder.get_object('shift_click_action_combo').connect('changed', Lang.bind (this, function(widget) { -+ this._settings.set_boolean('minimize-shift', widget.get_active()==1); -+ })); ++ // Window removed at oldIndex ++ if (oldWin[oldIndex] && ++ newWin.indexOf(oldWin[oldIndex]) == -1) { ++ removedActors.push(children[oldIndex]); ++ oldIndex++; ++ continue; ++ } + -+ this._settings.bind('scroll-switch-workspace', this._builder.get_object('switch_workspace_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); ++ // Window added at newIndex ++ if (newWin[newIndex] && ++ oldWin.indexOf(newWin[newIndex]) == -1) { ++ addedItems.push({ item: this._createPreviewItem(newWin[newIndex]), ++ pos: newIndex }); ++ newIndex++; ++ continue; ++ } + -+ /* Appearance Panel */ ++ // Window moved ++ let insertHere = newWin[newIndex + 1] && ++ newWin[newIndex + 1] == oldWin[oldIndex]; ++ let alreadyRemoved = removedActors.reduce(function(result, actor) { ++ let removedWin = actor._window; ++ return result || removedWin == newWin[newIndex]; ++ }, false); + -+ this._settings.bind('apply-custom-theme', this._builder.get_object('customize_theme'), 'sensitive', Gio.SettingsBindFlags.INVERT_BOOLEAN | Gio.SettingsBindFlags.GET); -+ this._settings.bind('apply-custom-theme', this._builder.get_object('builtin_theme_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); -+ this._settings.bind('custom-theme-shrink', this._builder.get_object('shrink_dash_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); -+ this._settings.bind('custom-theme-running-dots', this._builder.get_object('running_dots_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); -+ this._settings.bind('opaque-background', this._builder.get_object('customize_opacity_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); -+ this._builder.get_object('custom_opacity_scale').set_value(this._settings.get_double('background-opacity')); -+ this._settings.bind('opaque-background', this._builder.get_object('custom_opacity'), 'sensitive', Gio.SettingsBindFlags.DEFAULT); ++ if (insertHere || alreadyRemoved) { ++ addedItems.push({ item: this._createPreviewItem(newWin[newIndex]), ++ pos: newIndex + removedActors.length }); ++ newIndex++; ++ } else { ++ removedActors.push(children[oldIndex]); ++ oldIndex++; ++ } ++ } + -+ /* About Panel */ ++ for (let i = 0; i < addedItems.length; i++) ++ this.addMenuItem(addedItems[i].item, ++ addedItems[i].pos); + -+ this._builder.get_object('extension_version').set_label(Me.metadata.version.toString()); ++ for (let i = 0; i < removedActors.length; i++) { ++ let item = removedActors[i]; ++ if (this._shownInitially) ++ item._animateOutAndDestroy(); ++ else ++ item.actor.destroy(); ++ } + -+ }, ++ // Separate windows from other workspaces ++ let ws_index = global.screen.get_active_workspace_index(); ++ let separator_index = 0; ++ for (let i = 0; i < newWin.length; i++) ++ if (newWin[i].get_workspace().index() == ws_index) ++ separator_index++; ++ ++ if (separator_index > 0 && separator_index !== newWin.length) { ++ let separatorItem = new PopupMenu.PopupSeparatorMenuItem(); ++ if (this.isHorizontal) { ++ separatorItem._separator.set_x_expand(false); ++ separatorItem._separator.set_y_expand(true); ++ separatorItem._separator.set_name('dashtodockPreviewSeparator'); ++ separatorItem._separator.add_style_class_name('popup-separator-menu-item-horizontal'); ++ separatorItem._separator.set_x_align(Clutter.ActorAlign.CENTER); ++ separatorItem._separator.set_y_align(Clutter.ActorAlign.FILL); ++ } ++ this.addMenuItem(separatorItem, separator_index); ++ } + ++ // Skip animations on first run when adding the initial set ++ // of items, to avoid all items zooming in at once ++ let animate = this._shownInitially; + -+ // Object containing all signals defined in the glade file -+ _SignalHandler: { ++ if (!this._shownInitially) ++ this._shownInitially = true; + -+ dock_display_combo_changed_cb: function (combo) { -+ this._settings.set_int('preferred-monitor', this._monitors[combo.get_active()]); -+ }, -+ -+ position_top_button_toggled_cb: function (button){ -+ if (button.get_active()) -+ this._settings.set_enum('dock-position', 0); -+ }, -+ -+ position_right_button_toggled_cb: function (button) { -+ if (button.get_active()) -+ this._settings.set_enum('dock-position', 1); -+ }, -+ -+ position_bottom_button_toggled_cb: function (button) { -+ if (button.get_active()) -+ this._settings.set_enum('dock-position', 2); -+ }, -+ -+ position_left_button_toggled_cb: function (button) { -+ if (button.get_active()) -+ this._settings.set_enum('dock-position', 3); -+ }, -+ -+ icon_size_combo_changed_cb: function(combo) { -+ this._settings.set_int('dash-max-icon-size', this._allIconSizes[combo.get_active()]); -+ }, -+ -+ dock_size_scale_format_value_cb: function(scale, value) { -+ return Math.round(value*100)+ ' %'; -+ }, -+ -+ dock_size_scale_value_changed_cb: function(scale){ -+ // Avoid settings the size consinuosly -+ if (this._dock_size_timeout >0) -+ Mainloop.source_remove(this._dock_size_timeout); -+ -+ this._dock_size_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, Lang.bind(this, function(){ -+ this._settings.set_double('height-fraction', scale.get_value()); -+ this._dock_size_timeout = 0; -+ return GLib.SOURCE_REMOVE; -+ })); -+ }, ++ for (let i = 0; i < addedItems.length; i++) ++ addedItems[i].item.show(animate); + -+ icon_size_scale_format_value_cb: function(scale, value) { -+ return value+ ' px'; -+ }, ++ // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744 ++ // Without it, StBoxLayout may use a stale size cache ++ this.box.queue_relayout(); + -+ icon_size_scale_value_changed_cb: function(scale){ -+ // Avoid settings the size consinuosly -+ if (this._icon_size_timeout >0) -+ Mainloop.source_remove(this._icon_size_timeout); ++ if (newWin.length < 1) ++ this._getTopMenu().close(~0); ++ }, + -+ this._icon_size_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, Lang.bind(this, function(){ -+ this._settings.set_int('dash-max-icon-size', scale.get_value()); -+ this._icon_size_timeout = 0; -+ return GLib.SOURCE_REMOVE; -+ })); -+ }, ++ isAnimatingOut: function() { ++ return this.actor.get_children().reduce(function(result, actor) { ++ return result || actor.animatingOut; ++ }, false); ++ }, + -+ custom_opacity_scale_value_changed_cb: function(scale){ -+ // Avoid settings the opacity consinuosly as it's change is animated -+ if (this._opacity_timeout >0) -+ Mainloop.source_remove(this._opacity_timeout); ++ sortWindowsCompareFunction: function(windowA, windowB) { ++ let ws_index = global.screen.get_active_workspace_index(); ++ let winA_inActiveWS = windowA.get_workspace().index() == ws_index; ++ let winB_inActiveWS = windowB.get_workspace().index() == ws_index; + -+ this._opacity_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, Lang.bind(this, function(){ -+ this._settings.set_double('background-opacity', scale.get_value()); -+ this._opacity_timeout = 0; -+ return GLib.SOURCE_REMOVE; -+ })); -+ }, ++ // Only change the order if winA is not in the current WS, while winB is ++ if (!winA_inActiveWS && winB_inActiveWS) ++ return 1; + -+ custom_opacity_scale_format_value_cb: function(scale, value) { -+ return Math.round(value*100) + ' %'; -+ } ++ return 0; + } +}); + -+function init() { -+ Convenience.initTranslations(); -+} ++const WindowPreviewMenuItem = new Lang.Class({ ++ Name: 'WindowPreviewMenuItem', ++ Extends: PopupMenu.PopupBaseMenuItem, ++ ++ _init: function(window, params) { ++ this._window = window; ++ this._destroyId = 0; ++ this._windowAddedId = 0; ++ params = Params.parse(params, { style_class: 'app-well-preview-menu-item' }); ++ this.parent(params); ++ ++ this._cloneBin = new St.Bin(); ++ this._cloneBin.set_size(PREVIEW_MAX_WIDTH, PREVIEW_MAX_HEIGHT); ++ ++ // TODO: improve the way the closebutton is layout. Just use some padding ++ // for the moment. ++ this._cloneBin.set_style('padding: 5px'); ++ ++ this.closeButton = new St.Button({ style_class: 'window-close', ++ x_expand: true, ++ y_expand: true}); ++ this.closeButton.set_x_align(Clutter.ActorAlign.END); ++ this.closeButton.set_y_align(Clutter.ActorAlign.START); ++ ++ ++ this.closeButton.opacity = 0; ++ this.closeButton.connect('clicked', Lang.bind(this, this._closeWindow)); ++ ++ let overlayGroup = new Clutter.Actor({layout_manager: new Clutter.BinLayout() }); ++ ++ overlayGroup.add_actor(this._cloneBin); ++ overlayGroup.add_actor(this.closeButton); ++ ++ let label = new St.Label({ text: window.get_title()}); ++ label.set_style('max-width: '+PREVIEW_MAX_WIDTH +'px'); ++ let labelBin = new St.Bin({ child: label, ++ x_align: St.Align.MIDDLE}); ++ ++ this._windowTitleId = this._window.connect('notify::title', Lang.bind(this, function() { ++ label.set_text(this._window.get_title()); ++ })); ++ ++ let box = new St.BoxLayout({ vertical: true, ++ reactive:true, ++ x_expand:true }); ++ box.add(overlayGroup); ++ box.add(labelBin); ++ this.actor.add_actor(box); ++ ++ this.actor.connect('enter-event', ++ Lang.bind(this, this._onEnter)); ++ this.actor.connect('leave-event', ++ Lang.bind(this, this._onLeave)); ++ this.actor.connect('key-focus-in', ++ Lang.bind(this, this._onEnter)); ++ this.actor.connect('key-focus-out', ++ Lang.bind(this, this._onLeave)); ++ ++ this._cloneTexture(window); ++ ++ }, ++ ++ _cloneTexture: function(metaWin){ ++ ++ let mutterWindow = metaWin.get_compositor_private(); ++ ++ // Newly-created windows are added to a workspace before ++ // the compositor finds out about them... ++ // Moreover sometimes they return an empty texture, thus as a workarounf also check for it size ++ if (!mutterWindow || !mutterWindow.get_texture() || !mutterWindow.get_texture().get_size()[0]) { ++ let id = Mainloop.idle_add(Lang.bind(this, ++ function () { ++ // Check if there's still a point in getting the texture, ++ // otherwise this could go on indefinitely ++ if (this.actor && metaWin.get_workspace()) ++ this._cloneTexture(metaWin); ++ return GLib.SOURCE_REMOVE; ++ })); ++ GLib.Source.set_name_by_id(id, '[dash-to-dock] this._cloneTexture'); ++ return; ++ } + -+function buildPrefsWidget() { -+ let settings = new Settings(); -+ let widget = settings.widget; -+ widget.show_all(); -+ return widget; -+} ++ let windowTexture = mutterWindow.get_texture(); ++ let [width, height] = windowTexture.get_size(); + -diff --git a/extensions/dash-to-dock/stylesheet.css b/extensions/dash-to-dock/stylesheet.css -new file mode 100644 -index 0000000..e2ba893 ---- /dev/null -+++ b/extensions/dash-to-dock/stylesheet.css -@@ -0,0 +1,181 @@ -+/* Shrink the dash by reducing padding and border radius */ -+#dashtodockContainer.shrink #dash, -+#dashtodockContainer.dashtodock #dash { -+ border:1px; -+} ++ let scale = Math.min(1.0, PREVIEW_MAX_WIDTH/width, PREVIEW_MAX_HEIGHT/height); + -+#dashtodockContainer.shrink.left #dash, -+#dashtodockContainer.dashtodock.left #dash { -+ padding: 1px; -+ border-left: 0px; -+ border-radius: 0px 6px 6px 0px; -+} ++ let clone = new Clutter.Clone ({ source: windowTexture, ++ reactive: true, ++ width: width * scale, ++ height: height * scale }); + -+#dashtodockContainer.shrink.right #dash, -+#dashtodockContainer.dashtodock.right #dash { -+ padding: 1px; -+ border-right: 0px; -+ border-radius: 6px 0px 0px 6px; -+} ++ // when the source actor is destroyed, i.e. the window closed, first destroy the clone ++ // and then destroy the menu item (do this animating out) ++ this._destroyId = mutterWindow.connect('destroy', Lang.bind(this, function() { ++ clone.destroy(); ++ this._destroyId = 0; // avoid to try to disconnect this signal from mutterWindow in _onDestroy(), ++ // as the object was just destroyed ++ this._animateOutAndDestroy(); ++ })); + -+#dashtodockContainer.shrink.top #dash, -+#dashtodockContainer.dashtodock.top #dash { -+ padding: 1px; -+ border-top: 0px; -+ border-radius: 0px 0px 6px 6px; -+} ++ this._clone = clone; ++ this._mutterWindow = mutterWindow; ++ this._cloneBin.set_child(this._clone); ++ }, + -+#dashtodockContainer.shrink.bottom #dash, -+#dashtodockContainer.dashtodock.bottom #dash { -+ padding: 1px; -+ border-bottom: 0px; -+ border-radius: 6px 6px 0px 0px; -+} ++ _windowCanClose: function() { ++ return this._window.can_close() && ++ !this._hasAttachedDialogs(); ++ }, + -+/* Scrollview style */ -+.bottom #dashtodockDashScrollview, -+.top #dashtodockDashScrollview { -+ -st-hfade-offset: 24px; -+} ++ _closeWindow: function(actor) { ++ this._workspace = this._window.get_workspace(); + -+.left #dashtodockDashScrollview, -+.right #dashtodockDashScrollview { -+ -st-vfade-offset: 24px; -+} ++ // This mechanism is copied from the workspace.js upstream code ++ // It forces window activation if the windows don't get closed, ++ // for instance because asking user confirmation, by monitoring the opening of ++ // such additional confirmation window ++ this._windowAddedId = this._workspace.connect('window-added', ++ Lang.bind(this, ++ this._onWindowAdded)); + -+#dashtodockContainer.running-dots .dash-item-container > StButton, -+#dashtodockContainer.dashtodock .dash-item-container > StButton { -+ transition-duration: 250; -+ background-size: contain; -+} ++ this.deleteAllWindows(); ++ }, + -+#dashtodockContainer.shrink .dash-item-container > StButton, -+#dashtodockContainer.dashtodock .dash-item-container > StButton { -+ padding: 1px 2px; -+} ++ deleteAllWindows: function() { ++ // Delete all windows, starting from the bottom-most (most-modal) one ++ //let windows = this._window.get_compositor_private().get_children(); ++ let windows = this._clone.get_children(); ++ for (let i = windows.length - 1; i >= 1; i--) { ++ let realWindow = windows[i].source; ++ let metaWindow = realWindow.meta_window; + -+/* Dash height extended to the whole available vertical space */ -+#dashtodockContainer.extended.top #dash, -+#dashtodockContainer.extended.right #dash, -+#dashtodockContainer.extended.bottom #dash, -+#dashtodockContainer.extended.left #dash { -+ border-radius: 0; -+} ++ metaWindow.delete(global.get_current_time()); ++ } + -+#dashtodockContainer.extended.top #dash, -+#dashtodockContainer.extended.bottom #dash { -+ border-left:0px; -+ border-right:0px; -+} ++ this._window.delete(global.get_current_time()); ++ }, + -+#dashtodockContainer.extended.right #dash, -+#dashtodockContainer.extended.left #dash { -+ border-top:0px; -+ border-bottom:0px; -+} ++ _onWindowAdded: function(workspace, win) { ++ let metaWindow = this._window; + -+/* Running and focused application style */ ++ if (win.get_transient_for() == metaWindow) { ++ workspace.disconnect(this._windowAddedId); ++ this._windowAddedId = 0; + -+#dashtodockContainer.running-dots .app-well-app.running > .overview-icon, -+#dashtodockContainer.dashtodock .app-well-app.running > .overview-icon { -+ background-image:none; -+} ++ // use an idle handler to avoid mapping problems - ++ // see comment in Workspace._windowAdded ++ let id = Mainloop.idle_add(Lang.bind(this, ++ function() { ++ this.emit('activate'); ++ return GLib.SOURCE_REMOVE; ++ })); ++ GLib.Source.set_name_by_id(id, '[dash-to-dock] this.emit'); ++ } ++ }, + -+#dashtodockContainer.running-dots .app-well-app.focused > .overview-icon, -+#dashtodockContainer.dashtodock .app-well-app.focused > .overview-icon { -+ transition-duration: 250; -+ background-gradient-start: rgba(255, 255, 255, .05); -+ background-gradient-end: rgba(255, 255, 255, .15); -+ background-gradient-direction: vertical; -+ border-radius: 4px; -+ box-shadow: inset 0px 1px 2px 0px rgba(0, 0, 0, 1); -+} ++ _hasAttachedDialogs: function() { ++ // count trasient windows ++ let n=0; ++ this._window.foreach_transient(function(){n++;}); ++ return n>0; ++ }, + -+#dashtodockContainer.running-dots.left .running1, -+#dashtodockContainer.dashtodock.left .running1 { -+ background-image: url('./media/one.svg'); -+} ++ _onEnter: function() { ++ this._showCloseButton(); ++ return Clutter.EVENT_PROPAGATE; ++ }, + -+#dashtodockContainer.running-dots.right .running1, -+#dashtodockContainer.dashtodock.right .running1 { -+ background-image: url('./media/one_rtl.svg'); -+} ++ _onLeave: function() { ++ if (!this._cloneBin.has_pointer && ++ !this.closeButton.has_pointer) ++ this._hideCloseButton(); + -+#dashtodockContainer.running-dots.bottom .running1, -+#dashtodockContainer.dashtodock.bottom .running1 { -+ background-image: url('./media/one_bottom.svg'); -+} ++ return Clutter.EVENT_PROPAGATE; ++ }, + -+#dashtodockContainer.running-dots.top .running1, -+#dashtodockContainer.dashtodock.top .running1 { -+ background-image: url('./media/one_top.svg'); -+} ++ _idleToggleCloseButton: function() { ++ this._idleToggleCloseId = 0; + -+#dashtodockContainer.running-dots.left .running2, -+#dashtodockContainer.dashtodock.left .running2 { -+ background-image: url('./media/two.svg'); -+} ++ if (!this._cloneBin.has_pointer && ++ !this.closeButton.has_pointer) ++ this._hideCloseButton(); + -+#dashtodockContainer.running-dots.right .running2, -+#dashtodockContainer.dashtodock.right .running2 { -+ background-image: url('./media/two_rtl.svg'); -+} ++ return GLib.SOURCE_REMOVE; ++ }, + -+#dashtodockContainer.running-dots.bottom .running2, -+#dashtodockContainer.dashtodock.bottom .running2 { -+ background-image: url('./media/two_bottom.svg'); -+} ++ _showCloseButton: function() { + -+#dashtodockContainer.running-dots.top .running2, -+#dashtodockContainer.dashtodock.top .running2 { -+ background-image: url('./media/two_top.svg'); -+} ++ if (this._windowCanClose()) { ++ this.closeButton.show(); ++ Tweener.addTween(this.closeButton, ++ { opacity: 255, ++ time: Workspace.CLOSE_BUTTON_FADE_TIME, ++ transition: 'easeOutQuad' }); ++ } ++ }, + -+#dashtodockContainer.running-dots.left .running3, -+#dashtodockContainer.dashtodock.left .running3 { -+ background-image: url('./media/three.svg'); -+} ++ _hideCloseButton: function() { ++ Tweener.addTween(this.closeButton, ++ { opacity: 0, ++ time: Workspace.CLOSE_BUTTON_FADE_TIME, ++ transition: 'easeInQuad' }); ++ }, + -+#dashtodockContainer.running-dots.right .running3, -+#dashtodockContainer.dashtodock.right .running3 { -+ background-image: url('./media/three_rtl.svg'); -+} ++ show: function(animate) { ++ let fullWidth = this.actor.get_width(); + -+#dashtodockContainer.running-dots.bottom .running3, -+#dashtodockContainer.dashtodock.bottom .running3 { -+ background-image: url('./media/three_bottom.svg'); -+} ++ this.actor.opacity = 0; ++ this.actor.set_width(0); + -+#dashtodockContainer.running-dots.top .running3, -+#dashtodockContainer.dashtodock.top .running3 { -+ background-image: url('./media/three_top.svg'); -+} ++ let time = animate ? 0.25 : 0; ++ Tweener.addTween(this.actor, ++ { opacity: 255, ++ width: fullWidth, ++ time: time, ++ transition: 'easeInOutQuad' ++ }); ++ }, + -+#dashtodockContainer.running-dots.left .running4, -+#dashtodockContainer.dashtodock.left .running4 { -+ background-image: url('./media/four.svg'); -+} ++ _animateOutAndDestroy: function() { ++ Tweener.addTween(this.actor, ++ { opacity: 0, ++ time: 0.25, ++ }); + -+#dashtodockContainer.running-dots.right .running4, -+#dashtodockContainer.dashtodock.right .running4 { -+ background-image: url('./media/four_rtl.svg'); -+} ++ Tweener.addTween(this.actor, ++ { height: 0, ++ width: 0, ++ time: 0.25, ++ delay: 0.25, ++ onCompleteScope: this, ++ onComplete: function() { ++ this.actor.destroy(); ++ } ++ }); ++ }, + -+#dashtodockContainer.running-dots.bottom .running4, -+#dashtodockContainer.dashtodock.bottom .running4{ -+ background-image: url('./media/four_bottom.svg'); -+} ++ activate: function() { ++ this._getTopMenu().close(); ++ Main.activateWindow(this._window); ++ }, + -+#dashtodockContainer.running-dots.top .running4, -+#dashtodockContainer.dashtodock.top .running4 { -+ background-image: url('./media/four_top.svg'); -+} ++ _onDestroy: function() { + -+#dashtodockContainer.dashtodock #dash { -+ background: rgba(0,0,0,0.9); -+} ++ this.parent(); ++ ++ if (this._windowAddedId > 0) { ++ this._workspace.disconnect(this._windowAddedId); ++ this._windowAddedId = 0; ++ } ++ ++ if (this._destroyId > 0) { ++ this._mutterWindow.disconnect(this._destroyId); ++ this._destroyId = 0; ++ } ++ ++ if (this._windowTitleId > 0) { ++ this._window.disconnect(this._windowTitleId); ++ this._windowTitleId = 0; ++ } ++ } ++ ++}); + -+#dashtodockContainer.dashtodock:overview #dash { -+ background: rgba(0,0,0,0.5); -+ transition-duration: 250ms; -+} -- -2.7.2 +2.13.0 -From 93c5d76ce1bd57a1bffb6bb98e23406553b320bf Mon Sep 17 00:00:00 2001 +From aa76304194ae13defe36cadf3aa8e73031d7a7ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Wed, 20 May 2015 18:55:47 +0200 Subject: [PATCH 3/4] Add panel-favorites extension @@ -9371,35 +13020,35 @@ Subject: [PATCH 3/4] Add panel-favorites extension create mode 100644 extensions/panel-favorites/stylesheet.css diff --git a/configure.ac b/configure.ac -index d40b9e0..92969e2 100644 +index cc7cd29..f05b0cb 100644 --- a/configure.ac +++ b/configure.ac -@@ -29,7 +29,7 @@ AC_SUBST([SHELL_VERSION]) +@@ -31,7 +31,7 @@ AC_SUBST([SHELL_VERSION]) dnl keep this in alphabetic order CLASSIC_EXTENSIONS="apps-menu places-menu alternate-tab launch-new-instance window-list" DEFAULT_EXTENSIONS="$CLASSIC_EXTENSIONS drive-menu screenshot-window-sizer windowsNavigator workspace-indicator" --ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows dash-to-dock example native-window-placement systemMonitor top-icons user-theme" -+ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows dash-to-dock example native-window-placement panel-favorites systemMonitor top-icons user-theme" +-ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows dash-to-dock example native-window-placement top-icons user-theme" ++ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows dash-to-dock example native-window-placement panel-favorites top-icons user-theme" AC_SUBST(CLASSIC_EXTENSIONS, [$CLASSIC_EXTENSIONS]) AC_SUBST(ALL_EXTENSIONS, [$ALL_EXTENSIONS]) AC_ARG_ENABLE([extensions], -@@ -66,7 +66,7 @@ for e in $enable_extensions; do - [AC_MSG_WARN([libgtop-2.0 not found, disabling systemMonitor])]) - ;; +@@ -63,7 +63,7 @@ ENABLED_EXTENSIONS= + for e in $enable_extensions; do + case $e in dnl keep this in alphabetic order - alternate-tab|apps-menu|auto-move-windows|dash-to-dock|drive-menu|example|launch-new-instance|native-window-placement|places-menu|screenshot-window-sizer|top-icons|user-theme|window-list|windowsNavigator|workspace-indicator) + alternate-tab|apps-menu|auto-move-windows|dash-to-dock|drive-menu|example|launch-new-instance|native-window-placement|panel-favorites|places-menu|screenshot-window-sizer|top-icons|user-theme|window-list|windowsNavigator|workspace-indicator) ENABLED_EXTENSIONS="$ENABLED_EXTENSIONS $e" ;; *) -@@ -87,6 +87,7 @@ AC_CONFIG_FILES([ +@@ -86,6 +86,7 @@ AC_CONFIG_FILES([ extensions/example/Makefile extensions/launch-new-instance/Makefile extensions/native-window-placement/Makefile + extensions/panel-favorites/Makefile extensions/places-menu/Makefile extensions/screenshot-window-sizer/Makefile - extensions/systemMonitor/Makefile + extensions/top-icons/Makefile diff --git a/extensions/panel-favorites/Makefile.am b/extensions/panel-favorites/Makefile.am new file mode 100644 index 0000000..4ce3128 @@ -9719,10 +13368,10 @@ index 0000000..120adac + -y-offset: 6px; +} -- -2.7.2 +2.13.0 -From b579155a83474964f810b444df1c09f0b1c52b7b Mon Sep 17 00:00:00 2001 +From d1f53a86db0e65d08cc7664561b874169c4dd85c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Fri, 4 Mar 2016 17:07:21 +0100 Subject: [PATCH 4/4] Add updates-dialog extension @@ -9732,41 +13381,41 @@ Subject: [PATCH 4/4] Add updates-dialog extension extensions/updates-dialog/Makefile.am | 5 + extensions/updates-dialog/extension.js | 490 +++++++++++++++++++++ extensions/updates-dialog/metadata.json.in | 10 + - ....shell.extensions.updates-dialog.gschema.xml.in | 30 ++ + ...ome.shell.extensions.updates-dialog.gschema.xml | 30 ++ extensions/updates-dialog/stylesheet.css | 1 + po/POTFILES.in | 2 + 7 files changed, 541 insertions(+), 2 deletions(-) create mode 100644 extensions/updates-dialog/Makefile.am create mode 100644 extensions/updates-dialog/extension.js create mode 100644 extensions/updates-dialog/metadata.json.in - create mode 100644 extensions/updates-dialog/org.gnome.shell.extensions.updates-dialog.gschema.xml.in + create mode 100644 extensions/updates-dialog/org.gnome.shell.extensions.updates-dialog.gschema.xml create mode 100644 extensions/updates-dialog/stylesheet.css diff --git a/configure.ac b/configure.ac -index 92969e2..528a2a5 100644 +index f05b0cb..7944b52 100644 --- a/configure.ac +++ b/configure.ac -@@ -29,7 +29,7 @@ AC_SUBST([SHELL_VERSION]) +@@ -31,7 +31,7 @@ AC_SUBST([SHELL_VERSION]) dnl keep this in alphabetic order CLASSIC_EXTENSIONS="apps-menu places-menu alternate-tab launch-new-instance window-list" DEFAULT_EXTENSIONS="$CLASSIC_EXTENSIONS drive-menu screenshot-window-sizer windowsNavigator workspace-indicator" --ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows dash-to-dock example native-window-placement panel-favorites systemMonitor top-icons user-theme" -+ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows dash-to-dock example native-window-placement panel-favorites systemMonitor top-icons updates-dialog user-theme" +-ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows dash-to-dock example native-window-placement panel-favorites top-icons user-theme" ++ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows dash-to-dock example native-window-placement panel-favorites top-icons updates-dialog user-theme" AC_SUBST(CLASSIC_EXTENSIONS, [$CLASSIC_EXTENSIONS]) AC_SUBST(ALL_EXTENSIONS, [$ALL_EXTENSIONS]) AC_ARG_ENABLE([extensions], -@@ -66,7 +66,7 @@ for e in $enable_extensions; do - [AC_MSG_WARN([libgtop-2.0 not found, disabling systemMonitor])]) - ;; +@@ -63,7 +63,7 @@ ENABLED_EXTENSIONS= + for e in $enable_extensions; do + case $e in dnl keep this in alphabetic order - alternate-tab|apps-menu|auto-move-windows|dash-to-dock|drive-menu|example|launch-new-instance|native-window-placement|panel-favorites|places-menu|screenshot-window-sizer|top-icons|user-theme|window-list|windowsNavigator|workspace-indicator) + alternate-tab|apps-menu|auto-move-windows|dash-to-dock|drive-menu|example|launch-new-instance|native-window-placement|panel-favorites|places-menu|screenshot-window-sizer|top-icons|updates-dialog|user-theme|window-list|windowsNavigator|workspace-indicator) ENABLED_EXTENSIONS="$ENABLED_EXTENSIONS $e" ;; *) -@@ -92,6 +92,7 @@ AC_CONFIG_FILES([ +@@ -90,6 +90,7 @@ AC_CONFIG_FILES([ + extensions/places-menu/Makefile extensions/screenshot-window-sizer/Makefile - extensions/systemMonitor/Makefile extensions/top-icons/Makefile + extensions/updates-dialog/Makefile extensions/user-theme/Makefile @@ -10295,11 +13944,11 @@ index 0000000..9946abb +"shell-version": [ "@shell_current@" ], +"url": "http://rtcm.fedorapeople.org/updates-dialog" +} -diff --git a/extensions/updates-dialog/org.gnome.shell.extensions.updates-dialog.gschema.xml.in b/extensions/updates-dialog/org.gnome.shell.extensions.updates-dialog.gschema.xml.in +diff --git a/extensions/updates-dialog/org.gnome.shell.extensions.updates-dialog.gschema.xml b/extensions/updates-dialog/org.gnome.shell.extensions.updates-dialog.gschema.xml new file mode 100644 -index 0000000..a4a7f8b +index 0000000..c08d33c --- /dev/null -+++ b/extensions/updates-dialog/org.gnome.shell.extensions.updates-dialog.gschema.xml.in ++++ b/extensions/updates-dialog/org.gnome.shell.extensions.updates-dialog.gschema.xml @@ -0,0 +1,30 @@ + + @@ -10339,18 +13988,18 @@ index 0000000..25134b6 @@ -0,0 +1 @@ +/* This extensions requires no special styling */ diff --git a/po/POTFILES.in b/po/POTFILES.in -index 89bd3ee..b229b6a 100644 +index d98ca1b..43a817f 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in -@@ -16,6 +16,8 @@ extensions/native-window-placement/org.gnome.shell.extensions.native-window-plac +@@ -15,6 +15,8 @@ extensions/native-window-placement/org.gnome.shell.extensions.native-window-plac extensions/places-menu/extension.js extensions/places-menu/placeDisplay.js - extensions/systemMonitor/extension.js + extensions/screenshot-window-sizer/org.gnome.shell.extensions.screenshot-window-sizer.gschema.xml +extensions/updates-dialog/extension.js -+extensions/updates-dialog/org.gnome.shell.extensions.updates-dialog.gschema.xml.in ++extensions/updates-dialog/org.gnome.shell.extensions.updates-dialog.gschema.xml extensions/user-theme/extension.js - extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml.in + extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml extensions/window-list/extension.js -- -2.7.2 +2.13.0 diff --git a/SOURCES/apps-menu-desktop-dnd.patch b/SOURCES/apps-menu-desktop-dnd.patch new file mode 100644 index 0000000..41c2eed --- /dev/null +++ b/SOURCES/apps-menu-desktop-dnd.patch @@ -0,0 +1,460 @@ +From 5aad5b8cb31833cb2db12cd0b9c2c4daceb93536 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 15 Mar 2017 05:16:49 +0100 +Subject: [PATCH 1/5] apps-menu: Use Map to keep track of app items + +The use of Array to keep track of inserted items is extremely +confusing, as no elements are ever added to the array. What +the code actually does is monkey-patching properties into an +empty object (that happens to be of type "Array"). While the +direct idiomatic replacement would be {}, update the code to +use a proper map instead. + +https://bugzilla.gnome.org/show_bug.cgi?id=780371 +--- + extensions/apps-menu/extension.js | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js +index c840a06..5f44b42 100644 +--- a/extensions/apps-menu/extension.js ++++ b/extensions/apps-menu/extension.js +@@ -296,6 +296,7 @@ const ApplicationsButton = new Lang.Class({ + Lang.bind(this, this._setKeybinding)); + this._setKeybinding(); + ++ this._applicationsButtons = new Map(); + this.reloadFlag = false; + this._createLayout(); + this._display(); +@@ -504,7 +505,7 @@ const ApplicationsButton = new Lang.Class({ + }, + + _display: function() { +- this._applicationsButtons = new Array(); ++ this._applicationsButtons.clear(); + this.mainBox.style=('width: 35em;'); + this.mainBox.hide(); + +@@ -563,12 +564,13 @@ const ApplicationsButton = new Lang.Class({ + if (apps) { + for (let i = 0; i < apps.length; i++) { + let app = apps[i]; +- if (!this._applicationsButtons[app]) { +- let applicationMenuItem = new ApplicationMenuItem(this, app); +- this._applicationsButtons[app] = applicationMenuItem; ++ let item = this._applicationsButtons.get(app); ++ if (!item) { ++ item = new ApplicationMenuItem(this, app); ++ this._applicationsButtons.set(app, item); + } +- if (!this._applicationsButtons[app].actor.get_parent()) +- this.applicationsBox.add_actor(this._applicationsButtons[app].actor); ++ if (!item.actor.get_parent()) ++ this.applicationsBox.add_actor(item.actor); + } + } + }, +-- +2.12.2 + + +From 7ed3f8618af00d6d0fb1440b6668d336b2b611d0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 15 Mar 2017 01:30:53 +0100 +Subject: [PATCH 2/5] apps-menu: Allow creating desktop launchers via DND + +Back in the olden days, it used to be possible to drag items from +the application menu to the desktop to create a launcher shortcut. +Reimplement that "classic" functionality in the apps menu extension. + +https://bugzilla.gnome.org/show_bug.cgi?id=780371 +--- + extensions/apps-menu/extension.js | 116 +++++++++++++++++++++++++++++++++++++- + 1 file changed, 115 insertions(+), 1 deletion(-) + +diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js +index 5f44b42..160975d 100644 +--- a/extensions/apps-menu/extension.js ++++ b/extensions/apps-menu/extension.js +@@ -1,6 +1,7 @@ + /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ + + const Atk = imports.gi.Atk; ++const DND = imports.ui.dnd; + const Gio = imports.gi.Gio; + const GMenu = imports.gi.GMenu; + const Lang = imports.lang; +@@ -8,6 +9,7 @@ const Shell = imports.gi.Shell; + const St = imports.gi.St; + const Clutter = imports.gi.Clutter; + const Main = imports.ui.main; ++const Meta = imports.gi.Meta; + const PanelMenu = imports.ui.panelMenu; + const PopupMenu = imports.ui.popupMenu; + const Gtk = imports.gi.Gtk; +@@ -73,6 +75,16 @@ const ApplicationMenuItem = new Lang.Class({ + textureCache.disconnect(iconThemeChangedId); + })); + this._updateIcon(); ++ ++ this.actor._delegate = this; ++ let draggable = DND.makeDraggable(this.actor); ++ ++ draggable.connect('drag-begin', () => { ++ Shell.util_set_hidden_from_pick(Main.legacyTray.actor, true); ++ }); ++ draggable.connect('drag-end', () => { ++ Shell.util_set_hidden_from_pick(Main.legacyTray.actor, false); ++ }); + }, + + activate: function(event) { +@@ -88,8 +100,16 @@ const ApplicationMenuItem = new Lang.Class({ + this.parent(active, params); + }, + ++ getDragActor: function() { ++ return this._app.create_icon_texture(APPLICATION_ICON_SIZE); ++ }, ++ ++ getDragActorSource: function() { ++ return this._iconBin; ++ }, ++ + _updateIcon: function() { +- this._iconBin.set_child(this._app.create_icon_texture(APPLICATION_ICON_SIZE)); ++ this._iconBin.set_child(this.getDragActor()); + } + }); + +@@ -251,6 +271,93 @@ const ApplicationsMenu = new Lang.Class({ + } + }); + ++const DesktopTarget = new Lang.Class({ ++ Name: 'DesktopTarget', ++ ++ _init: function() { ++ this._desktopDestroyedId = 0; ++ ++ this._windowAddedId = ++ global.window_group.connect('actor-added', ++ Lang.bind(this, this._onWindowAdded)); ++ ++ global.get_window_actors().forEach(a => { ++ this._onWindowAdded(a.get_parent(), a); ++ }); ++ }, ++ ++ _onWindowAdded: function(group, actor) { ++ if (!(actor instanceof Meta.WindowActor)) ++ return; ++ ++ if (actor.meta_window.get_window_type() == Meta.WindowType.DESKTOP) ++ this._setDesktop(actor); ++ }, ++ ++ _setDesktop: function(desktop) { ++ if (this._desktop) { ++ this._desktop.disconnect(this._desktopDestroyedId); ++ this._desktopDestroyedId = 0; ++ ++ delete this._desktop._delegate; ++ } ++ ++ this._desktop = desktop; ++ ++ if (this._desktop) { ++ this._desktopDestroyedId = this._desktop.connect('destroy', () => { ++ this._setDesktop(null); ++ }); ++ this._desktop._delegate = this; ++ } ++ }, ++ ++ _getSourceAppInfo: function(source) { ++ if (!(source instanceof ApplicationMenuItem)) ++ return null; ++ return source._app.app_info; ++ }, ++ ++ destroy: function() { ++ if (this._windowAddedId) ++ global.window_group.disconnect(this._windowAddedId); ++ this._windowAddedId = 0; ++ ++ this._setDesktop(null); ++ }, ++ ++ handleDragOver: function(source, actor, x, y, time) { ++ let appInfo = this._getSourceAppInfo(source); ++ if (!appInfo) ++ return DND.DragMotionResult.CONTINUE; ++ ++ return DND.DragMotionResult.COPY_DROP; ++ }, ++ ++ acceptDrop: function(source, actor, x, y, time) { ++ let appInfo = this._getSourceAppInfo(source); ++ if (!appInfo) ++ return false; ++ ++ this.emit('app-dropped'); ++ ++ let desktop = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP); ++ ++ let src = Gio.File.new_for_path(appInfo.get_filename()); ++ let dst = Gio.File.new_for_path(GLib.build_filenamev([desktop, src.get_basename()])); ++ ++ try { ++ // copy_async() isn't introspectable :-( ++ src.copy(dst, Gio.FileCopyFlags.OVERWRITE, null, null); ++ } catch(e) { ++ log('Failed to copy to desktop: ' + e.message); ++ } ++ ++ return true; ++ } ++}); ++Signals.addSignalMethods(DesktopTarget.prototype); ++ + const ApplicationsButton = new Lang.Class({ + Name: 'ApplicationsButton', + Extends: PanelMenu.Button, +@@ -296,6 +403,11 @@ const ApplicationsButton = new Lang.Class({ + Lang.bind(this, this._setKeybinding)); + this._setKeybinding(); + ++ this._desktopTarget = new DesktopTarget(); ++ this._desktopTarget.connect('app-dropped', () => { ++ this.menu.close(); ++ }); ++ + this._applicationsButtons = new Map(); + this.reloadFlag = false; + this._createLayout(); +@@ -340,6 +452,8 @@ const ApplicationsButton = new Lang.Class({ + Main.sessionMode.hasOverview ? + Lang.bind(Main.overview, Main.overview.toggle) : + null); ++ ++ this._desktopTarget.destroy(); + }, + + _onCapturedEvent: function(actor, event) { +-- +2.12.2 + + +From c90b4ec5daf6d3906360e61330a7aa386470d65b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 15 Mar 2017 05:23:57 +0100 +Subject: [PATCH 3/5] apps-menu: Only enable DND when there's a desktop + +It's not very useful to allow dragging when there's no drop target, +so tie the functionality added in the previous commit to the presence +of a DESKTOP window. + +https://bugzilla.gnome.org/show_bug.cgi?id=780371 +--- + extensions/apps-menu/extension.js | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js +index 160975d..a013a3e 100644 +--- a/extensions/apps-menu/extension.js ++++ b/extensions/apps-menu/extension.js +@@ -79,6 +79,13 @@ const ApplicationMenuItem = new Lang.Class({ + this.actor._delegate = this; + let draggable = DND.makeDraggable(this.actor); + ++ let maybeStartDrag = draggable._maybeStartDrag; ++ draggable._maybeStartDrag = (event) => { ++ if (this._dragEnabled) ++ return maybeStartDrag.call(draggable, event); ++ return false; ++ }; ++ + draggable.connect('drag-begin', () => { + Shell.util_set_hidden_from_pick(Main.legacyTray.actor, true); + }); +@@ -100,6 +107,13 @@ const ApplicationMenuItem = new Lang.Class({ + this.parent(active, params); + }, + ++ setDragEnabled: function(enable) { ++ if (this._dragEnabled == enable) ++ return; ++ ++ this._dragEnabled = enable; ++ }, ++ + getDragActor: function() { + return this._app.create_icon_texture(APPLICATION_ICON_SIZE); + }, +@@ -286,6 +300,10 @@ const DesktopTarget = new Lang.Class({ + }); + }, + ++ get hasDesktop() { ++ return this._desktop != null; ++ }, ++ + _onWindowAdded: function(group, actor) { + if (!(actor instanceof Meta.WindowActor)) + return; +@@ -303,6 +321,7 @@ const DesktopTarget = new Lang.Class({ + } + + this._desktop = desktop; ++ this.emit('desktop-changed'); + + if (this._desktop) { + this._desktopDestroyedId = this._desktop.connect('destroy', () => { +@@ -407,6 +426,10 @@ const ApplicationsButton = new Lang.Class({ + this._desktopTarget.connect('app-dropped', () => { + this.menu.close(); + }); ++ this._desktopTarget.connect('desktop-changed', () => { ++ for (let item of this._applicationsButtons.values()) ++ item.setDragEnabled(this._desktopTarget.hasDesktop); ++ }); + + this._applicationsButtons = new Map(); + this.reloadFlag = false; +@@ -681,6 +704,7 @@ const ApplicationsButton = new Lang.Class({ + let item = this._applicationsButtons.get(app); + if (!item) { + item = new ApplicationMenuItem(this, app); ++ item.setDragEnabled(this._desktopTarget.hasDesktop); + this._applicationsButtons.set(app, item); + } + if (!item.actor.get_parent()) +-- +2.12.2 + + +From f88c53b1a31e3937cbcb5e15f77a385f063bc04f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 21 Apr 2017 23:54:41 +0200 +Subject: [PATCH 4/5] apps-menu: Mark copied .desktop files as trusted + +The application can already be launched from the menu without further +confirmation from the user, so there is no security gain in asking the +user to trust it when launched from the desktop - just set the appropriate +attributes of the newly copied file to mark it as trusted to nautilus. + +https://bugzilla.gnome.org/show_bug.cgi?id=781596 +--- + extensions/apps-menu/extension.js | 25 +++++++++++++++++++++++++ + 1 file changed, 25 insertions(+) + +diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js +index a013a3e..665cc71 100644 +--- a/extensions/apps-menu/extension.js ++++ b/extensions/apps-menu/extension.js +@@ -337,6 +337,30 @@ const DesktopTarget = new Lang.Class({ + return source._app.app_info; + }, + ++ _markTrusted: function(file) { ++ let modeAttr = Gio.FILE_ATTRIBUTE_UNIX_MODE; ++ let trustedAttr = 'metadata::trusted'; ++ let queryFlags = Gio.FileQueryInfoFlags.NONE; ++ let ioPriority = GLib.PRIORITY_DEFAULT; ++ ++ file.query_info_async(modeAttr, queryFlags, ioPriority, null, ++ (o, res) => { ++ try { ++ let info = o.query_info_finish(res); ++ let mode = info.get_attribute_uint32(modeAttr) | 0100; ++ ++ info.set_attribute_uint32(modeAttr, mode); ++ info.set_attribute_string(trustedAttr, 'yes'); ++ file.set_attributes_async (info, queryFlags, ioPriority, null, ++ (o, res) => { ++ o.set_attributes_finish(res); ++ }); ++ } catch(e) { ++ log('Failed to mark file as trusted: ' + e.message); ++ } ++ }); ++ }, ++ + destroy: function() { + if (this._windowAddedId) + global.window_group.disconnect(this._windowAddedId); +@@ -368,6 +392,7 @@ const DesktopTarget = new Lang.Class({ + try { + // copy_async() isn't introspectable :-( + src.copy(dst, Gio.FileCopyFlags.OVERWRITE, null, null); ++ this._markTrusted(dst); + } catch(e) { + log('Failed to copy to desktop: ' + e.message); + } +-- +2.12.2 + + +From 7c91918e6c7cea50f1d1f6fc25c24154b495c104 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Sat, 22 Apr 2017 00:21:37 +0200 +Subject: [PATCH 5/5] apps-menu: 'Touch' copied .desktop file after updating + attributes + +At least for now, nautilus' file monitor is blind to certain metadata +updates: +https://git.gnome.org/browse/nautilus/tree/src/nautilus-mime-actions.c#n1537 + +So in order for the changes in the previous patch to take effect +immediately, we need to force nautilus to reload the file info. +Achieve this by updating the access time of the newly copied file +after marking it as trusted. + +https://bugzilla.gnome.org/show_bug.cgi?id=781596 +--- + extensions/apps-menu/extension.js | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js +index 665cc71..d2613c5 100644 +--- a/extensions/apps-menu/extension.js ++++ b/extensions/apps-menu/extension.js +@@ -337,6 +337,23 @@ const DesktopTarget = new Lang.Class({ + return source._app.app_info; + }, + ++ _touchFile: function(file) { ++ let queryFlags = Gio.FileQueryInfoFlags.NONE; ++ let ioPriority = GLib.PRIORITY_DEFAULT; ++ ++ let info = new Gio.FileInfo(); ++ info.set_attribute_uint64(Gio.FILE_ATTRIBUTE_TIME_ACCESS, ++ GLib.get_real_time()); ++ file.set_attributes_async (info, queryFlags, ioPriority, null, ++ (o, res) => { ++ try { ++ o.set_attributes_finish(res); ++ } catch(e) { ++ log('Failed to update access time: ' + e.message); ++ } ++ }); ++ }, ++ + _markTrusted: function(file) { + let modeAttr = Gio.FILE_ATTRIBUTE_UNIX_MODE; + let trustedAttr = 'metadata::trusted'; +@@ -354,6 +371,9 @@ const DesktopTarget = new Lang.Class({ + file.set_attributes_async (info, queryFlags, ioPriority, null, + (o, res) => { + o.set_attributes_finish(res); ++ ++ // Hack: force nautilus to reload file info ++ this._touchFile(file); + }); + } catch(e) { + log('Failed to mark file as trusted: ' + e.message); +-- +2.12.2 + diff --git a/SOURCES/fix-apps-menu-panel-shortcut.patch b/SOURCES/fix-apps-menu-panel-shortcut.patch deleted file mode 100644 index 032e4bc..0000000 --- a/SOURCES/fix-apps-menu-panel-shortcut.patch +++ /dev/null @@ -1,210 +0,0 @@ -From 3ca39599e941563898b920a04192be0edb372470 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Florian=20M=C3=BCllner?= -Date: Mon, 23 Mar 2015 23:48:56 +0100 -Subject: [PATCH 1/3] apps-menu: Clean up signal code - -Setting up signal handlers inside a class and rely on outside code -to disconnect them via global variables is utterly weird. Just -disconnect everything inside the class when the corresponding actor -is destroyed. - -https://bugzilla.gnome.org/show_bug.cgi?id=746639 ---- - extensions/apps-menu/extension.js | 24 ++++++++++++------------ - 1 file changed, 12 insertions(+), 12 deletions(-) - -diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js -index 2d198c1..b392a91 100644 ---- a/extensions/apps-menu/extension.js -+++ b/extensions/apps-menu/extension.js -@@ -297,18 +297,19 @@ const ApplicationsButton = new Lang.Class({ - this.actor.label_actor = this._label; - - this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent)); -+ this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); - -- _showingId = Main.overview.connect('showing', Lang.bind(this, function() { -+ this._showingId = Main.overview.connect('showing', Lang.bind(this, function() { - this.actor.add_accessible_state (Atk.StateType.CHECKED); - })); -- _hidingId = Main.overview.connect('hiding', Lang.bind(this, function() { -+ this._hidingId = Main.overview.connect('hiding', Lang.bind(this, function() { - this.actor.remove_accessible_state (Atk.StateType.CHECKED); - })); - - this.reloadFlag = false; - this._createLayout(); - this._display(); -- _installedChangedId = appSys.connect('installed-changed', Lang.bind(this, function() { -+ this._installedChangedId = appSys.connect('installed-changed', Lang.bind(this, function() { - if (this.menu.isOpen) { - this._redisplay(); - this.mainBox.show(); -@@ -320,7 +321,7 @@ const ApplicationsButton = new Lang.Class({ - // Since the hot corner uses stage coordinates, Clutter won't - // queue relayouts for us when the panel moves. Queue a relayout - // when that happens. -- _panelBoxChangedId = Main.layoutManager.connect('panel-box-changed', Lang.bind(this, function() { -+ this._panelBoxChangedId = Main.layoutManager.connect('panel-box-changed', Lang.bind(this, function() { - container.queue_relayout(); - })); - }, -@@ -336,6 +337,13 @@ const ApplicationsButton = new Lang.Class({ - return separator; - }, - -+ _onDestroy: function() { -+ Main.overview.disconnect(this._showingId); -+ Main.overview.disconnect(this._hidingId); -+ Main.layoutManager.disconnect(this._panelBoxChangedId); -+ appSys.disconnect(this._installedChangedId); -+ }, -+ - _onCapturedEvent: function(actor, event) { - if (event.type() == Clutter.EventType.BUTTON_PRESS) { - if (!Main.overview.shouldToggleByCornerOrButton()) -@@ -583,10 +591,6 @@ const ApplicationsButton = new Lang.Class({ - - let appsMenuButton; - let activitiesButton; --let _hidingId; --let _installedChangedId; --let _panelBoxChangedId; --let _showingId; - - function enable() { - activitiesButton = Main.panel.statusArea['activities']; -@@ -604,10 +608,6 @@ function enable() { - - function disable() { - Main.panel.menuManager.removeMenu(appsMenuButton.menu); -- appSys.disconnect(_installedChangedId); -- Main.layoutManager.disconnect(_panelBoxChangedId); -- Main.overview.disconnect(_hidingId); -- Main.overview.disconnect(_showingId); - appsMenuButton.destroy(); - activitiesButton.container.show(); - --- -2.5.0 - - -From ddd92a1cdd2b9ab8e2ba054c8a2a131ae277808d Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Florian=20M=C3=BCllner?= -Date: Mon, 23 Mar 2015 23:55:43 +0100 -Subject: [PATCH 2/3] apps-menu: Move panel-main-menu handling into - AppsMenuButton - -This is really where it belongs, and will make an upcoming fix slightly -less ugly ... - -https://bugzilla.gnome.org/show_bug.cgi?id=746639 ---- - extensions/apps-menu/extension.js | 31 +++++++++++++++++-------------- - 1 file changed, 17 insertions(+), 14 deletions(-) - -diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js -index b392a91..03a50b7 100644 ---- a/extensions/apps-menu/extension.js -+++ b/extensions/apps-menu/extension.js -@@ -305,6 +305,7 @@ const ApplicationsButton = new Lang.Class({ - this._hidingId = Main.overview.connect('hiding', Lang.bind(this, function() { - this.actor.remove_accessible_state (Atk.StateType.CHECKED); - })); -+ this._setKeybinding(); - - this.reloadFlag = false; - this._createLayout(); -@@ -342,6 +343,13 @@ const ApplicationsButton = new Lang.Class({ - Main.overview.disconnect(this._hidingId); - Main.layoutManager.disconnect(this._panelBoxChangedId); - appSys.disconnect(this._installedChangedId); -+ -+ Main.wm.setCustomKeybindingHandler('panel-main-menu', -+ Shell.KeyBindingMode.NORMAL | -+ Shell.KeyBindingMode.OVERVIEW, -+ Main.sessionMode.hasOverview ? -+ Lang.bind(Main.overview, Main.overview.toggle) : -+ null); - }, - - _onCapturedEvent: function(actor, event) { -@@ -389,6 +397,15 @@ const ApplicationsButton = new Lang.Class({ - this.parent(menu, open); - }, - -+ _setKeybinding: function() { -+ Main.wm.setCustomKeybindingHandler('panel-main-menu', -+ Shell.KeyBindingMode.NORMAL | -+ Shell.KeyBindingMode.OVERVIEW, -+ Lang.bind(this, function() { -+ this.menu.toggle(); -+ })); -+ }, -+ - _redisplay: function() { - this.applicationsBox.destroy_all_children(); - this.categoriesBox.destroy_all_children(); -@@ -597,26 +614,12 @@ function enable() { - activitiesButton.container.hide(); - appsMenuButton = new ApplicationsButton(); - Main.panel.addToStatusArea('apps-menu', appsMenuButton, 1, 'left'); -- -- Main.wm.setCustomKeybindingHandler('panel-main-menu', -- Shell.KeyBindingMode.NORMAL | -- Shell.KeyBindingMode.OVERVIEW, -- function() { -- appsMenuButton.menu.toggle(); -- }); - } - - function disable() { - Main.panel.menuManager.removeMenu(appsMenuButton.menu); - appsMenuButton.destroy(); - activitiesButton.container.show(); -- -- Main.wm.setCustomKeybindingHandler('panel-main-menu', -- Shell.KeyBindingMode.NORMAL | -- Shell.KeyBindingMode.OVERVIEW, -- Main.sessionMode.hasOverview ? -- Lang.bind(Main.overview, Main.overview.toggle) : -- null); - } - - function init(metadata) { --- -2.5.0 - - -From cff31d811ece77cf6eae017c6e7223f87c67cacc Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Florian=20M=C3=BCllner?= -Date: Tue, 24 Mar 2015 00:28:10 +0100 -Subject: [PATCH 3/3] apps-menu: Take over shortcuts again on startup-complete - -For a while now, gnome-shell has initialized extensions before -setting up its own keybinding handling. As a result, our taking -over of the panel-main-menu shortcut will be overwritten when -the extension is enabled at startup - work around this by setting -up the keybinding again on LayoutManager::startup-complete. - -https://bugzilla.gnome.org/show_bug.cgi?id=746639 ---- - extensions/apps-menu/extension.js | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js -index 03a50b7..719f298 100644 ---- a/extensions/apps-menu/extension.js -+++ b/extensions/apps-menu/extension.js -@@ -305,6 +305,8 @@ const ApplicationsButton = new Lang.Class({ - this._hidingId = Main.overview.connect('hiding', Lang.bind(this, function() { - this.actor.remove_accessible_state (Atk.StateType.CHECKED); - })); -+ Main.layoutManager.connect('startup-complete', -+ Lang.bind(this, this._setKeybinding)); - this._setKeybinding(); - - this.reloadFlag = false; --- -2.5.0 - diff --git a/SOURCES/gnome-shell-extension-translations.patch b/SOURCES/gnome-shell-extension-translations.patch deleted file mode 100644 index 99336ca..0000000 --- a/SOURCES/gnome-shell-extension-translations.patch +++ /dev/null @@ -1,4157 +0,0 @@ ---- gnome-shell-extensions-3.14.4/po/de.po 2015-03-24 01:08:10.000000000 +0530 -+++ gnome-shell-extensions-3.14.4/po/de.po 2016-06-25 07:52:33.483142879 +0530 -@@ -5,22 +5,21 @@ - # Christian Kirbach , 2011, 2012. - # Benjamin Steinwender , 2013. - # Wolfgang Stöggl , 2014. --# -+# lstemmle , 2016. #zanata - msgid "" - msgstr "" - "Project-Id-Version: gnome-shell-extensions master\n" --"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-" --"shell&keywords=I18N+L10N&component=extensions\n" --"POT-Creation-Date: 2014-09-12 07:37+0000\n" --"PO-Revision-Date: 2014-09-12 10:36+0100\n" --"Last-Translator: Wolfgang Stoeggl \n" --"Language-Team: Deutsch \n" --"Language: de_DE\n" -+"Report-Msgid-Bugs-To: \n" -+"POT-Creation-Date: 2016-04-19 10:07+0530\n" - "MIME-Version: 1.0\n" - "Content-Type: text/plain; charset=UTF-8\n" - "Content-Transfer-Encoding: 8bit\n" -+"PO-Revision-Date: 2016-06-06 05:07+0000\n" -+"Last-Translator: lstemmle \n" -+"Language-Team: Deutsch \n" -+"Language: de\n" - "Plural-Forms: nplurals=2; plural=(n != 1);\n" --"X-Generator: Poedit 1.6.9\n" -+"X-Generator: Zanata 3.8.2\n" - - #: ../data/gnome-classic.desktop.in.h:1 - #: ../data/gnome-classic.session.desktop.in.in.h:1 -@@ -98,15 +97,15 @@ - msgid "Show only windows in the current workspace" - msgstr "Nur Fenster der aktuellen Arbeitsfläche anzeigen" - --#: ../extensions/apps-menu/extension.js:39 -+#: ../extensions/apps-menu/extension.js:40 - msgid "Activities Overview" - msgstr "Aktivitäten-Übersicht" - --#: ../extensions/apps-menu/extension.js:113 -+#: ../extensions/apps-menu/extension.js:115 - msgid "Favorites" - msgstr "Favoriten" - --#: ../extensions/apps-menu/extension.js:282 -+#: ../extensions/apps-menu/extension.js:291 - msgid "Applications" - msgstr "Anwendungen" - -@@ -145,15 +144,15 @@ - msgstr "Hinzufügen" - - #: ../extensions/drive-menu/extension.js:106 --#, javascript-format -+#, c-format - msgid "Ejecting drive '%s' failed:" - msgstr "Auswerfen von Laufwerk »%s« schlug fehl:" - --#: ../extensions/drive-menu/extension.js:123 -+#: ../extensions/drive-menu/extension.js:124 - msgid "Removable devices" - msgstr "Wechseldatenträger" - --#: ../extensions/drive-menu/extension.js:150 -+#: ../extensions/drive-menu/extension.js:149 - msgid "Open File" - msgstr "Datei öffnen" - -@@ -177,6 +176,8 @@ - msgid "Message" - msgstr "Nachricht" - -+#. TRANSLATORS: Example is the name of the extension, should not be -+#. translated - #: ../extensions/example/prefs.js:43 - msgid "" - "Example aims to show how to build well behaved extensions for the Shell and " -@@ -199,9 +200,8 @@ - msgstr "" - "Versuchen, mehr Bildschirmfläche zum Platzieren der Fenstervorschaubilder zu " - "verwenden, indem das Bildschirmseitenverhältnis berücksichtigt wird und " --"diese stärker zusammengelegt werden, um den umgebenden Rahmen zu " --"verkleinern. Diese Einstellung betrifft nur den natürlichen " --"Platzierungsalgorithmus." -+"diese stärker zusammengelegt werden, um den umgebenden Rahmen zu verkleinern." -+" Diese Einstellung betrifft nur den natürlichen Platzierungsalgorithmus." - - #: ../extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml.in.h:3 - msgid "Place window captions on top" -@@ -223,7 +223,7 @@ - msgstr "Orte" - - #: ../extensions/places-menu/placeDisplay.js:57 --#, javascript-format -+#, c-format - msgid "Failed to launch \"%s\"" - msgstr "Starten von »%s« fehlgeschlagen" - -@@ -248,6 +248,57 @@ - msgid "Memory" - msgstr "Speicher" - -+#: ../extensions/updates-dialog/extension.js:108 -+msgid "Important security updates need to be installed.\n" -+msgstr "Wichtige Sicherheits-Updates müssen installiert werden.\n" -+ -+#: ../extensions/updates-dialog/extension.js:110 -+#, c-format -+msgid "You can close this dialog and get %d minute to finish your work." -+msgid_plural "" -+"You can close this dialog and get %d minutes to finish your work." -+msgstr[0] "" -+"Sie können dieses Dialogfeld schließen und haben %d Minute, um Ihre Arbeit " -+"zu beenden." -+msgstr[1] "" -+"Sie können dieses Dialogfeld schließen und haben %d Minuten, um Ihre Arbeit " -+"zu beenden." -+ -+#: ../extensions/updates-dialog/extension.js:114 -+#, c-format -+msgid "You can close this dialog and get %d hour to finish your work." -+msgid_plural "You can close this dialog and get %d hours to finish your work." -+msgstr[0] "" -+"Sie können dieses Dialogfeld schließen und haben %d Stunde, um Ihre Arbeit " -+"zu beenden." -+msgstr[1] "" -+"Sie können dieses Dialogfeld schließen und haben %d Stunden, um Ihre Arbeit " -+"zu beenden." -+ -+#: ../extensions/updates-dialog/extension.js:145 -+msgid "Important security updates" -+msgstr "Wichtige Sicherheits-Updates" -+ -+#: ../extensions/updates-dialog/extension.js:163 -+#: ../extensions/window-list/extension.js:111 -+msgid "Close" -+msgstr "Schließen" -+ -+#: ../extensions/updates-dialog/extension.js:166 -+msgid "Restart & Install" -+msgstr "Neu starten & installieren" -+ -+#: ../extensions/updates-dialog/extension.js:217 -+msgid "Important security updates need to be installed now.\n" -+msgstr "Wichtige Sicherheits-Updates müssen jetzt installiert werden.\n" -+ -+#: ../extensions/updates-dialog/extension.js:218 -+#, c-format -+msgid "This computer will restart in %d second." -+msgid_plural "This computer will restart in %d seconds." -+msgstr[0] "Dieser Computer wird in %d Sekunde neu gestartet." -+msgstr[1] "Dieser Computer wird in %d Sekunden neu gestartet." -+ - #: ../extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml.in.h:1 - msgid "Theme name" - msgstr "Themenname" -@@ -258,52 +309,48 @@ - "Der Name des Themas, welches aus ~/.themes/name/gnome-shell geladen werden " - "soll" - --#: ../extensions/window-list/extension.js:110 --msgid "Close" --msgstr "Schließen" -- --#: ../extensions/window-list/extension.js:120 -+#: ../extensions/window-list/extension.js:121 - msgid "Unminimize" - msgstr "Minimieren rückgängig" - --#: ../extensions/window-list/extension.js:121 -+#: ../extensions/window-list/extension.js:122 - msgid "Minimize" - msgstr "Minimieren" - --#: ../extensions/window-list/extension.js:127 -+#: ../extensions/window-list/extension.js:128 - msgid "Unmaximize" - msgstr "Maximieren rückgängig" - --#: ../extensions/window-list/extension.js:128 -+#: ../extensions/window-list/extension.js:129 - msgid "Maximize" - msgstr "Maximieren" - --#: ../extensions/window-list/extension.js:300 -+#: ../extensions/window-list/extension.js:400 - msgid "Minimize all" - msgstr "Alle minimieren" - --#: ../extensions/window-list/extension.js:308 -+#: ../extensions/window-list/extension.js:408 - msgid "Unminimize all" - msgstr "Alle minimieren rückgängig" - --#: ../extensions/window-list/extension.js:316 -+#: ../extensions/window-list/extension.js:416 - msgid "Maximize all" - msgstr "Alle maximieren" - --#: ../extensions/window-list/extension.js:325 -+#: ../extensions/window-list/extension.js:425 - msgid "Unmaximize all" - msgstr "Alle maximieren rückgängig" - --#: ../extensions/window-list/extension.js:334 -+#: ../extensions/window-list/extension.js:434 - msgid "Close all" - msgstr "Alle schließen" - --#: ../extensions/window-list/extension.js:644 -+#: ../extensions/window-list/extension.js:718 - #: ../extensions/workspace-indicator/extension.js:30 - msgid "Workspace Indicator" - msgstr "Arbeitsflächenindikator" - --#: ../extensions/window-list/extension.js:808 -+#: ../extensions/window-list/extension.js:882 - msgid "Window List" - msgstr "Fensterliste" - -@@ -320,22 +367,38 @@ - "werden sollen. Mögliche Werte sind »never« (nie), »auto« (automatisch) und " - "»always« (immer)." - --#: ../extensions/window-list/prefs.js:30 -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:3 -+msgid "Show the window list on all monitors" -+msgstr "Fensterliste auf allen Monitoren zeigen" -+ -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:4 -+msgid "" -+"Whether to show the window list on all connected monitors or only on the " -+"primary one." -+msgstr "" -+"Legt fest, ob die Fensterliste auf allen angeschlossenen Monitoren oder nur " -+"auf dem Primärmonitor angezeigt werden soll." -+ -+#: ../extensions/window-list/prefs.js:32 - msgid "Window Grouping" - msgstr "Fenstergruppierung" - --#: ../extensions/window-list/prefs.js:49 -+#: ../extensions/window-list/prefs.js:50 - msgid "Never group windows" - msgstr "Fenster niemals gruppieren" - --#: ../extensions/window-list/prefs.js:50 -+#: ../extensions/window-list/prefs.js:51 - msgid "Group windows when space is limited" - msgstr "Fenster bei Platzmangel gruppieren" - --#: ../extensions/window-list/prefs.js:51 -+#: ../extensions/window-list/prefs.js:52 - msgid "Always group windows" - msgstr "Fenster immer gruppieren" - -+#: ../extensions/window-list/prefs.js:75 -+msgid "Show on all monitors" -+msgstr "Auf allen Monitoren anzeigen" -+ - #: ../extensions/workspace-indicator/prefs.js:141 - msgid "Workspace Names" - msgstr "Namen der Arbeitsflächen" -@@ -345,81 +408,6 @@ - msgstr "Name" - - #: ../extensions/workspace-indicator/prefs.js:198 --#, javascript-format -+#, c-format - msgid "Workspace %d" - msgstr "Arbeitsfläche %d" -- --#~ msgid "Suspend" --#~ msgstr "Bereitschaft" -- --#~ msgid "Hibernate" --#~ msgstr "Ruhezustand" -- --#~ msgid "Power Off" --#~ msgstr "Ausschalten …" -- --#~ msgid "Enable suspending" --#~ msgstr "Bereitschaft einblenden" -- --#~ msgid "Control the visibility of the Suspend menu item" --#~ msgstr "Die Sichtbarkeit des Menüeintrags »Bereitschaft« festlegen" -- --#~ msgid "Enable hibernating" --#~ msgstr "Ruhezustand einblenden" -- --#~ msgid "Control the visibility of the Hibernate menu item" --#~ msgstr "Die Sichtbarkeit des Menüeintrags »Ruhezustand« festlegen" -- --#~ msgid "Normal" --#~ msgstr "Normal" -- --#~ msgid "Left" --#~ msgstr "Links" -- --#~ msgid "Right" --#~ msgstr "Rechts" -- --#~ msgid "Upside-down" --#~ msgstr "Kopfüber" -- --#~ msgid "Display" --#~ msgstr "Anzeige" -- --#~ msgid "Display Settings" --#~ msgstr "Bildschirmeinstellungen" -- --#~ msgid "The application icon mode." --#~ msgstr "Der Modus des Anwendungssymbols." -- --#~ msgid "" --#~ "Configures how the windows are shown in the switcher. Valid possibilities " --#~ "are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-" --#~ "only' (shows only the application icon) or 'both'." --#~ msgstr "" --#~ "Legt fest, wie die Fenster im Fensterumschalter angezeigt werden. Gültige " --#~ "Werte sind »thumbnail-only« (zeigt ein Vorschaubild des Fensters), »app-" --#~ "icon-only« (zeigt das Anwendungssymbol) oder »both« (beides)." -- --#~ msgid "Devices" --#~ msgstr "Geräte" -- --#~ msgid "Bookmarks" --#~ msgstr "Lesezeichen" -- --#~ msgid "Network" --#~ msgstr "Netzwerk" -- --#~ msgid "File System" --#~ msgstr "Dateisystem" -- --#~ msgid "Drag here to add favorites" --#~ msgstr "Hierher ziehen, um zu Favoriten hinzuzufügen" -- --#~ msgid "New Window" --#~ msgstr "Neues Fenster" -- --#~ msgid "Quit Application" --#~ msgstr "Anwendung beenden" -- --#~ msgid "Remove from Favorites" --#~ msgstr "Aus Favoriten entfernen" ---- gnome-shell-extensions-3.14.4/po/es.po 2015-03-24 01:08:10.000000000 +0530 -+++ gnome-shell-extensions-3.14.4/po/es.po 2016-06-25 07:52:33.485142879 +0530 -@@ -1,26 +1,25 @@ --# Spanish translation for gnome-shell-extensions. --# Copyright (C) 2011 gnome-shell-extensions's COPYRIGHT HOLDER --# This file is distributed under the same license as the gnome-shell-extensions package. --# Jorge González , 2011. --# Nicolás Satragno , 2011. --# --# Daniel Mustieles , 2011, 2012. , 2013, 2014. --# -+# Spanish translation for gnome-shell-extensions. -+# Copyright (C) 2011 gnome-shell-extensions's COPYRIGHT HOLDER -+# This file is distributed under the same license as the gnome-shell-extensions package. -+# Jorge González , 2011. -+# Nicolás Satragno , 2011. -+# -+# Daniel Mustieles , 2011, 2012, 2013, 2014. -+# gguerrer , 2016. #zanata - msgid "" - msgstr "" - "Project-Id-Version: gnome-shell-extensions master\n" --"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-" --"shell&keywords=I18N+L10N&component=extensions\n" --"POT-Creation-Date: 2014-06-11 07:32+0000\n" --"PO-Revision-Date: 2014-06-12 17:24+0200\n" --"Last-Translator: Daniel Mustieles \n" --"Language-Team: Español; Castellano \n" --"Language: \n" -+"Report-Msgid-Bugs-To: \n" -+"POT-Creation-Date: 2016-04-19 10:07+0530\n" - "MIME-Version: 1.0\n" - "Content-Type: text/plain; charset=UTF-8\n" - "Content-Transfer-Encoding: 8bit\n" -+"PO-Revision-Date: 2016-05-16 05:07+0000\n" -+"Last-Translator: gguerrer \n" -+"Language-Team: Español; Castellano \n" -+"Language: es\n" - "Plural-Forms: nplurals=2; plural=(n != 1);\n" --"X-Generator: Gtranslator 2.91.6\n" -+"X-Generator: Zanata 3.8.2\n" - - #: ../data/gnome-classic.desktop.in.h:1 - #: ../data/gnome-classic.session.desktop.in.in.h:1 -@@ -48,14 +47,13 @@ - "This key overrides the key in org.gnome.mutter when running GNOME Shell." - msgstr "" - "Esta clave sobreescribe la clave en org.gnome.mutter al ejecutar GNOME Shell." -+"" - - #: ../data/org.gnome.shell.extensions.classic-overrides.gschema.xml.in.h:3 - msgid "Arrangement of buttons on the titlebar" - msgstr "Ordenación de los botones en la barra de título" - - #: ../data/org.gnome.shell.extensions.classic-overrides.gschema.xml.in.h:4 --#| msgid "" --#| "This key overrides the key in org.gnome.mutter when running GNOME Shell." - msgid "" - "This key overrides the key in org.gnome.desktop.wm.preferences when running " - "GNOME Shell." -@@ -98,15 +96,15 @@ - msgid "Show only windows in the current workspace" - msgstr "Mostrar ventanas solamente en el área de trabajo actual" - --#: ../extensions/apps-menu/extension.js:39 -+#: ../extensions/apps-menu/extension.js:40 - msgid "Activities Overview" - msgstr "Vista de actividades" - --#: ../extensions/apps-menu/extension.js:113 -+#: ../extensions/apps-menu/extension.js:115 - msgid "Favorites" - msgstr "Favoritos" - --#: ../extensions/apps-menu/extension.js:282 -+#: ../extensions/apps-menu/extension.js:291 - msgid "Applications" - msgstr "Aplicaciones" - -@@ -120,7 +118,8 @@ - "followed by a colon and the workspace number" - msgstr "" - "Una lista de cadenas, conteniendo cada una un ID de aplicación (nombre de " --"archivo de escritorio), seguido de dos puntos y el número del área de trabajo" -+"archivo de escritorio), seguido de dos puntos y el número del área de " -+"trabajo" - - #: ../extensions/auto-move-windows/prefs.js:60 - msgid "Application" -@@ -132,7 +131,6 @@ - msgstr "Área de trabajo" - - #: ../extensions/auto-move-windows/prefs.js:85 --#| msgid "Add rule" - msgid "Add Rule" - msgstr "Añadir regla" - -@@ -145,15 +143,15 @@ - msgstr "Añadir" - - #: ../extensions/drive-menu/extension.js:106 --#, javascript-format -+#, c-format - msgid "Ejecting drive '%s' failed:" - msgstr "Falló al expulsar el dispositivo «%s»" - --#: ../extensions/drive-menu/extension.js:123 -+#: ../extensions/drive-menu/extension.js:124 - msgid "Removable devices" - msgstr "Dispositivos extraíbles" - --#: ../extensions/drive-menu/extension.js:150 -+#: ../extensions/drive-menu/extension.js:149 - msgid "Open File" - msgstr "Abrir archivo" - -@@ -174,10 +172,11 @@ - "panel." - - #: ../extensions/example/prefs.js:30 --#| msgid "Message:" - msgid "Message" - msgstr "Mensaje" - -+#. TRANSLATORS: Example is the name of the extension, should not be -+#. translated - #: ../extensions/example/prefs.js:43 - msgid "" - "Example aims to show how to build well behaved extensions for the Shell and " -@@ -185,8 +184,7 @@ - "Nevertheless it's possible to customize the greeting message." - msgstr "" - "«Example» tiene por objeto mostrar cómo construir extensiones de buen " --"comportamiento para la Shell y por eso tiene poca funcionalidad por sí " --"solo.\n" -+"comportamiento para la Shell y por eso tiene poca funcionalidad por sí solo.\n" - "Sin embargo, es posible personalizar el mensaje de bienvenida." - - #: ../extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml.in.h:1 -@@ -224,7 +222,7 @@ - msgstr "Lugares" - - #: ../extensions/places-menu/placeDisplay.js:57 --#, javascript-format -+#, c-format - msgid "Failed to launch \"%s\"" - msgstr "Falló al lanzar «%s»" - -@@ -249,6 +247,53 @@ - msgid "Memory" - msgstr "Memoria" - -+#: ../extensions/updates-dialog/extension.js:108 -+msgid "Important security updates need to be installed.\n" -+msgstr "Debe instalar importantes actualizaciones de seguridad.\n" -+ -+#: ../extensions/updates-dialog/extension.js:110 -+#, c-format -+msgid "You can close this dialog and get %d minute to finish your work." -+msgid_plural "" -+"You can close this dialog and get %d minutes to finish your work." -+msgstr[0] "" -+"Cierre este cuadro de diálogo y dedique %d minuto para terminar su trabajo." -+msgstr[1] "" -+"Cierre este cuadro de diálogo y dedique %d minutos para terminar su trabajo." -+ -+#: ../extensions/updates-dialog/extension.js:114 -+#, c-format -+msgid "You can close this dialog and get %d hour to finish your work." -+msgid_plural "You can close this dialog and get %d hours to finish your work." -+msgstr[0] "" -+"Cierre este cuadro de diálogo y dedique %d hora para terminar su trabajo." -+msgstr[1] "" -+"Cierre este cuadro de diálogo y dedique %d horas para terminar su trabajo." -+ -+#: ../extensions/updates-dialog/extension.js:145 -+msgid "Important security updates" -+msgstr "Importantes actualizaciones de seguridad" -+ -+#: ../extensions/updates-dialog/extension.js:163 -+#: ../extensions/window-list/extension.js:111 -+msgid "Close" -+msgstr "Cerrar" -+ -+#: ../extensions/updates-dialog/extension.js:166 -+msgid "Restart & Install" -+msgstr "Reiniciar e instalar" -+ -+#: ../extensions/updates-dialog/extension.js:217 -+msgid "Important security updates need to be installed now.\n" -+msgstr "Debe instalar importantes actualizaciones de seguridad ahora.\n" -+ -+#: ../extensions/updates-dialog/extension.js:218 -+#, c-format -+msgid "This computer will restart in %d second." -+msgid_plural "This computer will restart in %d seconds." -+msgstr[0] "El equipo se reiniciará en %d segundo." -+msgstr[1] "El equipo se reiniciará en %d segundos." -+ - #: ../extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml.in.h:1 - msgid "Theme name" - msgstr "Nombre del tema" -@@ -257,52 +302,48 @@ - msgid "The name of the theme, to be loaded from ~/.themes/name/gnome-shell" - msgstr "El nombre del tema, que se carga desde ~/.themes/nombre/gnome-shell" - --#: ../extensions/window-list/extension.js:110 --msgid "Close" --msgstr "Cerrar" -- --#: ../extensions/window-list/extension.js:120 -+#: ../extensions/window-list/extension.js:121 - msgid "Unminimize" - msgstr "Desminimizar" - --#: ../extensions/window-list/extension.js:121 -+#: ../extensions/window-list/extension.js:122 - msgid "Minimize" - msgstr "Minimizar" - --#: ../extensions/window-list/extension.js:127 -+#: ../extensions/window-list/extension.js:128 - msgid "Unmaximize" - msgstr "Desmaximizar" - --#: ../extensions/window-list/extension.js:128 -+#: ../extensions/window-list/extension.js:129 - msgid "Maximize" - msgstr "Maximizar" - --#: ../extensions/window-list/extension.js:300 -+#: ../extensions/window-list/extension.js:400 - msgid "Minimize all" - msgstr "Minimizar todo" - --#: ../extensions/window-list/extension.js:308 -+#: ../extensions/window-list/extension.js:408 - msgid "Unminimize all" - msgstr "Desminimizar todo" - --#: ../extensions/window-list/extension.js:316 -+#: ../extensions/window-list/extension.js:416 - msgid "Maximize all" - msgstr "Maximizar todo" - --#: ../extensions/window-list/extension.js:325 -+#: ../extensions/window-list/extension.js:425 - msgid "Unmaximize all" - msgstr "Desmaximizar todo" - --#: ../extensions/window-list/extension.js:334 -+#: ../extensions/window-list/extension.js:434 - msgid "Close all" - msgstr "Cerrar todo" - --#: ../extensions/window-list/extension.js:644 -+#: ../extensions/window-list/extension.js:718 - #: ../extensions/workspace-indicator/extension.js:30 - msgid "Workspace Indicator" - msgstr "Indicador de área de trabajo" - --#: ../extensions/window-list/extension.js:798 -+#: ../extensions/window-list/extension.js:882 - msgid "Window List" - msgstr "Lista de ventanas" - -@@ -318,24 +359,39 @@ - "Decide cuándo agrupar ventanas para la misma aplicación en la lista de " - "ventanas. Los valores posibles son «never», «auto» y «always»." - --#: ../extensions/window-list/prefs.js:30 -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:3 -+msgid "Show the window list on all monitors" -+msgstr "Mostrar la lista de ventana en todos los monitores" -+ -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:4 -+msgid "" -+"Whether to show the window list on all connected monitors or only on the " -+"primary one." -+msgstr "" -+"Si muestra la lista de ventana en todos los monitores o únicamente en el " -+"principal." -+ -+#: ../extensions/window-list/prefs.js:32 - msgid "Window Grouping" - msgstr "Agrupación de ventanas" - --#: ../extensions/window-list/prefs.js:49 -+#: ../extensions/window-list/prefs.js:50 - msgid "Never group windows" - msgstr "Nunca agrupar las ventanas" - --#: ../extensions/window-list/prefs.js:50 -+#: ../extensions/window-list/prefs.js:51 - msgid "Group windows when space is limited" - msgstr "Agrupar las ventanas cuando el espacio esté limitado" - --#: ../extensions/window-list/prefs.js:51 -+#: ../extensions/window-list/prefs.js:52 - msgid "Always group windows" - msgstr "Siempre agrupar las ventanas" - -+#: ../extensions/window-list/prefs.js:75 -+msgid "Show on all monitors" -+msgstr "Mostrar " -+ - #: ../extensions/workspace-indicator/prefs.js:141 --#| msgid "Workspace names:" - msgid "Workspace Names" - msgstr "Nombres de los áreas de trabajo" - -@@ -344,302 +400,6 @@ - msgstr "Nombre" - - #: ../extensions/workspace-indicator/prefs.js:198 --#, javascript-format -+#, c-format - msgid "Workspace %d" - msgstr "Área de trabajo %d" -- --#~ msgid "Suspend" --#~ msgstr "Suspender" -- --#~ msgid "Hibernate" --#~ msgstr "Hibernar" -- --#~ msgid "Power Off" --#~ msgstr "Apagar" -- --#~ msgid "Enable suspending" --#~ msgstr "Activar la suspensión" -- --#~ msgid "Control the visibility of the Suspend menu item" --#~ msgstr "Controla la visibilidad del elemento de menú «Suspender»" -- --#~ msgid "Enable hibernating" --#~ msgstr "Activar la hibernación" -- --#~ msgid "Control the visibility of the Hibernate menu item" --#~ msgstr "Controla la visibilidad del elemento de menú «Hibernar»" -- --#~ msgid "Normal" --#~ msgstr "Normal" -- --#~ msgid "Left" --#~ msgstr "Izquierda" -- --#~ msgid "Right" --#~ msgstr "Derecha" -- --#~ msgid "Upside-down" --#~ msgstr "Hacia abajo" -- --#~ msgid "Display" --#~ msgstr "Pantalla" -- --#~ msgid "Display Settings" --#~ msgstr "Configuración de pantalla" -- --#~ msgid "File System" --#~ msgstr "Sistema de archivos" -- --#~ msgid "The application icon mode." --#~ msgstr "El modo de icono de la aplicación." -- --#~ msgid "" --#~ "Configures how the windows are shown in the switcher. Valid possibilities " --#~ "are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-" --#~ "only' (shows only the application icon) or 'both'." --#~ msgstr "" --#~ "Configura cómo se muestran las ventanas en el intercambiador. Las " --#~ "opciones posibles son «thumbnail-only» (muestra una miniatura de la " --#~ "ventana, «app-icon-only» (sólo muestra el icono de la aplicación) o " --#~ "«both» (se muestran ambas cosas)." -- --#~ msgid "Devices" --#~ msgstr "Dispositivos" -- --#~ msgid "Bookmarks" --#~ msgstr "Marcadores" -- --#~ msgid "Network" --#~ msgstr "Red" -- --#~ msgid "Drag here to add favorites" --#~ msgstr "Arrastrar aquí para añadir a favoritos" -- --#~ msgid "New Window" --#~ msgstr "Ventana nueva" -- --#~ msgid "Quit Application" --#~ msgstr "Salir de la aplicación" -- --#~ msgid "Remove from Favorites" --#~ msgstr "Quitar de favoritos" -- --#~ msgid "Position of the dock" --#~ msgstr "Posición del tablero" -- --#~ msgid "" --#~ "Sets the position of the dock in the screen. Allowed values are 'right' " --#~ "or 'left'" --#~ msgstr "" --#~ "Configura la posición del tablero en la pantalla. Los valores permitidos " --#~ "son «right» (derecha) o «left» (izquierda)" -- --#~ msgid "Icon size" --#~ msgstr "Tamaño del icono" -- --#~ msgid "Sets icon size of the dock." --#~ msgstr "Configura el tamaño de los íconos del tablero." -- --#~ msgid "Enable/disable autohide" --#~ msgstr "Activar/desactivar ocultación automática" -- --#~ msgid "Autohide effect" --#~ msgstr "Efecto de ocultación automática" -- --#~ msgid "" --#~ "Sets the effect of the hide dock. Allowed values are 'resize', 'rescale' " --#~ "and 'move'" --#~ msgstr "" --#~ "Establece el efecto de ocultación del tablero. Los valores permitidos son " --#~ "«resize» (redimensionar) y «rescale» (re-escalar) y «move» (mover)" -- --#~ msgid "Autohide duration" --#~ msgstr "Duración de la ocultación automática" -- --#~ msgid "Sets the time duration of the autohide effect." --#~ msgstr "Configura la duración del efecto de ocultación automática." -- --#~ msgid "Monitor" --#~ msgstr "Pantalla" -- --#~ msgid "" --#~ "Sets monitor to display dock in. The default value (-1) is the primary " --#~ "monitor." --#~ msgstr "" --#~ "Establece la pantalla en la que mostrar el tablero. El valor " --#~ "predeterminado es (-1), que es la pantalla principal." -- --#~ msgid "%s is away." --#~ msgstr "%s no está disponible." -- --#~ msgid "%s is offline." --#~ msgstr "%s está desconectado." -- --#~ msgid "%s is online." --#~ msgstr "%s está conectado." -- --#~ msgid "%s is busy." --#~ msgstr "%s está ocupado." -- --#~ msgid "Removable Devices" --#~ msgstr "Dispositivos extraíbles" -- --#~ msgid "Configure display settings..." --#~ msgstr "Configurar las opciones de pantalla…" -- --#~ msgid "The alt tab behaviour." --#~ msgstr "El comportamiento de Alt+Tab." -- --#~ msgid "" --#~ "Sets the Alt-Tab behaviour. Possible values are: all_thumbnails and " --#~ "workspace_icons. See the configuration dialogs for details." --#~ msgstr "" --#~ "Establece el comportamiento de Alt+Tab. Los valores posibles son: " --#~ "«native» (nativo), «all_thumbnails» (todo y miniaturas) y " --#~ "«workspace_icons» (iconos de áreas de trabajo). Para obtener información " --#~ "más detallada, consulte la configuración de los diálogos " -- --#~ msgid "" --#~ "This mode presents all applications from all workspaces in one selection " --#~ "list. Instead of using the application icon of every window, it uses " --#~ "small thumbnails resembling the window itself." --#~ msgstr "" --#~ "Este modo presenta todas las aplicaciones de todas las áreas de trabajo " --#~ "en una lista de selección. En lugar de usar el icono de aplicación de " --#~ "cada ventana, usa pequeñas miniaturas que se asemejan a la propia ventana." -- --#~ msgid "Workspace & Icons" --#~ msgstr "Área de trabajo e iconos" -- --#~| msgid "" --#~| "This mode let's you switch between the applications of your current " --#~| "workspace and gives you additionally the option to switch to the last " --#~| "used application of your previous workspace. This is always the last " --#~| "symbol in the list and is segregated by a separator/vertical line if " --#~| "available. \n" --#~| "Every window is represented by its application icon." --#~ msgid "" --#~ "This mode lets you switch between the applications of your current " --#~ "workspace and gives you additionally the option to switch to the last " --#~ "used application of your previous workspace. This is always the last " --#~ "symbol in the list and is separated by a separator/vertical line if " --#~ "available. \n" --#~ "Every window is represented by its application icon." --#~ msgstr "" --#~ "Este modo le permite alternar entre las aplicaciones de su área de " --#~ "trabajo actual y le da la opción de cambiar a la última aplicación " --#~ "utilizada de su área de trabajo anterior. Este siempre es el último " --#~ "símbolo de la lista y está separado por un separador/línea vertical si " --#~ "está disponible.\n" --#~ "Cada ventana está representada por su icono de aplicación." -- --#~ msgid "Move current selection to front before closing the popup" --#~ msgstr "" --#~ "Mover la selección actual al frente antes de cerrar la ventana emergente" -- --#~ msgid "" --#~ "The Alternate Tab can be used in different modes, that affect the way " --#~ "windows are chosen and presented." --#~ msgstr "" --#~ "La combinación de teclas Alt+Tab se puede usar en diferentes modos, que " --#~ "afectan la manera en que se eligen y presentan las ventanas." -- --#~ msgid "Indicates if Alternate Tab is newly installed" --#~ msgstr "Indica si se ha instalado Alt+Tab recientemente" -- --#~ msgid "Ask the user for a default behaviour if true." --#~ msgstr "" --#~ "Si es cierto, preguntar al usuario el comportamiento predeterminado." -- --#~ msgid "" --#~ "This is the first time you use the Alternate Tab extension. \n" --#~ "Please choose your preferred behaviour:\n" --#~ "\n" --#~ "All & Thumbnails:\n" --#~ " This mode presents all applications from all workspaces in one " --#~ "selection \n" --#~ " list. Instead of using the application icon of every window, it uses " --#~ "small \n" --#~ " thumbnails resembling the window itself. \n" --#~ "\n" --#~ "Workspace & Icons:\n" --#~ " This mode let's you switch between the applications of your current \n" --#~ " workspace and gives you additionally the option to switch to the last " --#~ "used \n" --#~ " application of your previous workspace. This is always the last " --#~ "symbol in \n" --#~ " the list and is segregated by a separator/vertical line if " --#~ "available. \n" --#~ " Every window is represented by its application icon. \n" --#~ "\n" --#~ "If you whish to revert to the default behavior for the Alt-Tab switcher, " --#~ "just\n" --#~ "disable the extension from extensions.gnome.org or the Advanced Settings " --#~ "application." --#~ msgstr "" --#~ "Esta es la primera vez que usa la extensión Alt+Tab. \n" --#~ "Seleccione el comportamiento preferido:\n" --#~ "\n" --#~ "Todo y miniaturas:\n" --#~ " Este modo muestra todas la aplicaciones de todas las áreas de " --#~ "trabajo \n" --#~ " en una única lista. En lugar de usar el icono de la aplicación de " --#~ "cada ventana, usa \n" --#~ " miniaturas similares a la ventana.\n" --#~ "\n" --#~ "Área de trabajo e iconos:\n" --#~ " Este modo permite le alternar entre aplicaciones del área de trabajo " --#~ "actual, \n" --#~ " y ofrece la posibilidad de cambiar a la última aplicación usada en " --#~ "el \n" --#~ " área de trabajo anterior. Éste último es siempre el último símbolo en " --#~ "la \n" --#~ " lista, y está diferenciado mediante un separador/línea vertical, si " --#~ "está \n" --#~ " disponible. \n" --#~ " Cada ventana se representa con el icono de la aplicación. \n" --#~ "\n" --#~ "Si quiere volver al comportamiento predeterminado de Alt+Tab, " --#~ "simplemente\n" --#~ "desactive la extensión desde extensions.gnome.org o en la aplicación " --#~ "Configuración avanzada." -- --#~ msgid "Alt Tab Behaviour" --#~ msgstr "Comportamiento de Alt+Tab" -- --#~ msgid "Cancel" --#~ msgstr "Cancelar" -- --#~ msgid "Notifications" --#~ msgstr "Notificaciones" -- --#~ msgid "Online Accounts" --#~ msgstr "Cuentas en línea" -- --#~ msgid "Lock Screen" --#~ msgstr "Bloquear la pantalla" -- --#~ msgid "Switch User" --#~ msgstr "Cambiar de usuario" -- --#~ msgid "Log Out..." --#~ msgstr "Cerrar la sesión…" -- --#~ msgid "" --#~ "The algorithm used to layout thumbnails in the overview. 'grid' to use " --#~ "the default grid based algorithm, 'natural' to use another one that " --#~ "reflects more the position and size of the actual window" --#~ msgstr "" --#~ "El algoritmo usado para situar las miniaturas en la vista previa. " --#~ "«grid» (tabla) para usar el algoritmo predeterminado basado en una tabla, " --#~ "«natural» para usar otro que refleja mejor la posición y tamaño de la " --#~ "ventana representada." -- --#~ msgid "Window placement strategy" --#~ msgstr "Estrategia de ubicación de ventanas" -- --#~ msgid "Available" --#~ msgstr "Disponible" -- --#~ msgid "Busy" --#~ msgstr "Ocupado" ---- gnome-shell-extensions-3.14.4/po/fr.po 2015-03-24 01:08:10.000000000 +0530 -+++ gnome-shell-extensions-3.14.4/po/fr.po 2016-06-28 11:59:16.574344356 +0530 -@@ -3,21 +3,21 @@ - # This file is distributed under the same license as the gnome-shell-extensions package. - # Claude Paroz , 2011. - # Alain Lojewski , 2012-2013. --# -+# Corina Roe , 2016. #zanata - msgid "" - msgstr "" - "Project-Id-Version: gnome-shell-extensions master\n" --"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-" --"shell&keywords=I18N+L10N&component=extensions\n" --"POT-Creation-Date: 2014-08-25 19:37+0000\n" --"PO-Revision-Date: 2014-08-25 23:50+0200\n" --"Last-Translator: Alain Lojewski \n" --"Language-Team: GNOME French Team \n" --"Language: \n" -+"Report-Msgid-Bugs-To: \n" -+"POT-Creation-Date: 2016-04-19 10:07+0530\n" - "MIME-Version: 1.0\n" - "Content-Type: text/plain; charset=UTF-8\n" - "Content-Transfer-Encoding: 8bit\n" -+"PO-Revision-Date: 2016-05-19 04:58+0000\n" -+"Last-Translator: Corina Roe \n" -+"Language-Team: GNOME French Team \n" -+"Language: fr\n" - "Plural-Forms: nplurals=2; plural=(n > 1);\n" -+"X-Generator: Zanata 3.8.2\n" - - #: ../data/gnome-classic.desktop.in.h:1 - #: ../data/gnome-classic.session.desktop.in.in.h:1 -@@ -56,8 +56,8 @@ - "This key overrides the key in org.gnome.desktop.wm.preferences when running " - "GNOME Shell." - msgstr "" --"Cette clé remplace la clé dans org.gnome.desktop.wm.preferences lorsque GNOME Shell est en " --"cours d'exécution." -+"Cette clé remplace la clé dans org.gnome.desktop.wm.preferences lorsque " -+"GNOME Shell est en cours d'exécution." - - #: ../data/org.gnome.shell.extensions.classic-overrides.gschema.xml.in.h:5 - msgid "Enable edge tiling when dropping windows on screen edges" -@@ -95,15 +95,15 @@ - msgid "Show only windows in the current workspace" - msgstr "N'afficher les fenêtres que sur l'espace de travail actuel" - --#: ../extensions/apps-menu/extension.js:39 -+#: ../extensions/apps-menu/extension.js:40 - msgid "Activities Overview" - msgstr "Vue d'ensemble des activités" - --#: ../extensions/apps-menu/extension.js:113 -+#: ../extensions/apps-menu/extension.js:115 - msgid "Favorites" - msgstr "Favoris" - --#: ../extensions/apps-menu/extension.js:282 -+#: ../extensions/apps-menu/extension.js:291 - msgid "Applications" - msgstr "Applications" - -@@ -142,15 +142,15 @@ - msgstr "Ajouter" - - #: ../extensions/drive-menu/extension.js:106 --#, javascript-format -+#, c-format - msgid "Ejecting drive '%s' failed:" - msgstr "L'éjection du disque « %s » a échoué :" - --#: ../extensions/drive-menu/extension.js:123 -+#: ../extensions/drive-menu/extension.js:124 - msgid "Removable devices" - msgstr "Périphériques amovibles" - --#: ../extensions/drive-menu/extension.js:150 -+#: ../extensions/drive-menu/extension.js:149 - msgid "Open File" - msgstr "Ouvrir le fichier" - -@@ -174,6 +174,8 @@ - msgid "Message" - msgstr "Message" - -+#. TRANSLATORS: Example is the name of the extension, should not be -+#. translated - #: ../extensions/example/prefs.js:43 - msgid "" - "Example aims to show how to build well behaved extensions for the Shell and " -@@ -220,7 +222,7 @@ - msgstr "Emplacements" - - #: ../extensions/places-menu/placeDisplay.js:57 --#, javascript-format -+#, c-format - msgid "Failed to launch \"%s\"" - msgstr "Impossible de lancer « %s »" - -@@ -245,6 +247,59 @@ - msgid "Memory" - msgstr "Mémoire" - -+#: ../extensions/updates-dialog/extension.js:108 -+msgid "Important security updates need to be installed.\n" -+msgstr "Mises à jour de sécurité importantes ayant besoin d'être installées.\n" -+ -+#: ../extensions/updates-dialog/extension.js:110 -+#, c-format -+msgid "You can close this dialog and get %d minute to finish your work." -+msgid_plural "" -+"You can close this dialog and get %d minutes to finish your work." -+msgstr[0] "" -+"Vous pouvez fermer ce dialogue et obtenir %d minutes pour terminer votre " -+"tâche." -+msgstr[1] "" -+"Vous pouvez fermer ce dialogue et obtenir %d minutes pour terminer votre " -+"tâche." -+ -+#: ../extensions/updates-dialog/extension.js:114 -+#, c-format -+msgid "You can close this dialog and get %d hour to finish your work." -+msgid_plural "You can close this dialog and get %d hours to finish your work." -+msgstr[0] "" -+"Vous pouvez fermer ce dialogue et obtenir %d heures pour terminer votre " -+"tâche." -+msgstr[1] "" -+"Vous pouvez fermer ce dialogue et obtenir %d heures pour terminer votre " -+"tâche." -+ -+#: ../extensions/updates-dialog/extension.js:145 -+msgid "Important security updates" -+msgstr "Mises à jour de sécurité importantes" -+ -+#: ../extensions/updates-dialog/extension.js:163 -+#: ../extensions/window-list/extension.js:111 -+msgid "Close" -+msgstr "Fermer" -+ -+#: ../extensions/updates-dialog/extension.js:166 -+msgid "Restart & Install" -+msgstr "Redémarrer et installer" -+ -+#: ../extensions/updates-dialog/extension.js:217 -+msgid "Important security updates need to be installed now.\n" -+msgstr "" -+"Mises à jour de sécurité importantes ayant besoin d'être installées " -+"maintenant.\n" -+ -+#: ../extensions/updates-dialog/extension.js:218 -+#, c-format -+msgid "This computer will restart in %d second." -+msgid_plural "This computer will restart in %d seconds." -+msgstr[0] "Cet ordinateur redémarrera automatiquement dans %d secondes." -+msgstr[1] "Cet ordinateur redémarrera automatiquement dans %d secondes." -+ - #: ../extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml.in.h:1 - msgid "Theme name" - msgstr "Nom du thème" -@@ -253,52 +308,48 @@ - msgid "The name of the theme, to be loaded from ~/.themes/name/gnome-shell" - msgstr "Le nom du thème, à charger à partir de ~/.themes/name/gnome-shell" - --#: ../extensions/window-list/extension.js:110 --msgid "Close" --msgstr "Fermer" -- --#: ../extensions/window-list/extension.js:120 -+#: ../extensions/window-list/extension.js:121 - msgid "Unminimize" - msgstr "Restaurer" - --#: ../extensions/window-list/extension.js:121 -+#: ../extensions/window-list/extension.js:122 - msgid "Minimize" - msgstr "Réduire" - --#: ../extensions/window-list/extension.js:127 -+#: ../extensions/window-list/extension.js:128 - msgid "Unmaximize" - msgstr "Restaurer" - --#: ../extensions/window-list/extension.js:128 -+#: ../extensions/window-list/extension.js:129 - msgid "Maximize" - msgstr "Maximiser" - --#: ../extensions/window-list/extension.js:300 -+#: ../extensions/window-list/extension.js:400 - msgid "Minimize all" - msgstr "Réduire tout" - --#: ../extensions/window-list/extension.js:308 -+#: ../extensions/window-list/extension.js:408 - msgid "Unminimize all" - msgstr "Restaurer tout" - --#: ../extensions/window-list/extension.js:316 -+#: ../extensions/window-list/extension.js:416 - msgid "Maximize all" - msgstr "Maximiser tout" - --#: ../extensions/window-list/extension.js:325 -+#: ../extensions/window-list/extension.js:425 - msgid "Unmaximize all" - msgstr "Restaurer tout" - --#: ../extensions/window-list/extension.js:334 -+#: ../extensions/window-list/extension.js:434 - msgid "Close all" - msgstr "Fermer tout" - --#: ../extensions/window-list/extension.js:644 -+#: ../extensions/window-list/extension.js:718 - #: ../extensions/workspace-indicator/extension.js:30 - msgid "Workspace Indicator" - msgstr "Indicateur d'espace de travail" - --#: ../extensions/window-list/extension.js:798 -+#: ../extensions/window-list/extension.js:882 - msgid "Window List" - msgstr "Liste de fenêtres" - -@@ -315,22 +366,38 @@ - "fenêtres. Les valeurs possibles sont « never » (jamais), « auto » et " - "« always » (toujours)." - --#: ../extensions/window-list/prefs.js:30 -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:3 -+msgid "Show the window list on all monitors" -+msgstr "Afficher la liste de tous les moniteurs dans la fenêtre" -+ -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:4 -+msgid "" -+"Whether to show the window list on all connected monitors or only on the " -+"primary one." -+msgstr "" -+"Indique s'il faut afficher la liste de tous les moniteurs connectés ou " -+"uniquement le principal dans la fenêtre." -+ -+#: ../extensions/window-list/prefs.js:32 - msgid "Window Grouping" - msgstr "Regroupement de fenêtres" - --#: ../extensions/window-list/prefs.js:49 -+#: ../extensions/window-list/prefs.js:50 - msgid "Never group windows" - msgstr "Ne jamais regrouper les fenêtres" - --#: ../extensions/window-list/prefs.js:50 -+#: ../extensions/window-list/prefs.js:51 - msgid "Group windows when space is limited" - msgstr "Regrouper les fenêtres quand l'espace est limité" - --#: ../extensions/window-list/prefs.js:51 -+#: ../extensions/window-list/prefs.js:52 - msgid "Always group windows" - msgstr "Toujours regrouper les fenêtres" - -+#: ../extensions/window-list/prefs.js:75 -+msgid "Show on all monitors" -+msgstr "Afficher tous les moniteurs" -+ - #: ../extensions/workspace-indicator/prefs.js:141 - msgid "Workspace Names" - msgstr "Noms des espaces de travail" -@@ -340,12 +407,6 @@ - msgstr "Nom" - - #: ../extensions/workspace-indicator/prefs.js:198 --#, javascript-format -+#, c-format - msgid "Workspace %d" - msgstr "Espace de travail %d" -- --#~ msgid "Control the visibility of the Suspend menu item" --#~ msgstr "Gère la visibilité de l'élément de menu « mise en veille »" -- --#~ msgid "Control the visibility of the Hibernate menu item" --#~ msgstr "Gère la visibilité de l'élément de menu « hibernation »" ---- gnome-shell-extensions-3.14.4/po/it.po 2015-03-24 01:08:10.000000000 +0530 -+++ gnome-shell-extensions-3.14.4/po/it.po 2016-06-25 07:52:33.490142879 +0530 -@@ -4,22 +4,21 @@ - # This file is distributed under the same license as the gnome-shell-extensions package. - # Giovanni Campagna , 2011 - # Milo Casagrande , 2013, 2014. --# -+# tchuang , 2016. #zanata - msgid "" - msgstr "" - "Project-Id-Version: gnome-shell-extensions\n" --"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-" --"shell&keywords=I18N+L10N&component=extensions\n" --"POT-Creation-Date: 2014-10-05 07:43+0000\n" --"PO-Revision-Date: 2014-10-05 15:19+0100\n" --"Last-Translator: Milo Casagrande \n" --"Language-Team: Italian \n" --"Language: it\n" -+"Report-Msgid-Bugs-To: \n" -+"POT-Creation-Date: 2016-04-19 10:07+0530\n" - "MIME-Version: 1.0\n" - "Content-Type: text/plain; charset=UTF-8\n" - "Content-Transfer-Encoding: 8bit\n" -+"PO-Revision-Date: 2014-10-05 02:19+0000\n" -+"Last-Translator: Milo Casagrande \n" -+"Language-Team: Italian \n" -+"Language: it\n" - "Plural-Forms: nplurals=2; plural=(n!=1);\n" --"X-Generator: Poedit 1.6.9\n" -+"X-Generator: Zanata 3.8.2\n" - - #: ../data/gnome-classic.desktop.in.h:1 - #: ../data/gnome-classic.session.desktop.in.in.h:1 -@@ -98,15 +97,15 @@ - msgid "Show only windows in the current workspace" - msgstr "Mostra solo le finestre dello spazio di lavoro corrente" - --#: ../extensions/apps-menu/extension.js:39 -+#: ../extensions/apps-menu/extension.js:40 - msgid "Activities Overview" - msgstr "Panoramica attività" - --#: ../extensions/apps-menu/extension.js:113 -+#: ../extensions/apps-menu/extension.js:115 - msgid "Favorites" - msgstr "Preferiti" - --#: ../extensions/apps-menu/extension.js:282 -+#: ../extensions/apps-menu/extension.js:291 - msgid "Applications" - msgstr "Applicazioni" - -@@ -144,15 +143,15 @@ - msgstr "Aggiungi" - - #: ../extensions/drive-menu/extension.js:106 --#, javascript-format -+#, c-format - msgid "Ejecting drive '%s' failed:" - msgstr "Espulsione dell'unità «%s» non riuscita:" - --#: ../extensions/drive-menu/extension.js:123 -+#: ../extensions/drive-menu/extension.js:124 - msgid "Removable devices" - msgstr "Dispositivi rimovibili" - --#: ../extensions/drive-menu/extension.js:150 -+#: ../extensions/drive-menu/extension.js:149 - msgid "Open File" - msgstr "Apri file" - -@@ -176,6 +175,8 @@ - msgid "Message" - msgstr "Messaggio" - -+#. TRANSLATORS: Example is the name of the extension, should not be -+#. translated - #: ../extensions/example/prefs.js:43 - msgid "" - "Example aims to show how to build well behaved extensions for the Shell and " -@@ -221,7 +222,7 @@ - msgstr "Posizioni" - - #: ../extensions/places-menu/placeDisplay.js:57 --#, javascript-format -+#, c-format - msgid "Failed to launch \"%s\"" - msgstr "Avvio di «%s» non riuscito" - -@@ -246,6 +247,57 @@ - msgid "Memory" - msgstr "Memoria" - -+#: ../extensions/updates-dialog/extension.js:108 -+msgid "Important security updates need to be installed.\n" -+msgstr "Devono essere installati importanti aggiornamenti di sicurezza.\n" -+ -+#: ../extensions/updates-dialog/extension.js:110 -+#, c-format -+msgid "You can close this dialog and get %d minute to finish your work." -+msgid_plural "" -+"You can close this dialog and get %d minutes to finish your work." -+msgstr[0] "" -+"È possibile chiudere questa finestra di dialogo e impiegare %d minuto per " -+"terminare il lavoro." -+msgstr[1] "" -+"È possibile chiudere questa finestra di dialogo e impiegare %d minuti per " -+"terminare il lavoro." -+ -+#: ../extensions/updates-dialog/extension.js:114 -+#, c-format -+msgid "You can close this dialog and get %d hour to finish your work." -+msgid_plural "You can close this dialog and get %d hours to finish your work." -+msgstr[0] "" -+"È possibile chiudere questa finestra di dialogo e impiegare %d ora per " -+"terminare il lavoro." -+msgstr[1] "" -+"È possibile chiudere questa finestra di dialogo e impiegare %d ore per " -+"terminare il lavoro." -+ -+#: ../extensions/updates-dialog/extension.js:145 -+msgid "Important security updates" -+msgstr "Aggiornamenti di sicurezza importanti" -+ -+#: ../extensions/updates-dialog/extension.js:163 -+#: ../extensions/window-list/extension.js:111 -+msgid "Close" -+msgstr "Chiudi" -+ -+#: ../extensions/updates-dialog/extension.js:166 -+msgid "Restart & Install" -+msgstr "Riavvia e installa" -+ -+#: ../extensions/updates-dialog/extension.js:217 -+msgid "Important security updates need to be installed now.\n" -+msgstr "Devono essere installati ora importanti aggiornamenti di sicurezza.\n" -+ -+#: ../extensions/updates-dialog/extension.js:218 -+#, c-format -+msgid "This computer will restart in %d second." -+msgid_plural "This computer will restart in %d seconds." -+msgstr[0] "Il computer verrà riavviato entro %d secondo." -+msgstr[1] "Il computer verrà riavviato entro %d secondi." -+ - #: ../extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml.in.h:1 - msgid "Theme name" - msgstr "Nome del tema" -@@ -254,52 +306,48 @@ - msgid "The name of the theme, to be loaded from ~/.themes/name/gnome-shell" - msgstr "Il nome del tema, da caricare da ~/.themes/nome/gnome-shell" - --#: ../extensions/window-list/extension.js:110 --msgid "Close" --msgstr "Chiudi" -- --#: ../extensions/window-list/extension.js:120 -+#: ../extensions/window-list/extension.js:121 - msgid "Unminimize" - msgstr "Deminimizza" - --#: ../extensions/window-list/extension.js:121 -+#: ../extensions/window-list/extension.js:122 - msgid "Minimize" - msgstr "Minimizza" - --#: ../extensions/window-list/extension.js:127 -+#: ../extensions/window-list/extension.js:128 - msgid "Unmaximize" - msgstr "Demassimizza" - --#: ../extensions/window-list/extension.js:128 -+#: ../extensions/window-list/extension.js:129 - msgid "Maximize" - msgstr "Massimizza" - --#: ../extensions/window-list/extension.js:300 -+#: ../extensions/window-list/extension.js:400 - msgid "Minimize all" - msgstr "Minimizza tutto" - --#: ../extensions/window-list/extension.js:308 -+#: ../extensions/window-list/extension.js:408 - msgid "Unminimize all" - msgstr "Deminimizza tutto" - --#: ../extensions/window-list/extension.js:316 -+#: ../extensions/window-list/extension.js:416 - msgid "Maximize all" - msgstr "Massimizza tutto" - --#: ../extensions/window-list/extension.js:325 -+#: ../extensions/window-list/extension.js:425 - msgid "Unmaximize all" - msgstr "Demassimizza tutto" - --#: ../extensions/window-list/extension.js:334 -+#: ../extensions/window-list/extension.js:434 - msgid "Close all" - msgstr "Chiudi tutto" - --#: ../extensions/window-list/extension.js:644 -+#: ../extensions/window-list/extension.js:718 - #: ../extensions/workspace-indicator/extension.js:30 - msgid "Workspace Indicator" - msgstr "Indicatore spazi di lavoro" - --#: ../extensions/window-list/extension.js:808 -+#: ../extensions/window-list/extension.js:882 - msgid "Window List" - msgstr "Elenco finestre" - -@@ -315,22 +363,38 @@ - "Decide quando raggruppare le finestre della stessa applicazione sull'elenco " - "delle finestre. I possibili valori sono \"never\", \"auto\" e \"always\"." - --#: ../extensions/window-list/prefs.js:30 -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:3 -+msgid "Show the window list on all monitors" -+msgstr "Mostra l’elenco di finestre su tutti i monitor" -+ -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:4 -+msgid "" -+"Whether to show the window list on all connected monitors or only on the " -+"primary one." -+msgstr "" -+"Indica se mostrare l’elenco di finestre su tutti i monitor connessi o solo " -+"sul primario." -+ -+#: ../extensions/window-list/prefs.js:32 - msgid "Window Grouping" - msgstr "Raggruppamento finestre" - --#: ../extensions/window-list/prefs.js:49 -+#: ../extensions/window-list/prefs.js:50 - msgid "Never group windows" - msgstr "Non raggruppare le finestre" - --#: ../extensions/window-list/prefs.js:50 -+#: ../extensions/window-list/prefs.js:51 - msgid "Group windows when space is limited" - msgstr "Raggruppare le finestre quando c'è poco spazio" - --#: ../extensions/window-list/prefs.js:51 -+#: ../extensions/window-list/prefs.js:52 - msgid "Always group windows" - msgstr "Raggruppare sempre le finestre" - -+#: ../extensions/window-list/prefs.js:75 -+msgid "Show on all monitors" -+msgstr "Mostra su tutti i monitor" -+ - #: ../extensions/workspace-indicator/prefs.js:141 - msgid "Workspace Names" - msgstr "Nomi degli spazi di lavoro" -@@ -340,6 +404,6 @@ - msgstr "Nome" - - #: ../extensions/workspace-indicator/prefs.js:198 --#, javascript-format -+#, c-format - msgid "Workspace %d" - msgstr "Spazio di lavoro %d" ---- gnome-shell-extensions-3.14.4/po/ja.po 2014-10-07 17:24:02.000000000 +0530 -+++ gnome-shell-extensions-3.14.4/po/ja.po 2016-06-25 07:52:33.492142879 +0530 -@@ -6,20 +6,21 @@ - # Nishio Futoshi , 2013. - # Ikuya Awashiro , 2014. - # Hajime Taira , 2014. --# -+# tnagamot , 2016. #zanata - msgid "" - msgstr "" - "Project-Id-Version: gnome-shell-extensions master\n" --"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&keywords=I18N+L10N&component=extensions\n" --"POT-Creation-Date: 2014-08-28 19:37+0000\n" --"PO-Revision-Date: 2014-08-29 12:16+0900\n" --"Last-Translator: Hajime Taira \n" --"Language-Team: Japanese \n" --"Language: ja\n" -+"Report-Msgid-Bugs-To: \n" -+"POT-Creation-Date: 2016-04-19 10:07+0530\n" - "MIME-Version: 1.0\n" - "Content-Type: text/plain; charset=UTF-8\n" - "Content-Transfer-Encoding: 8bit\n" -+"PO-Revision-Date: 2016-05-18 06:34+0000\n" -+"Last-Translator: tnagamot \n" -+"Language-Team: Japanese \n" -+"Language: ja\n" - "Plural-Forms: nplurals=1; plural=0;\n" -+"X-Generator: Zanata 3.8.2\n" - - #: ../data/gnome-classic.desktop.in.h:1 - #: ../data/gnome-classic.session.desktop.in.in.h:1 -@@ -43,7 +44,8 @@ - msgstr "モーダルダイアログを親ウィンドウに結び付ける" - - #: ../data/org.gnome.shell.extensions.classic-overrides.gschema.xml.in.h:2 --msgid "This key overrides the key in org.gnome.mutter when running GNOME Shell." -+msgid "" -+"This key overrides the key in org.gnome.mutter when running GNOME Shell." - msgstr "GNOME Shell 使用時は、このキーが、org.gnome.mutter の同じキーよりも優先します。" - - #: ../data/org.gnome.shell.extensions.classic-overrides.gschema.xml.in.h:3 -@@ -51,8 +53,11 @@ - msgstr "タイトルバー上のボタンの配置" - - #: ../data/org.gnome.shell.extensions.classic-overrides.gschema.xml.in.h:4 --msgid "This key overrides the key in org.gnome.desktop.wm.preferences when running GNOME Shell." --msgstr "GNOME Shell 使用時は、このキーが、org.gnome.desktop.wm.preferences の同じキーよりも優先します。" -+msgid "" -+"This key overrides the key in org.gnome.desktop.wm.preferences when running " -+"GNOME Shell." -+msgstr "" -+"GNOME Shell 使用時は、このキーが、org.gnome.desktop.wm.preferences の同じキーよりも優先します。" - - #: ../data/org.gnome.shell.extensions.classic-overrides.gschema.xml.in.h:5 - msgid "Enable edge tiling when dropping windows on screen edges" -@@ -86,15 +91,15 @@ - msgid "Show only windows in the current workspace" - msgstr "現在のワークスペースのウィンドウのみ表示する" - --#: ../extensions/apps-menu/extension.js:39 -+#: ../extensions/apps-menu/extension.js:40 - msgid "Activities Overview" - msgstr "アクティビティ" - --#: ../extensions/apps-menu/extension.js:113 -+#: ../extensions/apps-menu/extension.js:115 - msgid "Favorites" - msgstr "お気に入り" - --#: ../extensions/apps-menu/extension.js:282 -+#: ../extensions/apps-menu/extension.js:291 - msgid "Applications" - msgstr "アプリケーション" - -@@ -103,7 +108,9 @@ - msgstr "アプリケーションとワークスペースのリスト" - - #: ../extensions/auto-move-windows/org.gnome.shell.extensions.auto-move-windows.gschema.xml.in.h:2 --msgid "A list of strings, each containing an application id (desktop file name), followed by a colon and the workspace number" -+msgid "" -+"A list of strings, each containing an application id (desktop file name), " -+"followed by a colon and the workspace number" - msgstr "アプリケーションの識別子 (.desktop ファイル名) とコロンの後にワークスペース番号を付与した文字列を要素とするリストです" - - #: ../extensions/auto-move-windows/prefs.js:60 -@@ -128,15 +135,15 @@ - msgstr "追加" - - #: ../extensions/drive-menu/extension.js:106 --#, javascript-format -+#, c-format - msgid "Ejecting drive '%s' failed:" - msgstr "ドライブ '%s' の取り出しに失敗しました:" - --#: ../extensions/drive-menu/extension.js:123 -+#: ../extensions/drive-menu/extension.js:124 - msgid "Removable devices" - msgstr "リムーバブルデバイス" - --#: ../extensions/drive-menu/extension.js:150 -+#: ../extensions/drive-menu/extension.js:149 - msgid "Open File" - msgstr "ファイルを開く" - -@@ -149,16 +156,21 @@ - msgstr "代わりの挨拶テキストです。" - - #: ../extensions/example/org.gnome.shell.extensions.example.gschema.xml.in.h:2 --msgid "If not empty, it contains the text that will be shown when clicking on the panel." -+msgid "" -+"If not empty, it contains the text that will be shown when clicking on the " -+"panel." - msgstr "空でない場合、指定したテキストが、パネルをクリックした時に表示されます。" - - #: ../extensions/example/prefs.js:30 - msgid "Message" - msgstr "メッセージ" - -+#. TRANSLATORS: Example is the name of the extension, should not be -+#. translated - #: ../extensions/example/prefs.js:43 - msgid "" --"Example aims to show how to build well behaved extensions for the Shell and as such it has little functionality on its own.\n" -+"Example aims to show how to build well behaved extensions for the Shell and " -+"as such it has little functionality on its own.\n" - "Nevertheless it's possible to customize the greeting message." - msgstr "" - "Example は、うまく動作する GNOME Shell 拡張機能の構築方法を示すことを目的としています。それ自体の機能はほんとどありません。\n" -@@ -169,16 +181,26 @@ - msgstr "ウィンドウにたくさんの画面を使うかどうか" - - #: ../extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml.in.h:2 --msgid "Try to use more screen for placing window thumbnails by adapting to screen aspect ratio, and consolidating them further to reduce the bounding box. This setting applies only with the natural placement strategy." --msgstr "ウィンドウのサムネイルを複数配置する際に、画面のアスペクト比に合わせて、境界部分を減らすことにより、ウィンドウを統合することで、さらにたくさんの画面を使用できるようにするかどうかです。この設定は 'natural' の配置アルゴリズムを採用している場合にのみ適用されます。" -+msgid "" -+"Try to use more screen for placing window thumbnails by adapting to screen " -+"aspect ratio, and consolidating them further to reduce the bounding box. " -+"This setting applies only with the natural placement strategy." -+msgstr "" -+"ウィンドウのサムネイルを複数配置する際に、画面のアスペクト比に合わせて、境界部分を減らすことにより、ウィンドウを統合することで、さらにたくさんの画面を使用できるようにするかどうかです。この設定は " -+"'natural' の配置アルゴリズムを採用している場合にのみ適用されます。" - - #: ../extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml.in.h:3 - msgid "Place window captions on top" - msgstr "ウィンドウのタイトルバーを上端に表示するかどうか" - - #: ../extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml.in.h:4 --msgid "If true, place window captions on top the respective thumbnail, overriding shell default of placing it at the bottom. Changing this setting requires restarting the shell to have any effect." --msgstr "TRUE にすると、ウィンドウのサムネイルの上端にそのウィンドウのタイトルバーを表示します (これは、サムネイルの下端にタイトルバーを表示する GNOME シェルのデフォルト値よりも優先されます)。この設定を適用する際は GNOME シェルを再起動してください。" -+msgid "" -+"If true, place window captions on top the respective thumbnail, overriding " -+"shell default of placing it at the bottom. Changing this setting requires " -+"restarting the shell to have any effect." -+msgstr "" -+"TRUE にすると、ウィンドウのサムネイルの上端にそのウィンドウのタイトルバーを表示します (これは、サムネイルの下端にタイトルバーを表示する " -+"GNOME シェルのデフォルト値よりも優先されます)。この設定を適用する際は GNOME シェルを再起動してください。" - - #: ../extensions/places-menu/extension.js:78 - #: ../extensions/places-menu/extension.js:81 -@@ -186,7 +208,7 @@ - msgstr "場所" - - #: ../extensions/places-menu/placeDisplay.js:57 --#, javascript-format -+#, c-format - msgid "Failed to launch \"%s\"" - msgstr "\"%s\" の起動に失敗" - -@@ -211,6 +233,46 @@ - msgid "Memory" - msgstr "メモリ" - -+#: ../extensions/updates-dialog/extension.js:108 -+msgid "Important security updates need to be installed.\n" -+msgstr "重要なセキュリティーアップデートをインストールする必要があります。\n" -+ -+#: ../extensions/updates-dialog/extension.js:110 -+#, c-format -+msgid "You can close this dialog and get %d minute to finish your work." -+msgid_plural "" -+"You can close this dialog and get %d minutes to finish your work." -+msgstr[0] "このダイアログを閉じ、%d 分で作業を完了することができます。" -+ -+#: ../extensions/updates-dialog/extension.js:114 -+#, c-format -+msgid "You can close this dialog and get %d hour to finish your work." -+msgid_plural "You can close this dialog and get %d hours to finish your work." -+msgstr[0] "このダイアログを閉じ、%d 時間で作業を完了することができます。" -+ -+#: ../extensions/updates-dialog/extension.js:145 -+msgid "Important security updates" -+msgstr "重要なセキュリティーアップデート" -+ -+#: ../extensions/updates-dialog/extension.js:163 -+#: ../extensions/window-list/extension.js:111 -+msgid "Close" -+msgstr "閉じる" -+ -+#: ../extensions/updates-dialog/extension.js:166 -+msgid "Restart & Install" -+msgstr "再移動 & インストール" -+ -+#: ../extensions/updates-dialog/extension.js:217 -+msgid "Important security updates need to be installed now.\n" -+msgstr "今すぐ重要なセキュリティーアップデートをインストールする必要があります。\n" -+ -+#: ../extensions/updates-dialog/extension.js:218 -+#, c-format -+msgid "This computer will restart in %d second." -+msgid_plural "This computer will restart in %d seconds." -+msgstr[0] "このコンピューターは %d 秒後に再起動されます。" -+ - #: ../extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml.in.h:1 - msgid "Theme name" - msgstr "テーマの名前" -@@ -219,52 +281,48 @@ - msgid "The name of the theme, to be loaded from ~/.themes/name/gnome-shell" - msgstr "テーマの名前です (~/.themes/name/gnome-shell 配下に格納します)" - --#: ../extensions/window-list/extension.js:110 --msgid "Close" --msgstr "閉じる" -- --#: ../extensions/window-list/extension.js:120 -+#: ../extensions/window-list/extension.js:121 - msgid "Unminimize" - msgstr "最小化解除" - --#: ../extensions/window-list/extension.js:121 -+#: ../extensions/window-list/extension.js:122 - msgid "Minimize" - msgstr "最小化" - --#: ../extensions/window-list/extension.js:127 -+#: ../extensions/window-list/extension.js:128 - msgid "Unmaximize" - msgstr "最大化解除" - --#: ../extensions/window-list/extension.js:128 -+#: ../extensions/window-list/extension.js:129 - msgid "Maximize" - msgstr "最大化" - --#: ../extensions/window-list/extension.js:300 -+#: ../extensions/window-list/extension.js:400 - msgid "Minimize all" - msgstr "全て最小化" - --#: ../extensions/window-list/extension.js:308 -+#: ../extensions/window-list/extension.js:408 - msgid "Unminimize all" - msgstr "全て最小化解除" - --#: ../extensions/window-list/extension.js:316 -+#: ../extensions/window-list/extension.js:416 - msgid "Maximize all" - msgstr "全て最大化" - --#: ../extensions/window-list/extension.js:325 -+#: ../extensions/window-list/extension.js:425 - msgid "Unmaximize all" - msgstr "全て最大化解除" - --#: ../extensions/window-list/extension.js:334 -+#: ../extensions/window-list/extension.js:434 - msgid "Close all" - msgstr "全て閉じる" - --#: ../extensions/window-list/extension.js:644 -+#: ../extensions/window-list/extension.js:718 - #: ../extensions/workspace-indicator/extension.js:30 - msgid "Workspace Indicator" - msgstr "ワークスペースインジケーター" - --#: ../extensions/window-list/extension.js:798 -+#: ../extensions/window-list/extension.js:882 - msgid "Window List" - msgstr "ウィンドウのリスト" - -@@ -273,25 +331,43 @@ - msgstr "ウインドウをグループ化する条件" - - #: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:2 --msgid "Decides when to group windows from the same application on the window list. Possible values are \"never\", \"auto\" and \"always\"." --msgstr "ウィンドウ一覧にある同じアプリケーションをグループ化する条件を指定します。指定可能な値は、\"never\", \"auto\", \"always\" です。" -+msgid "" -+"Decides when to group windows from the same application on the window list. " -+"Possible values are \"never\", \"auto\" and \"always\"." -+msgstr "" -+"ウィンドウ一覧にある同じアプリケーションをグループ化する条件を指定します。指定可能な値は、\"never\", \"auto\", \"always\" " -+"です。" -+ -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:3 -+msgid "Show the window list on all monitors" -+msgstr "すべてのモニターでウィンドウ一覧を表示" -+ -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:4 -+msgid "" -+"Whether to show the window list on all connected monitors or only on the " -+"primary one." -+msgstr "接続されたすべてのモニターまたはプライマリーモニターのみでウィンドウ一覧を表示するかどうか。" - --#: ../extensions/window-list/prefs.js:30 -+#: ../extensions/window-list/prefs.js:32 - msgid "Window Grouping" - msgstr "ウィンドウのグループ化" - --#: ../extensions/window-list/prefs.js:49 -+#: ../extensions/window-list/prefs.js:50 - msgid "Never group windows" - msgstr "ウィンドウをグループ化しない" - --#: ../extensions/window-list/prefs.js:50 -+#: ../extensions/window-list/prefs.js:51 - msgid "Group windows when space is limited" - msgstr "ウィンドウ一覧の幅が制限される時にグループ化する" - --#: ../extensions/window-list/prefs.js:51 -+#: ../extensions/window-list/prefs.js:52 - msgid "Always group windows" - msgstr "ウィンドウをグループ化する" - -+#: ../extensions/window-list/prefs.js:75 -+msgid "Show on all monitors" -+msgstr "すべてのモニターで表示" -+ - #: ../extensions/workspace-indicator/prefs.js:141 - msgid "Workspace Names" - msgstr "ワークスペース名" -@@ -301,106 +377,6 @@ - msgstr "名前" - - #: ../extensions/workspace-indicator/prefs.js:198 --#, javascript-format -+#, c-format - msgid "Workspace %d" - msgstr "ワークスペース %d" -- --#~ msgid "Normal" --#~ msgstr "標準" -- --#~ msgid "Left" --#~ msgstr "左回り" -- --#~ msgid "Right" --#~ msgstr "右回り" -- --#~ msgid "Upside-down" --#~ msgstr "逆さま" -- --#~ msgid "Display" --#~ msgstr "ディスプレイ" -- --#~ msgid "Display Settings" --#~ msgstr "ディスプレイ設定" -- --#~ msgid "Suspend" --#~ msgstr "サスペンド" -- --#~ msgid "Hibernate" --#~ msgstr "ハイバーネート" -- --#~ msgid "Power Off" --#~ msgstr "電源オフ" -- --#~ msgid "Enable suspending" --#~ msgstr "サスペンドを有効にする" -- --#~ msgid "Control the visibility of the Suspend menu item" --#~ msgstr "メニューアイテムとしてサスペンドを表示します。" -- --#~ msgid "Enable hibernating" --#~ msgstr "ハイバーネートを有効にする" -- --#~ msgid "Control the visibility of the Hibernate menu item" --#~ msgstr "メニューアイテムとしてハイバーネートを表示します。" -- --#~ msgid "Notifications" --#~ msgstr "メッセージ通知" -- --#~ msgid "Online Accounts" --#~ msgstr "オンラインアカウント" -- --#~ msgid "Lock Screen" --#~ msgstr "画面のロック" -- --#~ msgid "Switch User" --#~ msgstr "ユーザーの切り替え" -- --#~ msgid "Log Out..." --#~ msgstr "ログアウト..." -- --#~ msgid "Drag here to add favorites" --#~ msgstr "ドラッグでお気に入りに追加" -- --#~ msgid "New Window" --#~ msgstr "新しいウィンドウで開く" -- --#~ msgid "Remove from Favorites" --#~ msgstr "お気に入りから削除" -- --#~ msgid "Icon size" --#~ msgstr "アイコンのサイズ" -- --#~ msgid "Position of the dock" --#~ msgstr "ドックの位置" -- --#~ msgid "Sets icon size of the dock." --#~ msgstr "ドックに表示するアイコンの大きさを指定します。" -- --#, fuzzy --#~ msgid "Sets the effect of the hide dock. Allowed values are 'resize' or 'rescale'" --#~ msgstr "ドックをデスクトップに表示する位置を指定します。指定可能な値: 'right'、'left'" -- --#~ msgid "Sets the position of the dock in the screen. Allowed values are 'right' or 'left'" --#~ msgstr "ドックをデスクトップに表示する位置を指定します。指定可能な値: 'right'、'left'" -- --#~ msgid "%s is away." --#~ msgstr "%s さんは離席中です。" -- --#~ msgid "%s is offline." --#~ msgstr "%s さんはオフラインです。" -- --#~ msgid "%s is online." --#~ msgstr "%s さんはオンラインです。" -- --#~ msgid "%s is busy." --#~ msgstr "%s さんは取り込み中です。" -- --#~ msgid "The algorithm used to layout thumbnails in the overview. 'grid' to use the default grid based algorithm, 'natural' to use another one that reflects more the position and size of the actual window" --#~ msgstr "オーバービュー・モードでウィンドウのサムネイルを配置する際のアルゴリズムです。指定可能な値: 'grid' (原則的に格子状に配置していくアルゴリズム)、'natural' (ウィンドウの実際の位置や大きさを考慮して配置していくアルゴリズム)" -- --#~ msgid "Window placement strategy" --#~ msgstr "ウィンドウを配置するアルゴリズム" -- --#~ msgid "Configure display settings..." --#~ msgstr "ディスプレイの設定..." ---- gnome-shell-extensions-3.14.4/po/ko.po 2015-03-24 01:08:10.000000000 +0530 -+++ gnome-shell-extensions-3.14.4/po/ko.po 2016-06-25 07:52:33.494142878 +0530 -@@ -1,24 +1,25 @@ - # Korean translation for gnome-shell-extensions. - # Copyright (C) 2012 gnome-shell-extensions's COPYRIGHT HOLDER - # This file is distributed under the same license as the gnome-shell-extensions package. --# -+# - # Seong-ho Cho , 2012. - # Changwoo Ryu , 2013-2014. --# -+# eukim , 2016. #zanata -+# tchuang , 2016. #zanata - msgid "" - msgstr "" - "Project-Id-Version: gnome-shell-extensions\n" --"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-" --"shell&keywords=I18N+L10N&component=extensions\n" --"POT-Creation-Date: 2014-09-04 19:38+0000\n" --"PO-Revision-Date: 2014-09-05 07:14+0900\n" --"Last-Translator: Changwoo Ryu \n" --"Language-Team: Korean \n" --"Language: ko\n" -+"Report-Msgid-Bugs-To: \n" -+"POT-Creation-Date: 2016-04-19 10:07+0530\n" - "MIME-Version: 1.0\n" - "Content-Type: text/plain; charset=UTF-8\n" - "Content-Transfer-Encoding: 8bit\n" -+"PO-Revision-Date: 2016-06-15 03:54+0000\n" -+"Last-Translator: eukim \n" -+"Language-Team: Korean \n" -+"Language: ko\n" - "Plural-Forms: nplurals=1; plural=0;\n" -+"X-Generator: Zanata 3.8.2\n" - - #: ../data/gnome-classic.desktop.in.h:1 - #: ../data/gnome-classic.session.desktop.in.in.h:1 -@@ -88,15 +89,15 @@ - msgid "Show only windows in the current workspace" - msgstr "현재 작업 공간의 창만 표시합니다" - --#: ../extensions/apps-menu/extension.js:39 -+#: ../extensions/apps-menu/extension.js:40 - msgid "Activities Overview" - msgstr "현재 활동" - --#: ../extensions/apps-menu/extension.js:113 -+#: ../extensions/apps-menu/extension.js:115 - msgid "Favorites" - msgstr "즐겨찾기" - --#: ../extensions/apps-menu/extension.js:282 -+#: ../extensions/apps-menu/extension.js:291 - msgid "Applications" - msgstr "프로그램" - -@@ -108,9 +109,7 @@ - msgid "" - "A list of strings, each containing an application id (desktop file name), " - "followed by a colon and the workspace number" --msgstr "" --"문자열 목록, 각각은 프로그램 ID(데스크톱 파일 이름) 다음에 콜론 뒤에 작업 공" --"간 번호." -+msgstr "문자열 목록, 각각은 프로그램 ID(데스크톱 파일 이름) 다음에 콜론 뒤에 작업 공간 번호." - - #: ../extensions/auto-move-windows/prefs.js:60 - msgid "Application" -@@ -134,15 +133,15 @@ - msgstr "추가" - - #: ../extensions/drive-menu/extension.js:106 --#, javascript-format -+#, c-format - msgid "Ejecting drive '%s' failed:" - msgstr "'%s' 드라이브를 빼는데 실패했습니다:" - --#: ../extensions/drive-menu/extension.js:123 -+#: ../extensions/drive-menu/extension.js:124 - msgid "Removable devices" - msgstr "이동식 장치" - --#: ../extensions/drive-menu/extension.js:150 -+#: ../extensions/drive-menu/extension.js:149 - msgid "Open File" - msgstr "파일 열기" - -@@ -164,14 +163,15 @@ - msgid "Message" - msgstr "메시지" - -+#. TRANSLATORS: Example is the name of the extension, should not be -+#. translated - #: ../extensions/example/prefs.js:43 - msgid "" - "Example aims to show how to build well behaved extensions for the Shell and " - "as such it has little functionality on its own.\n" - "Nevertheless it's possible to customize the greeting message." - msgstr "" --"Example 확장은 잘 동작하는 셸 확장을 어떻게 만드는지 보여주는 예제이므로 자" --"체 기능은 거의 없습니다.\n" -+"Example 확장은 잘 동작하는 셸 확장을 어떻게 만드는지 보여주는 예제이므로 자체 기능은 거의 없습니다.\n" - "하지만 인사 메시지를 원하는대로 지정할 수 있습니다." - - #: ../extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml.in.h:1 -@@ -185,9 +185,8 @@ - "aspect ratio, and consolidating them further to reduce the bounding box. " - "This setting applies only with the natural placement strategy." - msgstr "" --"창의 섬네일을 표시할 때 더 많은 화면을 사용합니다. 섬네일을 화면 종횡비에 맞" --"추고 섬네일을 통합해 차지하는 크기를 줄입니다. 이 설정은 자동 배치 방식에서" --"만 적용됩니다." -+"창의 섬네일을 표시할 때 더 많은 화면을 사용합니다. 섬네일을 화면 종횡비에 맞추고 섬네일을 통합해 차지하는 크기를 줄입니다. 이 설정은 " -+"자동 배치 방식에서만 적용됩니다." - - #: ../extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml.in.h:3 - msgid "Place window captions on top" -@@ -199,8 +198,8 @@ - "shell default of placing it at the bottom. Changing this setting requires " - "restarting the shell to have any effect." - msgstr "" --"참이면, 창의 이름을 각 섬네일 위에 표시합니다. 셸의 기본값은 아래에 창 이름" --"을 표시합니다. 이 설정을 바꾸면 셸을 다시 시작해야 적용됩니다." -+"참이면, 창의 이름을 각 섬네일 위에 표시합니다. 셸의 기본값은 아래에 창 이름을 표시합니다. 이 설정을 바꾸면 셸을 다시 시작해야 " -+"적용됩니다." - - #: ../extensions/places-menu/extension.js:78 - #: ../extensions/places-menu/extension.js:81 -@@ -208,7 +207,7 @@ - msgstr "위치" - - #: ../extensions/places-menu/placeDisplay.js:57 --#, javascript-format -+#, c-format - msgid "Failed to launch \"%s\"" - msgstr "\"%s\" 실행에 실패했습니다" - -@@ -233,6 +232,46 @@ - msgid "Memory" - msgstr "메모리" - -+#: ../extensions/updates-dialog/extension.js:108 -+msgid "Important security updates need to be installed.\n" -+msgstr "중요 보안 업데이트를 설치해야 합니다.\n" -+ -+#: ../extensions/updates-dialog/extension.js:110 -+#, c-format -+msgid "You can close this dialog and get %d minute to finish your work." -+msgid_plural "" -+"You can close this dialog and get %d minutes to finish your work." -+msgstr[0] "대화 상자를 닫고 %d 분에 작업을 완료합니다." -+ -+#: ../extensions/updates-dialog/extension.js:114 -+#, c-format -+msgid "You can close this dialog and get %d hour to finish your work." -+msgid_plural "You can close this dialog and get %d hours to finish your work." -+msgstr[0] "대화 상자를 닫고 %d 시간에 작업을 완료합니다." -+ -+#: ../extensions/updates-dialog/extension.js:145 -+msgid "Important security updates" -+msgstr "중요 보안 업데이트" -+ -+#: ../extensions/updates-dialog/extension.js:163 -+#: ../extensions/window-list/extension.js:111 -+msgid "Close" -+msgstr "닫기" -+ -+#: ../extensions/updates-dialog/extension.js:166 -+msgid "Restart & Install" -+msgstr "다시 시작 및 설치" -+ -+#: ../extensions/updates-dialog/extension.js:217 -+msgid "Important security updates need to be installed now.\n" -+msgstr "지금 중요 보안 업데이트를 설치해야 합니다.\n" -+ -+#: ../extensions/updates-dialog/extension.js:218 -+#, c-format -+msgid "This computer will restart in %d second." -+msgid_plural "This computer will restart in %d seconds." -+msgstr[0] "%d 초 뒤에 시스템이 다시 시작됩니다." -+ - #: ../extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml.in.h:1 - msgid "Theme name" - msgstr "테마 이름" -@@ -241,52 +280,48 @@ - msgid "The name of the theme, to be loaded from ~/.themes/name/gnome-shell" - msgstr "테마 이름, ~/.themes/name/gnome-shell 아래에서 읽어들입니다." - --#: ../extensions/window-list/extension.js:110 --msgid "Close" --msgstr "닫기" -- --#: ../extensions/window-list/extension.js:120 -+#: ../extensions/window-list/extension.js:121 - msgid "Unminimize" - msgstr "최소화 취소" - --#: ../extensions/window-list/extension.js:121 -+#: ../extensions/window-list/extension.js:122 - msgid "Minimize" - msgstr "최소화" - --#: ../extensions/window-list/extension.js:127 -+#: ../extensions/window-list/extension.js:128 - msgid "Unmaximize" - msgstr "최대화 취소" - --#: ../extensions/window-list/extension.js:128 -+#: ../extensions/window-list/extension.js:129 - msgid "Maximize" - msgstr "최대화" - --#: ../extensions/window-list/extension.js:300 -+#: ../extensions/window-list/extension.js:400 - msgid "Minimize all" - msgstr "모두 최소화" - --#: ../extensions/window-list/extension.js:308 -+#: ../extensions/window-list/extension.js:408 - msgid "Unminimize all" - msgstr "모두 최소화 취소" - --#: ../extensions/window-list/extension.js:316 -+#: ../extensions/window-list/extension.js:416 - msgid "Maximize all" - msgstr "모두 최대화" - --#: ../extensions/window-list/extension.js:325 -+#: ../extensions/window-list/extension.js:425 - msgid "Unmaximize all" - msgstr "모두 최대화 취소" - --#: ../extensions/window-list/extension.js:334 -+#: ../extensions/window-list/extension.js:434 - msgid "Close all" - msgstr "모두 닫기" - --#: ../extensions/window-list/extension.js:644 -+#: ../extensions/window-list/extension.js:718 - #: ../extensions/workspace-indicator/extension.js:30 - msgid "Workspace Indicator" - msgstr "작업 공간 표시" - --#: ../extensions/window-list/extension.js:808 -+#: ../extensions/window-list/extension.js:882 - msgid "Window List" - msgstr "창 목록" - -@@ -298,26 +333,38 @@ - msgid "" - "Decides when to group windows from the same application on the window list. " - "Possible values are \"never\", \"auto\" and \"always\"." --msgstr "" --"창 목록에서 같은 프로그램의 창을 언제 모을지 결정합니다. 가능한 값은 \"never" --"\", \"auto\", \"always\"입니다." -+msgstr "창 목록에서 같은 프로그램의 창을 언제 모을지 결정합니다. 가능한 값은 \"never\", \"auto\", \"always\"입니다." -+ -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:3 -+msgid "Show the window list on all monitors" -+msgstr "모든 모니터에서 창 목록을 표시" - --#: ../extensions/window-list/prefs.js:30 -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:4 -+msgid "" -+"Whether to show the window list on all connected monitors or only on the " -+"primary one." -+msgstr "창 목록을 연결된 모든 모니터에 또는 기본 모니터에만 표시할 지 여부." -+ -+#: ../extensions/window-list/prefs.js:32 - msgid "Window Grouping" - msgstr "창 모으기" - --#: ../extensions/window-list/prefs.js:49 -+#: ../extensions/window-list/prefs.js:50 - msgid "Never group windows" - msgstr "창을 모으지 않기" - --#: ../extensions/window-list/prefs.js:50 -+#: ../extensions/window-list/prefs.js:51 - msgid "Group windows when space is limited" - msgstr "공간이 부족할 때 창 모으기" - --#: ../extensions/window-list/prefs.js:51 -+#: ../extensions/window-list/prefs.js:52 - msgid "Always group windows" - msgstr "항상 창 모으기" - -+#: ../extensions/window-list/prefs.js:75 -+msgid "Show on all monitors" -+msgstr "모든 모니터에서 표시" -+ - #: ../extensions/workspace-indicator/prefs.js:141 - msgid "Workspace Names" - msgstr "작업 공간 이름" -@@ -327,6 +374,6 @@ - msgstr "이름" - - #: ../extensions/workspace-indicator/prefs.js:198 --#, javascript-format -+#, c-format - msgid "Workspace %d" - msgstr "작업 공간 %d" ---- gnome-shell-extensions-3.14.4/po/pt_BR.po 2015-03-24 01:08:10.000000000 +0530 -+++ gnome-shell-extensions-3.14.4/po/pt_BR.po 2016-06-28 12:31:26.957190783 +0530 -@@ -9,22 +9,21 @@ - # Og Maciel , 2012. - # Rafael Ferreira , 2013. - # Enrico Nicoletto , 2013, 2014. --# -+# adallape , 2016. #zanata - msgid "" - msgstr "" - "Project-Id-Version: gnome-shell-extensions master\n" --"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-" --"shell&keywords=I18N+L10N&component=extensions\n" --"POT-Creation-Date: 2014-06-03 07:29+0000\n" --"PO-Revision-Date: 2014-06-03 10:05-0300\n" --"Last-Translator: Enrico Nicoletto \n" --"Language-Team: Brazilian Portuguese \n" --"Language: pt_BR\n" -+"Report-Msgid-Bugs-To: \n" -+"POT-Creation-Date: 2016-04-19 10:07+0530\n" - "MIME-Version: 1.0\n" - "Content-Type: text/plain; charset=UTF-8\n" - "Content-Transfer-Encoding: 8bit\n" -+"PO-Revision-Date: 2016-05-13 05:49+0000\n" -+"Last-Translator: adallape \n" -+"Language-Team: Brazilian Portuguese \n" -+"Language: pt-BR\n" - "Plural-Forms: nplurals=2; plural=(n > 1);\n" --"X-Generator: Poedit 1.6.5\n" -+"X-Generator: Zanata 3.8.2\n" - "X-Project-Style: gnome\n" - - #: ../data/gnome-classic.desktop.in.h:1 -@@ -60,8 +59,6 @@ - msgstr "Arranjo de botões na barra de títulos" - - #: ../data/org.gnome.shell.extensions.classic-overrides.gschema.xml.in.h:4 --#| msgid "" --#| "This key overrides the key in org.gnome.mutter when running GNOME Shell." - msgid "" - "This key overrides the key in org.gnome.desktop.wm.preferences when running " - "GNOME Shell." -@@ -103,15 +100,15 @@ - msgid "Show only windows in the current workspace" - msgstr "Mostrar somente janelas no espaço de trabalho atual" - --#: ../extensions/apps-menu/extension.js:39 -+#: ../extensions/apps-menu/extension.js:40 - msgid "Activities Overview" - msgstr "Panorama de atividades" - --#: ../extensions/apps-menu/extension.js:113 -+#: ../extensions/apps-menu/extension.js:115 - msgid "Favorites" - msgstr "Favoritos" - --#: ../extensions/apps-menu/extension.js:282 -+#: ../extensions/apps-menu/extension.js:291 - msgid "Applications" - msgstr "Aplicativos" - -@@ -137,7 +134,6 @@ - msgstr "Espaço de trabalho" - - #: ../extensions/auto-move-windows/prefs.js:85 --#| msgid "Add rule" - msgid "Add Rule" - msgstr "Adicionar regra" - -@@ -150,15 +146,15 @@ - msgstr "Adicionar" - - #: ../extensions/drive-menu/extension.js:106 --#, javascript-format -+#, c-format - msgid "Ejecting drive '%s' failed:" - msgstr "Falha ao ejetar a unidade \"%s\":" - --#: ../extensions/drive-menu/extension.js:123 -+#: ../extensions/drive-menu/extension.js:124 - msgid "Removable devices" - msgstr "Dispositivos removíveis" - --#: ../extensions/drive-menu/extension.js:150 -+#: ../extensions/drive-menu/extension.js:149 - msgid "Open File" - msgstr "Abrir arquivo" - -@@ -178,10 +174,11 @@ - "Quando não vazio, contém o texto que será exibido ao se clicar no painel." - - #: ../extensions/example/prefs.js:30 --#| msgid "Message:" - msgid "Message" - msgstr "Mensagem" - -+#. TRANSLATORS: Example is the name of the extension, should not be -+#. translated - #: ../extensions/example/prefs.js:43 - msgid "" - "Example aims to show how to build well behaved extensions for the Shell and " -@@ -228,7 +225,7 @@ - msgstr "Locais" - - #: ../extensions/places-menu/placeDisplay.js:57 --#, javascript-format -+#, c-format - msgid "Failed to launch \"%s\"" - msgstr "Falha ao iniciar \"%s\"" - -@@ -253,6 +250,56 @@ - msgid "Memory" - msgstr "Memória" - -+#: ../extensions/updates-dialog/extension.js:108 -+msgid "Important security updates need to be installed.\n" -+msgstr "Atualizações importantes de segurança precisam ser instaladas. \n" -+ -+#: ../extensions/updates-dialog/extension.js:110 -+#, c-format -+msgid "You can close this dialog and get %d minute to finish your work." -+msgid_plural "" -+"You can close this dialog and get %d minutes to finish your work." -+msgstr[0] "" -+"Você pode fechar este diálogo e obter %d minutos para finalizar seu trabalho." -+" " -+msgstr[1] "" -+"Você pode fechar este diálogo e obter %d minutos para finalizar seu trabalho." -+" " -+ -+#: ../extensions/updates-dialog/extension.js:114 -+#, c-format -+msgid "You can close this dialog and get %d hour to finish your work." -+msgid_plural "You can close this dialog and get %d hours to finish your work." -+msgstr[0] "" -+"Você pode fechar este diálogo e obter %d horas para finalizar seu trabalho." -+msgstr[1] "" -+"Você pode fechar este diálogo e obter %d horas para finalizar seu trabalho." -+ -+#: ../extensions/updates-dialog/extension.js:145 -+msgid "Important security updates" -+msgstr "Atualizações de segurança importantes" -+ -+#: ../extensions/updates-dialog/extension.js:163 -+#: ../extensions/window-list/extension.js:111 -+msgid "Close" -+msgstr "Fechar" -+ -+#: ../extensions/updates-dialog/extension.js:166 -+msgid "Restart & Install" -+msgstr "Reiniciar & instalar" -+ -+#: ../extensions/updates-dialog/extension.js:217 -+msgid "Important security updates need to be installed now.\n" -+msgstr "" -+"Atualizações importantes de segurança precisam ser instaladas agora. \n" -+ -+#: ../extensions/updates-dialog/extension.js:218 -+#, c-format -+msgid "This computer will restart in %d second." -+msgid_plural "This computer will restart in %d seconds." -+msgstr[0] "Este computador irá reiniciar em %d segundos. " -+msgstr[1] "Este computador irá reiniciar em %d segundos. " -+ - #: ../extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml.in.h:1 - msgid "Theme name" - msgstr "Nome do tema" -@@ -261,52 +308,48 @@ - msgid "The name of the theme, to be loaded from ~/.themes/name/gnome-shell" - msgstr "O nome do tema, para ser carregado de ~/.themes/nome/gnome-shell" - --#: ../extensions/window-list/extension.js:110 --msgid "Close" --msgstr "Fechar" -- --#: ../extensions/window-list/extension.js:120 -+#: ../extensions/window-list/extension.js:121 - msgid "Unminimize" - msgstr "Desfazer janelas minimizadas" - --#: ../extensions/window-list/extension.js:121 -+#: ../extensions/window-list/extension.js:122 - msgid "Minimize" - msgstr "Minimizar" - --#: ../extensions/window-list/extension.js:127 -+#: ../extensions/window-list/extension.js:128 - msgid "Unmaximize" - msgstr "Desfazer janelas maximizadas" - --#: ../extensions/window-list/extension.js:128 -+#: ../extensions/window-list/extension.js:129 - msgid "Maximize" - msgstr "Maximizar" - --#: ../extensions/window-list/extension.js:300 -+#: ../extensions/window-list/extension.js:400 - msgid "Minimize all" - msgstr "Minimizar todas" - --#: ../extensions/window-list/extension.js:308 -+#: ../extensions/window-list/extension.js:408 - msgid "Unminimize all" - msgstr "Desfazer todas as janelas minimizadas" - --#: ../extensions/window-list/extension.js:316 -+#: ../extensions/window-list/extension.js:416 - msgid "Maximize all" - msgstr "Maximizar todas" - --#: ../extensions/window-list/extension.js:325 -+#: ../extensions/window-list/extension.js:425 - msgid "Unmaximize all" - msgstr "Desfazer todas as janelas maximizadas" - --#: ../extensions/window-list/extension.js:334 -+#: ../extensions/window-list/extension.js:434 - msgid "Close all" - msgstr "Fechar todas" - --#: ../extensions/window-list/extension.js:644 -+#: ../extensions/window-list/extension.js:718 - #: ../extensions/workspace-indicator/extension.js:30 - msgid "Workspace Indicator" - msgstr "Indicador de espaços de trabalho" - --#: ../extensions/window-list/extension.js:798 -+#: ../extensions/window-list/extension.js:882 - msgid "Window List" - msgstr "Lista de janelas" - -@@ -322,24 +365,39 @@ - "Decide quando agrupar janelas do mesmo aplicativo na lista de janelas. " - "Valores possíveis são \"nunca\", \"auto\" e \"sempre\"." - --#: ../extensions/window-list/prefs.js:30 -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:3 -+msgid "Show the window list on all monitors" -+msgstr "Exibir a lista de janelas em todos os monitores " -+ -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:4 -+msgid "" -+"Whether to show the window list on all connected monitors or only on the " -+"primary one." -+msgstr "" -+"Exibir a lista de janelas em todos os monitores conectados ou somente nos " -+"primários." -+ -+#: ../extensions/window-list/prefs.js:32 - msgid "Window Grouping" - msgstr "Agrupamento de janelas" - --#: ../extensions/window-list/prefs.js:49 -+#: ../extensions/window-list/prefs.js:50 - msgid "Never group windows" - msgstr "Nunca agrupar janelas" - --#: ../extensions/window-list/prefs.js:50 -+#: ../extensions/window-list/prefs.js:51 - msgid "Group windows when space is limited" - msgstr "Agrupar janelas quando o espaço estiver limitado" - --#: ../extensions/window-list/prefs.js:51 -+#: ../extensions/window-list/prefs.js:52 - msgid "Always group windows" - msgstr "Sempre agrupar janelas" - -+#: ../extensions/window-list/prefs.js:75 -+msgid "Show on all monitors" -+msgstr "Exibir em todos os monitores " -+ - #: ../extensions/workspace-indicator/prefs.js:141 --#| msgid "Workspace names:" - msgid "Workspace Names" - msgstr "Nomes de espaços de trabalho" - -@@ -348,268 +406,6 @@ - msgstr "Nome" - - #: ../extensions/workspace-indicator/prefs.js:198 --#, javascript-format -+#, c-format - msgid "Workspace %d" - msgstr "Espaço de trabalho %d" -- --#~ msgid "Suspend" --#~ msgstr "Suspender" -- --#~ msgid "Hibernate" --#~ msgstr "Hibernar" -- --#~ msgid "Power Off" --#~ msgstr "Desligar" -- --#~ msgid "Enable suspending" --#~ msgstr "Habilitar suspenção" -- --#~ msgid "Control the visibility of the Suspend menu item" --#~ msgstr "Controla a visibilidade do item de menu Suspender" -- --#~ msgid "Enable hibernating" --#~ msgstr "Habilitar hibernação" -- --#~ msgid "Control the visibility of the Hibernate menu item" --#~ msgstr "Controla a visibilidade do item de menu Hibernar" -- --#~ msgid "Normal" --#~ msgstr "Normal" -- --#~ msgid "Left" --#~ msgstr "Esquerda" -- --#~ msgid "Right" --#~ msgstr "Direita" -- --#~ msgid "Upside-down" --#~ msgstr "De cabeça para baixo" -- --#~ msgid "Display" --#~ msgstr "Tela" -- --#~ msgid "Display Settings" --#~ msgstr "Configurações de tela" -- --#~ msgid "The application icon mode." --#~ msgstr "O modo de ícone do aplicativo." -- --#~ msgid "" --#~ "Configures how the windows are shown in the switcher. Valid possibilities " --#~ "are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-" --#~ "only' (shows only the application icon) or 'both'." --#~ msgstr "" --#~ "Configura como as janelas são mostradas no alternador. Possibilidades " --#~ "válidas são \"thumbnail-only\" (mostra uma miniatura da janela), \"app-" --#~ "icon-only\" (mostra somente o ícone do aplicativo) ou \"both\" (ambos)." -- --#~ msgid "Drag here to add favorites" --#~ msgstr "Arraste aqui para adicionar favoritos" -- --#~ msgid "New Window" --#~ msgstr "Nova janela" -- --#~ msgid "Quit Application" --#~ msgstr "Fechar aplicativo" -- --#~ msgid "Remove from Favorites" --#~ msgstr "Remover dos favoritos" -- --#~ msgid "Position of the dock" --#~ msgstr "Posição do dock" -- --#~ msgid "" --#~ "Sets the position of the dock in the screen. Allowed values are 'right' " --#~ "or 'left'" --#~ msgstr "" --#~ "Define a posição do dock na tela. Os valores permitidos são \"right\" ou " --#~ "\"left\"" -- --#~ msgid "Icon size" --#~ msgstr "Tamanho do ícone" -- --#~ msgid "Sets icon size of the dock." --#~ msgstr "Define o tamanho do ícone do dock." -- --#~ msgid "Enable/disable autohide" --#~ msgstr "Habilitar/desabilitar ocultar automaticamente" -- --#~ msgid "Autohide effect" --#~ msgstr "Efeito de ocultar automaticamente" -- --#~ msgid "" --#~ "Sets the effect of the hide dock. Allowed values are 'resize', 'rescale' " --#~ "and 'move'" --#~ msgstr "" --#~ "Define o efeito de ocultar dock. Os valores permitidos são \"resize\", " --#~ "\"rescale\" e \"move\"" -- --#~ msgid "Autohide duration" --#~ msgstr "Duração do ocultar automaticamente" -- --#~ msgid "Sets the time duration of the autohide effect." --#~ msgstr "Define o tempo de duração do efeito de ocultar automaticamente." -- --#~ msgid "Monitor" --#~ msgstr "Monitor" -- --#~ msgid "" --#~ "Sets monitor to display dock in. The default value (-1) is the primary " --#~ "monitor." --#~ msgstr "" --#~ "Configura o monitor para mostrar encaixe. O valor padrão (-1) é o monitor " --#~ "primário." -- --#~ msgid "%s is away." --#~ msgstr "%s está ausente." -- --#~ msgid "%s is offline." --#~ msgstr "%s está desconectado." -- --#~ msgid "%s is online." --#~ msgstr "%s está conectado." -- --#~ msgid "%s is busy." --#~ msgstr "%s está ocupado." -- --#~ msgid "Devices" --#~ msgstr "Dispositivos" -- --#~ msgid "Bookmarks" --#~ msgstr "Marcadores" -- --#~ msgid "Network" --#~ msgstr "Rede" -- --#~ msgid "File System" --#~ msgstr "Sistema de arquivos" -- --#~ msgid "The alt tab behaviour." --#~ msgstr "O comportamento do alt tab." -- --#~ msgid "" --#~ "Sets the Alt-Tab behaviour. Possible values are: all_thumbnails and " --#~ "workspace_icons. See the configuration dialogs for details." --#~ msgstr "" --#~ "Define o comportamento do Alt-Tab. Valores possíveis são: all_thumbnails " --#~ "e workspace_icons. Veja os diálogos de configuração para mais detalhes." -- --#~ msgid "" --#~ "This mode presents all applications from all workspaces in one selection " --#~ "list. Instead of using the application icon of every window, it uses " --#~ "small thumbnails resembling the window itself." --#~ msgstr "" --#~ "Esse modo apresenta todos os aplicativos de todos os espaços de trabalho " --#~ "em uma lista de seleção. Em vez de usar o ícone de aplicativo de cada " --#~ "janela, usa pequenas miniaturas com a aparência da própria janela." -- --#~ msgid "Workspace & Icons" --#~ msgstr "Espaço de trabalho & ícones" -- --#~ msgid "" --#~ "This mode let's you switch between the applications of your current " --#~ "workspace and gives you additionally the option to switch to the last " --#~ "used application of your previous workspace. This is always the last " --#~ "symbol in the list and is segregated by a separator/vertical line if " --#~ "available. \n" --#~ "Every window is represented by its application icon." --#~ msgstr "" --#~ "Esse modo permite que você alterne entre os aplicativos do seu espaço de " --#~ "trabalho atual e além disso lhe dá a opção de alternar para a última " --#~ "aplicação utilizada do seu espaço de trabalho anterior. Essa é sempre o " --#~ "último símbolo na lista e é segregada através de um separador/linha " --#~ "vertical quando disponível.\n" --#~ "Cada janela é representada pelo seu ícone de aplicativo." -- --#~ msgid "Move current selection to front before closing the popup" --#~ msgstr "Move a seleção atual para a frente antes de fechar a popup" -- --#~ msgid "" --#~ "The Alternate Tab can be used in different modes, that affect the way " --#~ "windows are chosen and presented." --#~ msgstr "" --#~ "O tab alternativo pode ser usado em modos diferentes que afetam a maneira " --#~ "como as janelas são escolhidas e apresentadas." -- --#~ msgid "Configure display settings..." --#~ msgstr "Alterar configurações de exibição..." -- --#~ msgid "" --#~ "This is the first time you use the Alternate Tab extension. \n" --#~ "Please choose your preferred behaviour:\n" --#~ "\n" --#~ "All & Thumbnails:\n" --#~ " This mode presents all applications from all workspaces in one " --#~ "selection \n" --#~ " list. Instead of using the application icon of every window, it uses " --#~ "small \n" --#~ " thumbnails resembling the window itself. \n" --#~ "\n" --#~ "Workspace & Icons:\n" --#~ " This mode let's you switch between the applications of your current \n" --#~ " workspace and gives you additionally the option to switch to the last " --#~ "used \n" --#~ " application of your previous workspace. This is always the last " --#~ "symbol in \n" --#~ " the list and is segregated by a separator/vertical line if " --#~ "available. \n" --#~ " Every window is represented by its application icon. \n" --#~ "\n" --#~ "If you whish to revert to the default behavior for the Alt-Tab switcher, " --#~ "just\n" --#~ "disable the extension from extensions.gnome.org or the Advanced Settings " --#~ "application." --#~ msgstr "" --#~ "Esta é a primeira vez que você usa a extensão Alternate Tab. \n" --#~ "Por favor, escolha seu comportamento preferido:\n" --#~ "\n" --#~ "All & miniaturas:\n" --#~ " Este modo apresenta todos os aplicativos de todos os espaços de " --#~ "trabalho em\n" --#~ " uma lista. Ao invés de usar o ícone do aplicativo de cada janela, ele " --#~ "usa\n" --#~ " pequenas miniaturas assemelhando-se a janela própria. \n" --#~ "\n" --#~ "Espaço de trabalho & ícones:\n" --#~ " Este modo permite que você alterne entre os aplicativos do seu espaço " --#~ "de trabalho\n" --#~ " atual e dá a você além da opção para mudar para o último aplicativo " --#~ "usado em seu\n" --#~ " espaço de trabalho anterior. Este é sempre o último símbolo na lista " --#~ "e separado\n" --#~ " por uma linha separadora/vertical se disponível. \n" --#~ " Cada janela é representada por seu ícone de aplicativo. \n" --#~ "\n" --#~ "Se você deseja reverter para o comportamento padrão a partir do " --#~ "alternador de Alt-Tab, apenas desative a extensão de extensions.gnome.org " --#~ "ou nas configurações avançadas do aplicativo." -- --#~ msgid "Alt Tab Behaviour" --#~ msgstr "Comportamento do Alt Tab" -- --#~ msgid "Cancel" --#~ msgstr "Cancelar" -- --#~ msgid "Ask the user for a default behaviour if true." --#~ msgstr "Pergunte ao usuário por um comportamento padrão se marcado." -- --#~ msgid "Indicates if Alternate Tab is newly installed" --#~ msgstr "Indica se o alternar com Tab for recém-instalado" -- --#~ msgid "Available" --#~ msgstr "Disponível" -- --#~ msgid "Busy" --#~ msgstr "Ocupado" -- --#~ msgid "My Account" --#~ msgstr "Minha conta" -- --#~ msgid "Lock Screen" --#~ msgstr "Bloquear tela" -- --#~ msgid "Switch User" --#~ msgstr "Trocar de usuário" -- --#~ msgid "Log Out..." --#~ msgstr "Encerrar sessão..." ---- gnome-shell-extensions-3.14.4/po/ru.po 2015-03-24 01:08:10.000000000 +0530 -+++ gnome-shell-extensions-3.14.4/po/ru.po 2016-06-25 07:52:33.499142878 +0530 -@@ -3,23 +3,22 @@ - # This file is distributed under the same license as the gnome-shell-extensions package. - # Yuri Myasoedov , 2011, 2012, 2013. - # Stas Solovey , 2011, 2012, 2013. --# -+# ypoyarko , 2016. #zanata - msgid "" - msgstr "" - "Project-Id-Version: gnome-shell-extensions gnome-3-0\n" --"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-" --"shell&keywords=I18N+L10N&component=extensions\n" --"POT-Creation-Date: 2014-06-27 07:35+0000\n" --"PO-Revision-Date: 2014-06-27 21:50+0400\n" --"Last-Translator: Yuri Myasoedov \n" --"Language-Team: Русский \n" --"Language: ru\n" -+"Report-Msgid-Bugs-To: \n" -+"POT-Creation-Date: 2016-04-19 10:07+0530\n" - "MIME-Version: 1.0\n" - "Content-Type: text/plain; charset=UTF-8\n" - "Content-Transfer-Encoding: 8bit\n" --"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" --"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" --"X-Generator: Poedit 1.6.5\n" -+"PO-Revision-Date: 2016-05-18 03:08+0000\n" -+"Last-Translator: ypoyarko \n" -+"Language-Team: Русский \n" -+"Language: ru\n" -+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " -+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -+"X-Generator: Zanata 3.8.2\n" - - #: ../data/gnome-classic.desktop.in.h:1 - #: ../data/gnome-classic.session.desktop.in.in.h:1 -@@ -53,8 +52,6 @@ - msgstr "Расположение кнопок в заголовке" - - #: ../data/org.gnome.shell.extensions.classic-overrides.gschema.xml.in.h:4 --#| msgid "" --#| "This key overrides the key in org.gnome.mutter when running GNOME Shell." - msgid "" - "This key overrides the key in org.gnome.desktop.wm.preferences when running " - "GNOME Shell." -@@ -95,15 +92,15 @@ - msgid "Show only windows in the current workspace" - msgstr "Отображать окна только текущей рабочей области" - --#: ../extensions/apps-menu/extension.js:39 -+#: ../extensions/apps-menu/extension.js:40 - msgid "Activities Overview" - msgstr "Обзор" - --#: ../extensions/apps-menu/extension.js:113 -+#: ../extensions/apps-menu/extension.js:115 - msgid "Favorites" - msgstr "Избранное" - --#: ../extensions/apps-menu/extension.js:282 -+#: ../extensions/apps-menu/extension.js:291 - msgid "Applications" - msgstr "Приложения" - -@@ -129,7 +126,6 @@ - msgstr "Рабочая область" - - #: ../extensions/auto-move-windows/prefs.js:85 --#| msgid "Add rule" - msgid "Add Rule" - msgstr "Добавить правило" - -@@ -142,15 +138,15 @@ - msgstr "Добавить" - - #: ../extensions/drive-menu/extension.js:106 --#, javascript-format -+#, c-format - msgid "Ejecting drive '%s' failed:" - msgstr "Не удалось извлечь диск «%s»:" - --#: ../extensions/drive-menu/extension.js:123 -+#: ../extensions/drive-menu/extension.js:124 - msgid "Removable devices" - msgstr "Съёмные устройства" - --#: ../extensions/drive-menu/extension.js:150 -+#: ../extensions/drive-menu/extension.js:149 - msgid "Open File" - msgstr "Открыть файл" - -@@ -171,10 +167,11 @@ - "на панель." - - #: ../extensions/example/prefs.js:30 --#| msgid "Message:" - msgid "Message" - msgstr "Сообщение" - -+#. TRANSLATORS: Example is the name of the extension, should not be -+#. translated - #: ../extensions/example/prefs.js:43 - msgid "" - "Example aims to show how to build well behaved extensions for the Shell and " -@@ -220,7 +217,7 @@ - msgstr "Места" - - #: ../extensions/places-menu/placeDisplay.js:57 --#, javascript-format -+#, c-format - msgid "Failed to launch \"%s\"" - msgstr "Не удалось запустить «%s»" - -@@ -245,6 +242,58 @@ - msgid "Memory" - msgstr "Память" - -+#: ../extensions/updates-dialog/extension.js:108 -+msgid "Important security updates need to be installed.\n" -+msgstr "Необходимо установить важные обновления безопасности\n" -+ -+#: ../extensions/updates-dialog/extension.js:110 -+#, c-format -+msgid "You can close this dialog and get %d minute to finish your work." -+msgid_plural "" -+"You can close this dialog and get %d minutes to finish your work." -+msgstr[0] "" -+"Вы можете закрыть это окно и получить еще %d минуту, чтобы закончить работу." -+msgstr[1] "" -+"Вы можете закрыть это окно и получить еще %d минуты, чтобы закончить работу." -+msgstr[2] "" -+"Вы можете закрыть это окно и получить еще %d минут, чтобы закончить работу." -+ -+#: ../extensions/updates-dialog/extension.js:114 -+#, c-format -+msgid "You can close this dialog and get %d hour to finish your work." -+msgid_plural "You can close this dialog and get %d hours to finish your work." -+msgstr[0] "" -+"Вы можете закрыть это окно и получить еще %d час, чтобы закончить работу." -+msgstr[1] "" -+"Вы можете закрыть это окно и получить еще %d часа, чтобы закончить работу." -+msgstr[2] "" -+"Вы можете закрыть это окно и получить еще %d часов, чтобы закончить работу." -+ -+#: ../extensions/updates-dialog/extension.js:145 -+msgid "Important security updates" -+msgstr "Важные обновления безопасности" -+ -+#: ../extensions/updates-dialog/extension.js:163 -+#: ../extensions/window-list/extension.js:111 -+msgid "Close" -+msgstr "Закрыть" -+ -+#: ../extensions/updates-dialog/extension.js:166 -+msgid "Restart & Install" -+msgstr "Перезапустить и установить" -+ -+#: ../extensions/updates-dialog/extension.js:217 -+msgid "Important security updates need to be installed now.\n" -+msgstr "Сейчас будут установлены важные обновления безопасности.\n" -+ -+#: ../extensions/updates-dialog/extension.js:218 -+#, c-format -+msgid "This computer will restart in %d second." -+msgid_plural "This computer will restart in %d seconds." -+msgstr[0] "Компьютер будет перезагружен через %d секунду." -+msgstr[1] "Компьютер будет перезагружен через %d секунды." -+msgstr[2] "Компьютер будет перезагружен через %d секунд." -+ - #: ../extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml.in.h:1 - msgid "Theme name" - msgstr "Название темы" -@@ -253,54 +302,50 @@ - msgid "The name of the theme, to be loaded from ~/.themes/name/gnome-shell" - msgstr "Название темы, загружаемой из ~/.themes/name/gnome-shell" - --#: ../extensions/window-list/extension.js:110 --msgid "Close" --msgstr "Закрыть" -- - # ну или "восстановить", правда тогда появляется неоднозначный повтор (unmaximize) --#: ../extensions/window-list/extension.js:120 -+#: ../extensions/window-list/extension.js:121 - msgid "Unminimize" - msgstr "Вернуть" - --#: ../extensions/window-list/extension.js:121 -+#: ../extensions/window-list/extension.js:122 - msgid "Minimize" - msgstr "Свернуть" - --#: ../extensions/window-list/extension.js:127 -+#: ../extensions/window-list/extension.js:128 - msgid "Unmaximize" - msgstr "Восстановить" - --#: ../extensions/window-list/extension.js:128 -+#: ../extensions/window-list/extension.js:129 - msgid "Maximize" - msgstr "Развернуть" - --#: ../extensions/window-list/extension.js:300 -+#: ../extensions/window-list/extension.js:400 - msgid "Minimize all" - msgstr "Свернуть все" - - # ну или "восстановить", правда тогда появляется неоднозначный повтор (unmaximize) --#: ../extensions/window-list/extension.js:308 -+#: ../extensions/window-list/extension.js:408 - msgid "Unminimize all" - msgstr "Вернуть все" - --#: ../extensions/window-list/extension.js:316 -+#: ../extensions/window-list/extension.js:416 - msgid "Maximize all" - msgstr "Развернуть все" - --#: ../extensions/window-list/extension.js:325 -+#: ../extensions/window-list/extension.js:425 - msgid "Unmaximize all" - msgstr "Восстановить все" - --#: ../extensions/window-list/extension.js:334 -+#: ../extensions/window-list/extension.js:434 - msgid "Close all" - msgstr "Закрыть все" - --#: ../extensions/window-list/extension.js:644 -+#: ../extensions/window-list/extension.js:718 - #: ../extensions/workspace-indicator/extension.js:30 - msgid "Workspace Indicator" - msgstr "Индикатор рабочей области" - --#: ../extensions/window-list/extension.js:798 -+#: ../extensions/window-list/extension.js:882 - msgid "Window List" - msgstr "Список окон" - -@@ -317,24 +362,39 @@ - "окон. Возможные значения: «never» — никогда; «auto» — автоматически; " - "«always» — всегда." - --#: ../extensions/window-list/prefs.js:30 -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:3 -+msgid "Show the window list on all monitors" -+msgstr "Показывать список окон на всех мониторах" -+ -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:4 -+msgid "" -+"Whether to show the window list on all connected monitors or only on the " -+"primary one." -+msgstr "" -+"Показывать ли список окон на всех подключенных мониторах или только на " -+"основном." -+ -+#: ../extensions/window-list/prefs.js:32 - msgid "Window Grouping" - msgstr "Группировка окон" - --#: ../extensions/window-list/prefs.js:49 -+#: ../extensions/window-list/prefs.js:50 - msgid "Never group windows" - msgstr "Никогда не группировать окна" - --#: ../extensions/window-list/prefs.js:50 -+#: ../extensions/window-list/prefs.js:51 - msgid "Group windows when space is limited" - msgstr "Группировать окна, если место ограничено" - --#: ../extensions/window-list/prefs.js:51 -+#: ../extensions/window-list/prefs.js:52 - msgid "Always group windows" - msgstr "Всегда группировать окна" - -+#: ../extensions/window-list/prefs.js:75 -+msgid "Show on all monitors" -+msgstr "Показывать на всех мониторах" -+ - #: ../extensions/workspace-indicator/prefs.js:141 --#| msgid "Workspace names:" - msgid "Workspace Names" - msgstr "Названия рабочих областей" - -@@ -343,6 +403,6 @@ - msgstr "Название" - - #: ../extensions/workspace-indicator/prefs.js:198 --#, javascript-format -+#, c-format - msgid "Workspace %d" - msgstr "Рабочая область %d" ---- gnome-shell-extensions-3.14.4/po/zh_CN.po 2014-10-07 17:24:02.000000000 +0530 -+++ gnome-shell-extensions-3.14.4/po/zh_CN.po 2016-06-25 07:52:33.501142878 +0530 -@@ -5,21 +5,20 @@ - # Aron Xu , 2011. - # tuhaihe <1132321739qq@gmail.com>, 2012, 2013. - # 甘露(Gan Lu) , 2013. --# -+# xhuang , 2016. #zanata - msgid "" - msgstr "" - "Project-Id-Version: gnome-shell-extensions master\n" --"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-" --"shell&keywords=I18N+L10N&component=extensions\n" --"POT-Creation-Date: 2014-05-31 18:51+0000\n" --"PO-Revision-Date: 2014-06-01 10:36+0800\n" --"Last-Translator: irisgyq \n" --"Language-Team: Chinese (China) \n" --"Language: zh_CN\n" -+"Report-Msgid-Bugs-To: \n" -+"POT-Creation-Date: 2016-04-19 10:07+0530\n" - "MIME-Version: 1.0\n" - "Content-Type: text/plain; charset=UTF-8\n" - "Content-Transfer-Encoding: 8bit\n" --"X-Generator: Poedit 1.5.4\n" -+"PO-Revision-Date: 2016-06-06 01:47+0000\n" -+"Last-Translator: xhuang \n" -+"Language-Team: Chinese (China) \n" -+"Language: zh-CN\n" -+"X-Generator: Zanata 3.8.2\n" - "Plural-Forms: nplurals=1; plural=0;\n" - - #: ../data/gnome-classic.desktop.in.h:1 -@@ -90,15 +89,15 @@ - msgid "Show only windows in the current workspace" - msgstr "仅显示当前工作区中的窗口" - --#: ../extensions/apps-menu/extension.js:39 -+#: ../extensions/apps-menu/extension.js:40 - msgid "Activities Overview" - msgstr "活动概览" - --#: ../extensions/apps-menu/extension.js:113 -+#: ../extensions/apps-menu/extension.js:115 - msgid "Favorites" - msgstr "收藏" - --#: ../extensions/apps-menu/extension.js:282 -+#: ../extensions/apps-menu/extension.js:291 - msgid "Applications" - msgstr "应用程序" - -@@ -110,8 +109,7 @@ - msgid "" - "A list of strings, each containing an application id (desktop file name), " - "followed by a colon and the workspace number" --msgstr "" --"一系列字符串,每个字符串包含一个应用程序标识(桌面文件名称)、冒号加工作区号" -+msgstr "一系列字符串,每个字符串包含一个应用程序标识(桌面文件名称)、冒号加工作区号" - - #: ../extensions/auto-move-windows/prefs.js:60 - msgid "Application" -@@ -135,15 +133,15 @@ - msgstr "添加" - - #: ../extensions/drive-menu/extension.js:106 --#, javascript-format -+#, c-format - msgid "Ejecting drive '%s' failed:" - msgstr "弹出驱动器“%s”失败:" - --#: ../extensions/drive-menu/extension.js:123 -+#: ../extensions/drive-menu/extension.js:124 - msgid "Removable devices" - msgstr "可移动设备" - --#: ../extensions/drive-menu/extension.js:150 -+#: ../extensions/drive-menu/extension.js:149 - msgid "Open File" - msgstr "打开文件管理器" - -@@ -165,13 +163,14 @@ - msgid "Message" - msgstr "消息" - -+#. TRANSLATORS: Example is the name of the extension, should not be -+#. translated - #: ../extensions/example/prefs.js:43 - msgid "" - "Example aims to show how to build well behaved extensions for the Shell and " - "as such it has little functionality on its own.\n" - "Nevertheless it's possible to customize the greeting message." --msgstr "" --"示例意在展示如何为 Shell 创建良好工作的扩展,本身功能有限。\n" -+msgstr "示例意在展示如何为 Shell 创建良好工作的扩展,本身功能有限。\n" - "尽管如此,它还是具备定制祝福语的功能。" - - #: ../extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml.in.h:1 -@@ -183,9 +182,7 @@ - "Try to use more screen for placing window thumbnails by adapting to screen " - "aspect ratio, and consolidating them further to reduce the bounding box. " - "This setting applies only with the natural placement strategy." --msgstr "" --"尝试通过适应屏幕宽高比,以及相互组合以减少包围框,在摆放窗口缩略图时利用更多" --"屏幕空间。此设置只应用于自然摆放策略。" -+msgstr "尝试通过适应屏幕宽高比,以及相互组合以减少包围框,在摆放窗口缩略图时利用更多屏幕空间。此设置只应用于自然摆放策略。" - - #: ../extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml.in.h:3 - msgid "Place window captions on top" -@@ -197,8 +194,7 @@ - "shell default of placing it at the bottom. Changing this setting requires " - "restarting the shell to have any effect." - msgstr "" --"如果设置为 true,则将窗口说明文字放置在对应窗口的缩略图上方,而不是默认的下" --"方。修改此设置需要重启 GNOME Shell 以使设置生效。" -+"如果设置为 true,则将窗口说明文字放置在对应窗口的缩略图上方,而不是默认的下方。修改此设置需要重启 GNOME Shell 以使设置生效。" - - #: ../extensions/places-menu/extension.js:78 - #: ../extensions/places-menu/extension.js:81 -@@ -206,7 +202,7 @@ - msgstr "位置" - - #: ../extensions/places-menu/placeDisplay.js:57 --#, javascript-format -+#, c-format - msgid "Failed to launch \"%s\"" - msgstr "无法启动“%s”" - -@@ -231,6 +227,46 @@ - msgid "Memory" - msgstr "内存" - -+#: ../extensions/updates-dialog/extension.js:108 -+msgid "Important security updates need to be installed.\n" -+msgstr "需要安装重要的安全更新。\n" -+ -+#: ../extensions/updates-dialog/extension.js:110 -+#, c-format -+msgid "You can close this dialog and get %d minute to finish your work." -+msgid_plural "" -+"You can close this dialog and get %d minutes to finish your work." -+msgstr[0] "您可以关闭这个对话框并有 %d 分钟来完成工作。" -+ -+#: ../extensions/updates-dialog/extension.js:114 -+#, c-format -+msgid "You can close this dialog and get %d hour to finish your work." -+msgid_plural "You can close this dialog and get %d hours to finish your work." -+msgstr[0] "您可以关闭这个对话框并有 %d 小时来完成工作。" -+ -+#: ../extensions/updates-dialog/extension.js:145 -+msgid "Important security updates" -+msgstr "重要的安全更新" -+ -+#: ../extensions/updates-dialog/extension.js:163 -+#: ../extensions/window-list/extension.js:111 -+msgid "Close" -+msgstr "关闭" -+ -+#: ../extensions/updates-dialog/extension.js:166 -+msgid "Restart & Install" -+msgstr "重启和安装" -+ -+#: ../extensions/updates-dialog/extension.js:217 -+msgid "Important security updates need to be installed now.\n" -+msgstr "现在需要安装重要的安全更新。\n" -+ -+#: ../extensions/updates-dialog/extension.js:218 -+#, c-format -+msgid "This computer will restart in %d second." -+msgid_plural "This computer will restart in %d seconds." -+msgstr[0] "这台电脑将在 %d 秒后重启。" -+ - #: ../extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml.in.h:1 - msgid "Theme name" - msgstr "主题名称" -@@ -239,52 +275,48 @@ - msgid "The name of the theme, to be loaded from ~/.themes/name/gnome-shell" - msgstr "从 ~/.themes/name/gnome-shell 加载的主题名称" - --#: ../extensions/window-list/extension.js:110 --msgid "Close" --msgstr "关闭" -- --#: ../extensions/window-list/extension.js:120 -+#: ../extensions/window-list/extension.js:121 - msgid "Unminimize" - msgstr "取消最小化" - --#: ../extensions/window-list/extension.js:121 -+#: ../extensions/window-list/extension.js:122 - msgid "Minimize" - msgstr "最小化" - --#: ../extensions/window-list/extension.js:127 -+#: ../extensions/window-list/extension.js:128 - msgid "Unmaximize" - msgstr "取消最大化" - --#: ../extensions/window-list/extension.js:128 -+#: ../extensions/window-list/extension.js:129 - msgid "Maximize" - msgstr "最大化" - --#: ../extensions/window-list/extension.js:300 -+#: ../extensions/window-list/extension.js:400 - msgid "Minimize all" - msgstr "全部最小化" - --#: ../extensions/window-list/extension.js:308 -+#: ../extensions/window-list/extension.js:408 - msgid "Unminimize all" - msgstr "全部取消最小化" - --#: ../extensions/window-list/extension.js:316 -+#: ../extensions/window-list/extension.js:416 - msgid "Maximize all" - msgstr "全部最大化" - --#: ../extensions/window-list/extension.js:325 -+#: ../extensions/window-list/extension.js:425 - msgid "Unmaximize all" - msgstr "全部取消最大化" - --#: ../extensions/window-list/extension.js:334 -+#: ../extensions/window-list/extension.js:434 - msgid "Close all" - msgstr "全部关闭" - --#: ../extensions/window-list/extension.js:644 -+#: ../extensions/window-list/extension.js:718 - #: ../extensions/workspace-indicator/extension.js:30 - msgid "Workspace Indicator" - msgstr "工作区指示器" - --#: ../extensions/window-list/extension.js:798 -+#: ../extensions/window-list/extension.js:882 - msgid "Window List" - msgstr "窗口列表" - -@@ -296,26 +328,38 @@ - msgid "" - "Decides when to group windows from the same application on the window list. " - "Possible values are \"never\", \"auto\" and \"always\"." --msgstr "" --"决定何时对窗口列表上的同一应用的窗口进行分组。可用值有“never”(从" --"不)、“auto”(自动)和“always”(总是)。" -+msgstr "决定何时对窗口列表上的同一应用的窗口进行分组。可用值有“never”(从不)、“auto”(自动)和“always”(总是)。" -+ -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:3 -+msgid "Show the window list on all monitors" -+msgstr "在所有显示器上显示窗口列表" - --#: ../extensions/window-list/prefs.js:30 -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:4 -+msgid "" -+"Whether to show the window list on all connected monitors or only on the " -+"primary one." -+msgstr "是否在所有连接的显示器还是主要显示器上显示窗口列表。" -+ -+#: ../extensions/window-list/prefs.js:32 - msgid "Window Grouping" - msgstr "窗口分组" - --#: ../extensions/window-list/prefs.js:49 -+#: ../extensions/window-list/prefs.js:50 - msgid "Never group windows" - msgstr "从不将窗口分组" - --#: ../extensions/window-list/prefs.js:50 -+#: ../extensions/window-list/prefs.js:51 - msgid "Group windows when space is limited" - msgstr "当空间有限时将窗口分组" - --#: ../extensions/window-list/prefs.js:51 -+#: ../extensions/window-list/prefs.js:52 - msgid "Always group windows" - msgstr "总是对窗口分组" - -+#: ../extensions/window-list/prefs.js:75 -+msgid "Show on all monitors" -+msgstr "在所有显示器上显示" -+ - #: ../extensions/workspace-indicator/prefs.js:141 - msgid "Workspace Names" - msgstr "工作区名称" -@@ -325,215 +369,6 @@ - msgstr "名称" - - #: ../extensions/workspace-indicator/prefs.js:198 --#, javascript-format -+#, c-format - msgid "Workspace %d" - msgstr "工作区 %d" -- --#~ msgid "Suspend" --#~ msgstr "挂起" -- --#~ msgid "Hibernate" --#~ msgstr "休眠" -- --#~ msgid "Power Off" --#~ msgstr "关机" -- --#~ msgid "Enable suspending" --#~ msgstr "启用挂起" -- --#~ msgid "Control the visibility of the Suspend menu item" --#~ msgstr "控制“挂起”菜单项的可见性" -- --#~ msgid "Enable hibernating" --#~ msgstr "启用休眠" -- --#~ msgid "Control the visibility of the Hibernate menu item" --#~ msgstr "控制“休眠”菜单项的可见性" -- --#~ msgid "Normal" --#~ msgstr "正常" -- --#~ msgid "Left" --#~ msgstr "左" -- --#~ msgid "Right" --#~ msgstr "右" -- --#~ msgid "Upside-down" --#~ msgstr "上下翻转" -- --#~ msgid "Display" --#~ msgstr "显示" -- --#~ msgid "Display Settings" --#~ msgstr "显示设置" -- --#~ msgid "The application icon mode." --#~ msgstr "应用程序图标模式。" -- --#~ msgid "" --#~ "Configures how the windows are shown in the switcher. Valid possibilities " --#~ "are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-" --#~ "only' (shows only the application icon) or 'both'." --#~ msgstr "" --#~ "配置窗口在切换器中的显示方式。有效值包括“仅缩略图”(显示窗口的缩略图)、“仅" --#~ "应用程序图标”(仅显示应用程序图标)或“全部”。" -- --#~ msgid "Drag here to add favorites" --#~ msgstr "拖放到这里以添加收藏" -- --#~ msgid "New Window" --#~ msgstr "新窗口" -- --#~ msgid "Quit Application" --#~ msgstr "退出应用程序" -- --#~ msgid "Remove from Favorites" --#~ msgstr "移除收藏" -- --#~ msgid "Position of the dock" --#~ msgstr "Dock 位置" -- --#~ msgid "" --#~ "Sets the position of the dock in the screen. Allowed values are 'right' " --#~ "or 'left'" --#~ msgstr "设置 Dock 在屏幕上的位置。允许的值有“右”或“左”。" -- --#~ msgid "Icon size" --#~ msgstr "图标大小" -- --#~ msgid "Sets icon size of the dock." --#~ msgstr "设置 Dock 上的图标大小。" -- --#~ msgid "Enable/disable autohide" --#~ msgstr "启用/禁用自动隐藏" -- --#~ msgid "Autohide effect" --#~ msgstr "自动隐藏效果" -- --#~ msgid "" --#~ "Sets the effect of the hide dock. Allowed values are 'resize', 'rescale' " --#~ "and 'move'" --#~ msgstr "设置隐藏 Dock 的效果。允许的值有“resize”、“rescale”和“move”" -- --#~ msgid "Autohide duration" --#~ msgstr "自动隐藏时间" -- --#~ msgid "Sets the time duration of the autohide effect." --#~ msgstr "设置自动隐藏的动画过渡时间。" -- --#~ msgid "Monitor" --#~ msgstr "显示器" -- --#~ msgid "" --#~ "Sets monitor to display dock in. The default value (-1) is the primary " --#~ "monitor." --#~ msgstr "设置显示 Dock 的显示器。默认值(-1)是主显示器。" -- --#~ msgid "%s is away." --#~ msgstr "%s 不在。" -- --#~ msgid "%s is offline." --#~ msgstr "%s 离线。" -- --#~ msgid "%s is online." --#~ msgstr "%s 在线。" -- --#~ msgid "%s is busy." --#~ msgstr "%s 忙碌。" -- --#~ msgid "Devices" --#~ msgstr "设备" -- --#~ msgid "Bookmarks" --#~ msgstr "书签" -- --#~ msgid "Network" --#~ msgstr "网络" -- --#~ msgid "File System" --#~ msgstr "文件系统" -- --#~ msgid "The alt tab behaviour." --#~ msgstr "Alt Tab 行为。" -- --#~ msgid "" --#~ "Sets the Alt-Tab behaviour. Possible values are: all_thumbnails and " --#~ "workspace_icons. See the configuration dialogs for details." --#~ msgstr "" --#~ "设置 Alt-Tab 行为。可取的值有:native(原生)、all_thumbnails(所有缩略图) " --#~ "和 workspace_icons(工作区内图标)。详情参阅配置对话框。" -- --#~ msgid "" --#~ "This mode presents all applications from all workspaces in one selection " --#~ "list. Instead of using the application icon of every window, it uses " --#~ "small thumbnails resembling the window itself." --#~ msgstr "" --#~ "此模式将在一个选择列表中展示全部工作区中的所有应用程序。它使用小缩略图而非" --#~ "各窗口的应用程序图标来代表窗口。" -- --#~ msgid "Workspace & Icons" --#~ msgstr "工作区和图标" -- --#~ msgid "" --#~ "This mode let's you switch between the applications of your current " --#~ "workspace and gives you additionally the option to switch to the last " --#~ "used application of your previous workspace. This is always the last " --#~ "symbol in the list and is segregated by a separator/vertical line if " --#~ "available. \n" --#~ "Every window is represented by its application icon." --#~ msgstr "" --#~ "此模式让您在当前工作区中的应用程序及上个工作区、上次使用的应用程序间切换。" --#~ "上次使用这项总是列表中的最后一个符号,并以分隔符或竖线区别(若可能)。\n" --#~ "每个窗口以其应用程序图标展示。" -- --#~ msgid "Move current selection to front before closing the popup" --#~ msgstr "在关闭弹出界面前将当前选择移到前面" -- --#~ msgid "" --#~ "The Alternate Tab can be used in different modes, that affect the way " --#~ "windows are chosen and presented." --#~ msgstr "" --#~ "Alternate Tab 有多种使用模式,所使用模式会影响窗口展示和选择的方式。" -- --#~ msgid "Removable Devices" --#~ msgstr "可移动设备" -- --#~ msgid "Configure display settings..." --#~ msgstr "配置显示设置..." -- --#~ msgid "Notifications" --#~ msgstr "提示" -- --#~ msgid "Online Accounts" --#~ msgstr "在线帐号" -- --#~ msgid "Lock Screen" --#~ msgstr "锁定屏幕" -- --#~ msgid "Switch User" --#~ msgstr "切换用户" -- --#~ msgid "Log Out..." --#~ msgstr "注销..." -- --#~ msgid "Alt Tab Behaviour" --#~ msgstr "Alt Tab 行为" -- --#~ msgid "Cancel" --#~ msgstr "取消" -- --#~ msgid "Ask the user for a default behaviour if true." --#~ msgstr "如果设置为 true,询问用户设置一个默认行为。" -- --#~ msgid "Indicates if Alternate Tab is newly installed" --#~ msgstr "指示“候选标签”是否为最近安装的" -- --#~ msgid "Window placement strategy" --#~ msgstr "窗口放置策略" -- --#~ msgid "Available" --#~ msgstr "在线" -- --#~ msgid "Busy" --#~ msgstr "忙碌" ---- gnome-shell-extensions-3.14.4/po/zh_TW.po 2015-03-24 01:08:10.000000000 +0530 -+++ gnome-shell-extensions-3.14.4/po/zh_TW.po 2016-06-25 07:52:33.503142878 +0530 -@@ -2,21 +2,21 @@ - # Copyright (C) 2011 gnome-shell-extensions's COPYRIGHT HOLDER - # This file is distributed under the same license as the gnome-shell-extensions package. - # Cheng-Chia Tseng , 2011. --# -+# ccheng , 2016. #zanata - msgid "" - msgstr "" - "Project-Id-Version: gnome-shell-extensions gnome-3-0\n" --"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-" --"shell&keywords=I18N+L10N&component=extensions\n" --"POT-Creation-Date: 2014-08-18 19:38+0000\n" --"PO-Revision-Date: 2014-08-19 10:13+0800\n" --"Last-Translator: Chao-Hsiung Liao \n" --"Language-Team: Chinese (Taiwan) \n" --"Language: zh_TW\n" -+"Report-Msgid-Bugs-To: \n" -+"POT-Creation-Date: 2016-04-19 10:07+0530\n" - "MIME-Version: 1.0\n" - "Content-Type: text/plain; charset=UTF-8\n" - "Content-Transfer-Encoding: 8bit\n" --"X-Generator: Poedit 1.6.5\n" -+"PO-Revision-Date: 2016-05-18 06:11+0000\n" -+"Last-Translator: ccheng \n" -+"Language-Team: Chinese (Taiwan) \n" -+"Language: zh-TW\n" -+"X-Generator: Zanata 3.8.2\n" -+"Plural-Forms: nplurals=1; plural=0\n" - - #: ../data/gnome-classic.desktop.in.h:1 - #: ../data/gnome-classic.session.desktop.in.in.h:1 -@@ -49,14 +49,10 @@ - msgstr "標頭列按鈕的配置" - - #: ../data/org.gnome.shell.extensions.classic-overrides.gschema.xml.in.h:4 --#| msgid "" --#| "This key overrides the key in org.gnome.mutter when running GNOME Shell." - msgid "" - "This key overrides the key in org.gnome.desktop.wm.preferences when running " - "GNOME Shell." --msgstr "" --"當執行 GNOME Shell 時這個設定鍵會覆蓋在 org.gnome.desktop.wm.preferences 中的" --"設定值。" -+msgstr "當執行 GNOME Shell 時這個設定鍵會覆蓋在 org.gnome.desktop.wm.preferences 中的設定值。" - - #: ../data/org.gnome.shell.extensions.classic-overrides.gschema.xml.in.h:5 - msgid "Enable edge tiling when dropping windows on screen edges" -@@ -90,15 +86,15 @@ - msgid "Show only windows in the current workspace" - msgstr "僅顯示目前工作區中的視窗" - --#: ../extensions/apps-menu/extension.js:39 -+#: ../extensions/apps-menu/extension.js:40 - msgid "Activities Overview" - msgstr "活動概覽" - --#: ../extensions/apps-menu/extension.js:113 -+#: ../extensions/apps-menu/extension.js:115 - msgid "Favorites" - msgstr "喜好" - --#: ../extensions/apps-menu/extension.js:282 -+#: ../extensions/apps-menu/extension.js:291 - msgid "Applications" - msgstr "應用程式" - -@@ -110,9 +106,7 @@ - msgid "" - "A list of strings, each containing an application id (desktop file name), " - "followed by a colon and the workspace number" --msgstr "" --"字串的清單,每個都包含一個應用程式 id (桌面檔名稱),後面接著半形分號 \";\" 與" --"工作區號碼" -+msgstr "字串的清單,每個都包含一個應用程式 id (桌面檔名稱),後面接著半形分號 \";\" 與工作區號碼" - - #: ../extensions/auto-move-windows/prefs.js:60 - msgid "Application" -@@ -124,7 +118,6 @@ - msgstr "工作區" - - #: ../extensions/auto-move-windows/prefs.js:85 --#| msgid "Add rule" - msgid "Add Rule" - msgstr "加入規則" - -@@ -137,15 +130,15 @@ - msgstr "加入" - - #: ../extensions/drive-menu/extension.js:106 --#, javascript-format -+#, c-format - msgid "Ejecting drive '%s' failed:" - msgstr "裝置「%s」退出失敗:" - --#: ../extensions/drive-menu/extension.js:123 -+#: ../extensions/drive-menu/extension.js:124 - msgid "Removable devices" - msgstr "可移除式裝置" - --#: ../extensions/drive-menu/extension.js:150 -+#: ../extensions/drive-menu/extension.js:149 - msgid "Open File" - msgstr "開啟檔案" - -@@ -164,18 +157,17 @@ - msgstr "若不是空的,它則包含點擊面板時會顯示的文字。" - - #: ../extensions/example/prefs.js:30 --#| msgid "Message:" - msgid "Message" - msgstr "訊息:" - -+#. TRANSLATORS: Example is the name of the extension, should not be -+#. translated - #: ../extensions/example/prefs.js:43 - msgid "" - "Example aims to show how to build well behaved extensions for the Shell and " - "as such it has little functionality on its own.\n" - "Nevertheless it's possible to customize the greeting message." --msgstr "" --"Example 旨要顯示如何替 Shell 建立行為良好的擴充套件,但它自己本身沒有什麼功" --"能。\n" -+msgstr "Example 旨要顯示如何替 Shell 建立行為良好的擴充套件,但它自己本身沒有什麼功能。\n" - "不過,它可以讓您自訂歡迎訊息。" - - #: ../extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml.in.h:1 -@@ -187,9 +179,7 @@ - "Try to use more screen for placing window thumbnails by adapting to screen " - "aspect ratio, and consolidating them further to reduce the bounding box. " - "This setting applies only with the natural placement strategy." --msgstr "" --"藉由適應螢幕長寬比來試著使用更多螢幕空間放置視窗縮圖,進一步聯合它們來減少邊" --"界盒。這個設定僅適用於自然放置策略。" -+msgstr "藉由適應螢幕長寬比來試著使用更多螢幕空間放置視窗縮圖,進一步聯合它們來減少邊界盒。這個設定僅適用於自然放置策略。" - - #: ../extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml.in.h:3 - msgid "Place window captions on top" -@@ -200,9 +190,7 @@ - "If true, place window captions on top the respective thumbnail, overriding " - "shell default of placing it at the bottom. Changing this setting requires " - "restarting the shell to have any effect." --msgstr "" --"如果為真,在對映的縮圖頂端放置視窗說明標題,凌駕 Shell 將它放置在底部的預設" --"值。變更這個設定值需要重新啟動 Shell 來套用效果。" -+msgstr "如果為真,在對映的縮圖頂端放置視窗說明標題,凌駕 Shell 將它放置在底部的預設值。變更這個設定值需要重新啟動 Shell 來套用效果。" - - #: ../extensions/places-menu/extension.js:78 - #: ../extensions/places-menu/extension.js:81 -@@ -210,7 +198,7 @@ - msgstr "位置" - - #: ../extensions/places-menu/placeDisplay.js:57 --#, javascript-format -+#, c-format - msgid "Failed to launch \"%s\"" - msgstr "無法啟動「%s」" - -@@ -235,6 +223,46 @@ - msgid "Memory" - msgstr "記憶體" - -+#: ../extensions/updates-dialog/extension.js:108 -+msgid "Important security updates need to be installed.\n" -+msgstr "需要安裝重要的安全性更新。\n" -+ -+#: ../extensions/updates-dialog/extension.js:110 -+#, c-format -+msgid "You can close this dialog and get %d minute to finish your work." -+msgid_plural "" -+"You can close this dialog and get %d minutes to finish your work." -+msgstr[0] "您可以關閉這對話視窗,並取得 %d 分鐘來完成工作。" -+ -+#: ../extensions/updates-dialog/extension.js:114 -+#, c-format -+msgid "You can close this dialog and get %d hour to finish your work." -+msgid_plural "You can close this dialog and get %d hours to finish your work." -+msgstr[0] "您可以關閉這對話視窗,並取得 %d 小時來完成工作。" -+ -+#: ../extensions/updates-dialog/extension.js:145 -+msgid "Important security updates" -+msgstr "重要安全性更新" -+ -+#: ../extensions/updates-dialog/extension.js:163 -+#: ../extensions/window-list/extension.js:111 -+msgid "Close" -+msgstr "關閉" -+ -+#: ../extensions/updates-dialog/extension.js:166 -+msgid "Restart & Install" -+msgstr "重新啟動並安裝" -+ -+#: ../extensions/updates-dialog/extension.js:217 -+msgid "Important security updates need to be installed now.\n" -+msgstr "現在需要安裝重要的安全性更新。\n" -+ -+#: ../extensions/updates-dialog/extension.js:218 -+#, c-format -+msgid "This computer will restart in %d second." -+msgid_plural "This computer will restart in %d seconds." -+msgstr[0] "電腦會在 %d 秒後重新啟動。" -+ - #: ../extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml.in.h:1 - msgid "Theme name" - msgstr "主題名稱" -@@ -243,52 +271,48 @@ - msgid "The name of the theme, to be loaded from ~/.themes/name/gnome-shell" - msgstr "主題的名稱,要從 ~/.themes/name/gnome-shell 載入" - --#: ../extensions/window-list/extension.js:110 --msgid "Close" --msgstr "關閉" -- --#: ../extensions/window-list/extension.js:120 -+#: ../extensions/window-list/extension.js:121 - msgid "Unminimize" - msgstr "取消最小化" - --#: ../extensions/window-list/extension.js:121 -+#: ../extensions/window-list/extension.js:122 - msgid "Minimize" - msgstr "最小化" - --#: ../extensions/window-list/extension.js:127 -+#: ../extensions/window-list/extension.js:128 - msgid "Unmaximize" - msgstr "取消最大化" - --#: ../extensions/window-list/extension.js:128 -+#: ../extensions/window-list/extension.js:129 - msgid "Maximize" - msgstr "最大化" - --#: ../extensions/window-list/extension.js:300 -+#: ../extensions/window-list/extension.js:400 - msgid "Minimize all" - msgstr "全部最小化" - --#: ../extensions/window-list/extension.js:308 -+#: ../extensions/window-list/extension.js:408 - msgid "Unminimize all" - msgstr "全部取消最小化" - --#: ../extensions/window-list/extension.js:316 -+#: ../extensions/window-list/extension.js:416 - msgid "Maximize all" - msgstr "全部最大化" - --#: ../extensions/window-list/extension.js:325 -+#: ../extensions/window-list/extension.js:425 - msgid "Unmaximize all" - msgstr "全部取消最大化" - --#: ../extensions/window-list/extension.js:334 -+#: ../extensions/window-list/extension.js:434 - msgid "Close all" - msgstr "全部關閉" - --#: ../extensions/window-list/extension.js:644 -+#: ../extensions/window-list/extension.js:718 - #: ../extensions/workspace-indicator/extension.js:30 - msgid "Workspace Indicator" - msgstr "工作區指示器" - --#: ../extensions/window-list/extension.js:798 -+#: ../extensions/window-list/extension.js:882 - msgid "Window List" - msgstr "視窗清單" - -@@ -300,27 +324,39 @@ - msgid "" - "Decides when to group windows from the same application on the window list. " - "Possible values are \"never\", \"auto\" and \"always\"." --msgstr "" --"決定在視窗清單中何時群組視窗。可能的數值有「never」、「auto」、「always」。" -+msgstr "決定在視窗清單中何時群組視窗。可能的數值有「never」、「auto」、「always」。" -+ -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:3 -+msgid "Show the window list on all monitors" -+msgstr "在所有螢幕上顯示視窗列表" -+ -+#: ../extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in.h:4 -+msgid "" -+"Whether to show the window list on all connected monitors or only on the " -+"primary one." -+msgstr "是否要在所有連上的螢幕上顯示視窗清單,還是只在主螢幕上顯示。" - --#: ../extensions/window-list/prefs.js:30 -+#: ../extensions/window-list/prefs.js:32 - msgid "Window Grouping" - msgstr "視窗群組" - --#: ../extensions/window-list/prefs.js:49 -+#: ../extensions/window-list/prefs.js:50 - msgid "Never group windows" - msgstr "永不群組視窗" - --#: ../extensions/window-list/prefs.js:50 -+#: ../extensions/window-list/prefs.js:51 - msgid "Group windows when space is limited" - msgstr "當空間受限時群組視窗" - --#: ../extensions/window-list/prefs.js:51 -+#: ../extensions/window-list/prefs.js:52 - msgid "Always group windows" - msgstr "永遠群組視窗" - -+#: ../extensions/window-list/prefs.js:75 -+msgid "Show on all monitors" -+msgstr "在所有螢幕上顯示" -+ - #: ../extensions/workspace-indicator/prefs.js:141 --#| msgid "Workspace names:" - msgid "Workspace Names" - msgstr "工作區名稱" - -@@ -329,256 +365,6 @@ - msgstr "名稱" - - #: ../extensions/workspace-indicator/prefs.js:198 --#, javascript-format -+#, c-format - msgid "Workspace %d" - msgstr "工作區 %d" -- --#~ msgid "Normal" --#~ msgstr "一般" -- --#~ msgid "Left" --#~ msgstr "左" -- --#~ msgid "Right" --#~ msgstr "右" -- --#~ msgid "Upside-down" --#~ msgstr "上下顛倒" -- --#~ msgid "Display" --#~ msgstr "顯示" -- --#~ msgid "Display Settings" --#~ msgstr "顯示設定值" -- --#~ msgid "Suspend" --#~ msgstr "暫停" -- --#~ msgid "Hibernate" --#~ msgstr "休眠" -- --#~ msgid "Power Off" --#~ msgstr "關閉電源" -- --#~ msgid "Enable suspending" --#~ msgstr "啟用暫停" -- --#~ msgid "Control the visibility of the Suspend menu item" --#~ msgstr "控制暫停選單項目的可見性" -- --#~ msgid "Enable hibernating" --#~ msgstr "啟用休眠" -- --#~ msgid "Control the visibility of the Hibernate menu item" --#~ msgstr "控制休眠選單項目的可見性" -- --#~ msgid "The application icon mode." --#~ msgstr "應用程式圖示模式。" -- --#~ msgid "" --#~ "Configures how the windows are shown in the switcher. Valid possibilities " --#~ "are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-" --#~ "only' (shows only the application icon) or 'both'." --#~ msgstr "" --#~ "設定視窗在切換器中顯示的方式。有效的選項為「thumbnail-only」(顯示視窗的縮" --#~ "圖)、「app-icon-only」(僅顯示應用程式圖示),或「both」(兩者)" -- --#~ msgid "Devices" --#~ msgstr "裝置" -- --#~ msgid "Bookmarks" --#~ msgstr "書籤" -- --#~ msgid "Network" --#~ msgstr "網路" -- --#~ msgid "File System" --#~ msgstr "檔案系統" -- --#~ msgid "" --#~ "Sets the Alt-Tab behaviour. Possible values are: all_thumbnails and " --#~ "workspace_icons. See the configuration dialogs for details." --#~ msgstr "" --#~ "設定 Alt-Tab 行為。可用的值為:all_thumbnails、workspace_icons。請見組態對" --#~ "話盒瞭解詳細資訊。" -- --#~ msgid "The alt tab behaviour." --#~ msgstr "alt tab 按鍵行為。" -- --#~ msgid "" --#~ "This mode presents all applications from all workspaces in one selection " --#~ "list. Instead of using the application icon of every window, it uses " --#~ "small thumbnails resembling the window itself." --#~ msgstr "" --#~ "這個模式會將所有工作區的應用程式呈現於一個選取清單中。它使用小縮圖來象徵視" --#~ "窗本身,而不是使用應用程式圖示來代表每個視窗。" -- --#~ msgid "Workspace & Icons" --#~ msgstr "工作區與圖示" -- --#~ msgid "" --#~ "This mode let's you switch between the applications of your current " --#~ "workspace and gives you additionally the option to switch to the last " --#~ "used application of your previous workspace. This is always the last " --#~ "symbol in the list and is segregated by a separator/vertical line if " --#~ "available. \n" --#~ "Every window is represented by its application icon." --#~ msgstr "" --#~ "這個模式讓您在您目前工作區中的應用程式之間切換,而且給您額外的選項來切換至" --#~ "上個工作區中您上次使用的應用程式;它總會是清單中的最後一個圖像,而且可能的" --#~ "話會用分隔符/垂直線隔離開來。\n" --#~ "每個視窗會以其應用程式圖示呈現。" -- --#~ msgid "Move current selection to front before closing the popup" --#~ msgstr "在關閉彈出視窗之前,移動目前選取的項目至前方" -- --#~ msgid "" --#~ "The Alternate Tab can be used in different modes, that affect the way " --#~ "windows are chosen and presented." --#~ msgstr "Alternate Tab 可以使用不同的模式,會影響到選擇與呈現視窗的方式。" -- --#~ msgid "Drag here to add favorites" --#~ msgstr "拖曳至此處以加入喜好" -- --#~ msgid "New Window" --#~ msgstr "新視窗" -- --#~ msgid "Quit Application" --#~ msgstr "退出應用程式" -- --#~ msgid "Remove from Favorites" --#~ msgstr "自喜好移除" -- --#~ msgid "Autohide duration" --#~ msgstr "自動隱藏時間" -- --#~ msgid "Autohide effect" --#~ msgstr "自動隱藏效果" -- --#~ msgid "Enable/disable autohide" --#~ msgstr "啟用/停用自動隱藏" -- --#~ msgid "Icon size" --#~ msgstr "圖示大小" -- --#~ msgid "Position of the dock" --#~ msgstr "Dock 的位置" -- --#~ msgid "Sets icon size of the dock." --#~ msgstr "設定 Dock 的圖示大小。" -- --#~ msgid "" --#~ "Sets the effect of the hide dock. Allowed values are 'resize', 'rescale' " --#~ "and 'move'" --#~ msgstr "設定隱藏 Dock 的效果。可用的值為「resize」、「rescale」、「move」" -- --#~ msgid "" --#~ "Sets the position of the dock in the screen. Allowed values are 'right' " --#~ "or 'left'" --#~ msgstr "設定 Dock 在螢幕的位置。可用的值為「right」(右) 或「left」(左)" -- --#~ msgid "Sets the time duration of the autohide effect." --#~ msgstr "設定自動隱藏效果的時間長度。" -- --#~ msgid "%s is away." --#~ msgstr "%s 已離開。" -- --#~ msgid "%s is offline." --#~ msgstr "%s 現在離線。" -- --#~ msgid "%s is online." --#~ msgstr "%s 現在上線。" -- --#~ msgid "%s is busy." --#~ msgstr "%s 正忙碌。" -- --#~ msgid "Configure display settings..." --#~ msgstr "設定顯示器設定值..." -- --#~ msgid "Notifications" --#~ msgstr "通知" -- --#~ msgid "Online Accounts" --#~ msgstr "線上帳號" -- --#~ msgid "Lock Screen" --#~ msgstr "鎖定畫面" -- --#~ msgid "Switch User" --#~ msgstr "切換使用者" -- --#~ msgid "Log Out..." --#~ msgstr "登出..." -- --#~ msgid "" --#~ "This is the first time you use the Alternate Tab extension. \n" --#~ "Please choose your preferred behaviour:\n" --#~ "\n" --#~ "All & Thumbnails:\n" --#~ " This mode presents all applications from all workspaces in one " --#~ "selection \n" --#~ " list. Instead of using the application icon of every window, it uses " --#~ "small \n" --#~ " thumbnails resembling the window itself. \n" --#~ "\n" --#~ "Workspace & Icons:\n" --#~ " This mode let's you switch between the applications of your current \n" --#~ " workspace and gives you additionally the option to switch to the last " --#~ "used \n" --#~ " application of your previous workspace. This is always the last " --#~ "symbol in \n" --#~ " the list and is segregated by a separator/vertical line if " --#~ "available. \n" --#~ " Every window is represented by its application icon. \n" --#~ "\n" --#~ "Native:\n" --#~ " This mode is the native GNOME 3 behaviour or in other words: " --#~ "Clicking \n" --#~ " native switches the Alternate Tab extension off. \n" --#~ msgstr "" --#~ "這是您第一次使用 Alternate Tab 擴充套件。\n" --#~ "請選擇您偏好的行為:\n" --#~ "\n" --#~ "全部與縮圖:\n" --#~ " 這個模式會在一個選取清單中展現所有工作區的應用程式。\n" --#~ " 每個視窗不使用應用程式圖示,而是採用象徵視窗自己本身\n" --#~ " 的縮圖。\n" --#~ "\n" --#~ "工作區與圖示:\n" --#~ " 這個模式讓您在目前工作區的應用程式間切換,並額外給您\n" --#~ " 切換至上次使用的前個工作區應用程式的選項。它會是清單\n" --#~ " 中最後一個圖像,並且可能的話會用分隔符或垂直線分開。\n" --#~ " 每個視窗是由其應用程式圖示所表示。\n" --#~ "\n" --#~ "原生:\n" --#~ " 這個模式是 GNOME 3 的原生行為,換句話說:按下原生\n" --#~ " 會關閉 Alternate Tab 擴充套件。\n" -- --#~ msgid "Alt Tab Behaviour" --#~ msgstr "Alt Tab 行為" -- --#~ msgid "Cancel" --#~ msgstr "取消" -- --#~ msgid "Ask the user for a default behaviour if true." --#~ msgstr "若為真,詢問使用者預設行為。" -- --#~ msgid "Indicates if Alternate Tab is newly installed" --#~ msgstr "指示 Alternate Tab 是否為新安裝" -- --#~ msgid "" --#~ "The algorithm used to layout thumbnails in the overview. 'grid' to use " --#~ "the default grid based algorithm, 'natural' to use another one that " --#~ "reflects more the position and size of the actual window" --#~ msgstr "" --#~ "用來配置縮圖的演算法。「grid」是採用預設的網格基礎演算法,「natural」則採" --#~ "用另一種反映視窗位置與實際大小的演算法。" -- --#~ msgid "Window placement strategy" --#~ msgstr "視窗擺放策略" -- --#~ msgid "Available" --#~ msgstr "有空" -- --#~ msgid "Busy" --#~ msgstr "忙碌" diff --git a/SOURCES/multi-monitor-window-list.patch b/SOURCES/multi-monitor-window-list.patch deleted file mode 100644 index 525a702..0000000 --- a/SOURCES/multi-monitor-window-list.patch +++ /dev/null @@ -1,1197 +0,0 @@ -From a69a71fafe3222026c938a0832f350654fe05c5c Mon Sep 17 00:00:00 2001 -From: Sylvain Pasche -Date: Mon, 10 Nov 2014 21:35:27 +0100 -Subject: [PATCH 1/5] window-list: Refactoring to use an Extension object - -Move the global state into a new Extension object. This is in -preparation for adding more logic to the Extension object. - -https://bugzilla.gnome.org/show_bug.cgi?id=737486 ---- - extensions/window-list/extension.js | 81 +++++++++++++++++++++---------------- - 1 file changed, 46 insertions(+), 35 deletions(-) - -diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js -index f37c850..8589c43 100644 ---- a/extensions/window-list/extension.js -+++ b/extensions/window-list/extension.js -@@ -53,7 +53,7 @@ function _onMenuStateChanged(menu, isOpen) { - - let [x, y,] = global.get_pointer(); - let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y); -- if (windowList.actor.contains(actor)) -+ if (Me.stateObj.windowListContains(actor)) - actor.sync_hover(); - } - -@@ -1196,49 +1196,60 @@ const WindowList = new Lang.Class({ - } - }); - --let windowList; --let injections = {}; --let notificationParent; -+const Extension = new Lang.Class({ -+ Name: 'Extension', - --function init() { --} -+ _init: function() { -+ this._windowList = null; -+ this._injections = {}; -+ this._notificationParent = null; -+ }, - --function enable() { -- windowList = new WindowList(); -+ enable: function() { -+ this._windowList = new WindowList(); -+ let windowListActor = this._windowList.actor; - -- windowList.actor.connect('notify::hover', Lang.bind(Main.messageTray, -- function() { -- this._pointerInNotification = windowList.actor.hover; -- this._updateState(); -- })); -+ windowListActor.connect('notify::hover', Lang.bind(Main.messageTray, -+ function() { -+ this._pointerInNotification = windowListActor.hover; -+ this._updateState(); -+ })); - -- injections['_trayDwellTimeout'] = MessageTray.MessageTray.prototype._trayDwellTimeout; -- MessageTray.MessageTray.prototype._trayDwellTimeout = function() { -- return false; -- }; -+ this._injections['_trayDwellTimeout'] = -+ MessageTray.MessageTray.prototype._trayDwellTimeout; -+ MessageTray.MessageTray.prototype._trayDwellTimeout = function() { -+ return false; -+ }; - -- notificationParent = Main.messageTray._notificationWidget.get_parent(); -- Main.messageTray._notificationWidget.hide(); -- Main.messageTray._notificationWidget.reparent(windowList.actor); -- Main.messageTray._notificationWidget.show(); --} -+ this._notificationParent = Main.messageTray._notificationWidget.get_parent(); -+ Main.messageTray._notificationWidget.hide(); -+ Main.messageTray._notificationWidget.reparent(windowListActor); -+ Main.messageTray._notificationWidget.show(); -+ }, - --function disable() { -- var prop; -+ disable: function() { -+ if (!this._windowList) -+ return; - -- if (!windowList) -- return; -+ this._windowList.actor.hide(); - -- windowList.actor.hide(); -+ if (this._notificationParent) { -+ Main.messageTray._notificationWidget.reparent(this._notificationParent); -+ this._notificationParent = null; -+ } - -- if (notificationParent) { -- Main.messageTray._notificationWidget.reparent(notificationParent); -- notificationParent = null; -- } -+ this._windowList.actor.destroy(); -+ this._windowList = null; - -- windowList.actor.destroy(); -- windowList = null; -+ for (let prop in this._injections) -+ MessageTray.MessageTray.prototype[prop] = this._injections[prop]; -+ }, - -- for (prop in injections) -- MessageTray.MessageTray.prototype[prop] = injections[prop]; -+ windowListContains: function(actor) { -+ return this._windowList.actor.contains(actor); -+ } -+}); -+ -+function init() { -+ return new Extension(); - } --- -2.5.0 - - -From ca4b95594d10e80d4b3a430d4a2b3fb79472970d Mon Sep 17 00:00:00 2001 -From: Sylvain Pasche -Date: Mon, 10 Nov 2014 21:37:22 +0100 -Subject: [PATCH 2/5] window-list: Move messageTray patching to the WindowList - class - -Move messageTray patching form the Extension object to the WindowList -class. Moreover, only do the patching if the window list is on the bottom -monitor. This refactoring will make it easier to have several instances -of WindowList (one on each monitor). - -https://bugzilla.gnome.org/show_bug.cgi?id=737486 ---- - extensions/window-list/extension.js | 58 ++++++++++++++++++++++--------------- - 1 file changed, 35 insertions(+), 23 deletions(-) - -diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js -index 8589c43..d537f7c 100644 ---- a/extensions/window-list/extension.js -+++ b/extensions/window-list/extension.js -@@ -858,6 +858,24 @@ const WindowList = new Lang.Class({ - this._updateKeyboardAnchor(); - this._updateMessageTrayAnchor(); - })); -+ -+ this._isOnBottomMonitor = Main.layoutManager.primaryIndex == Main.layoutManager.bottomIndex; -+ -+ if (this._isOnBottomMonitor) { -+ let actor = this.actor; -+ this._bottomHoverChangedId = -+ actor.connect('notify::hover', Lang.bind(Main.messageTray, -+ function() { -+ this._pointerInNotification = actor.hover; -+ this._updateState(); -+ })); -+ -+ this._notificationParent = Main.messageTray._notificationWidget.get_parent(); -+ Main.messageTray._notificationWidget.hide(); -+ Main.messageTray._notificationWidget.reparent(this.actor); -+ Main.messageTray._notificationWidget.show(); -+ } -+ - this._updateMessageTrayAnchor(); - - this._fullscreenChangedId = -@@ -995,8 +1013,10 @@ const WindowList = new Lang.Class({ - }, - - _updateMessageTrayAnchor: function() { -- let sameMonitor = Main.layoutManager.primaryIndex == Main.layoutManager.bottomIndex; -- let anchorY = this.actor.visible && sameMonitor ? this.actor.height : 0; -+ if (!this._isOnBottomMonitor) -+ return; -+ -+ let anchorY = this.actor.visible ? this.actor.height : 0; - - Main.messageTray.actor.anchor_y = anchorY; - Main.messageTray._notificationWidget.anchor_y = -anchorY; -@@ -1177,8 +1197,19 @@ const WindowList = new Lang.Class({ - global.window_manager.disconnect(this._switchWorkspaceId); - this._switchWorkspaceId = 0; - -- Main.messageTray.actor.anchor_y = 0; -- Main.messageTray._notificationWidget.anchor_y = 0; -+ if (this._bottomHoverChangedId) -+ this.actor.disconnect(this._bottomHoverChangedId); -+ this._bottomHoverChangedId = 0; -+ -+ if (this._notificationParent) { -+ Main.messageTray._notificationWidget.reparent(this._notificationParent); -+ this._notificationParent = null; -+ } -+ -+ if (this._isOnBottomMonitor) { -+ Main.messageTray.actor.anchor_y = 0; -+ Main.messageTray._notificationWidget.anchor_y = 0; -+ } - - Main.overview.disconnect(this._overviewShowingId); - Main.overview.disconnect(this._overviewHidingId); -@@ -1202,29 +1233,16 @@ const Extension = new Lang.Class({ - _init: function() { - this._windowList = null; - this._injections = {}; -- this._notificationParent = null; - }, - - enable: function() { - this._windowList = new WindowList(); -- let windowListActor = this._windowList.actor; -- -- windowListActor.connect('notify::hover', Lang.bind(Main.messageTray, -- function() { -- this._pointerInNotification = windowListActor.hover; -- this._updateState(); -- })); - - this._injections['_trayDwellTimeout'] = - MessageTray.MessageTray.prototype._trayDwellTimeout; - MessageTray.MessageTray.prototype._trayDwellTimeout = function() { - return false; - }; -- -- this._notificationParent = Main.messageTray._notificationWidget.get_parent(); -- Main.messageTray._notificationWidget.hide(); -- Main.messageTray._notificationWidget.reparent(windowListActor); -- Main.messageTray._notificationWidget.show(); - }, - - disable: function() { -@@ -1232,12 +1250,6 @@ const Extension = new Lang.Class({ - return; - - this._windowList.actor.hide(); -- -- if (this._notificationParent) { -- Main.messageTray._notificationWidget.reparent(this._notificationParent); -- this._notificationParent = null; -- } -- - this._windowList.actor.destroy(); - this._windowList = null; - --- -2.5.0 - - -From 352352112596ea4b6d21120b2c6d49dcabf0d0c2 Mon Sep 17 00:00:00 2001 -From: Sylvain Pasche -Date: Mon, 10 Nov 2014 21:37:29 +0100 -Subject: [PATCH 3/5] window-list: Refactor {Window,App}Button shared code into - BaseButton - -BaseButton is a new class that shares the common logic of WindowButton -and AppButton. AppButton is passed to AppContextMenu so that it can reuse -code from the now public getWindowList() method. - -https://bugzilla.gnome.org/show_bug.cgi?id=737486 ---- - extensions/window-list/extension.js | 233 +++++++++++++++++++----------------- - 1 file changed, 123 insertions(+), 110 deletions(-) - -diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js -index d537f7c..897f448 100644 ---- a/extensions/window-list/extension.js -+++ b/extensions/window-list/extension.js -@@ -195,47 +195,30 @@ const WindowTitle = new Lang.Class({ - }); - - --const WindowButton = new Lang.Class({ -- Name: 'WindowButton', -- -- _init: function(metaWindow) { -- this.metaWindow = metaWindow; -+const BaseButton = new Lang.Class({ -+ Name: 'BaseButton', -+ Abstract: true, - -- this._windowTitle = new WindowTitle(this.metaWindow); -+ _init: function() { - this.actor = new St.Button({ style_class: 'window-button', - x_fill: true, - y_fill: true, - can_focus: true, - button_mask: St.ButtonMask.ONE | -- St.ButtonMask.THREE, -- child: this._windowTitle.actor }); -+ St.ButtonMask.THREE }); - this.actor._delegate = this; - -- this._menuManager = new PopupMenu.PopupMenuManager(this); -- this._contextMenu = new WindowContextMenu(this.actor, this.metaWindow); -- this._contextMenu.connect('open-state-changed', _onMenuStateChanged); -- this._contextMenu.actor.hide(); -- this._menuManager.addMenu(this._contextMenu); -- Main.uiGroup.add_actor(this._contextMenu.actor); -- - this.actor.connect('allocation-changed', - Lang.bind(this, this._updateIconGeometry)); - this.actor.connect('clicked', Lang.bind(this, this._onClicked)); - this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); - this.actor.connect('popup-menu', Lang.bind(this, this._onPopupMenu)); - -+ this._contextMenuManager = new PopupMenu.PopupMenuManager(this); -+ - this._switchWorkspaceId = - global.window_manager.connect('switch-workspace', - Lang.bind(this, this._updateVisibility)); -- this._workspaceChangedId = -- this.metaWindow.connect('workspace-changed', -- Lang.bind(this, this._updateVisibility)); -- this._updateVisibility(); -- -- this._notifyFocusId = -- global.display.connect('notify::focus-window', -- Lang.bind(this, this._updateStyle)); -- this._updateStyle(); - }, - - get active() { -@@ -250,6 +233,89 @@ const WindowButton = new Lang.Class({ - }, - - _onClicked: function(actor, button) { -+ throw new Error('Not implemented'); -+ }, -+ -+ _canOpenPopupMenu: function() { -+ return true; -+ }, -+ -+ _onPopupMenu: function(actor) { -+ if (!this._canOpenPopupMenu() || this._contextMenu.isOpen) -+ return; -+ _openMenu(this._contextMenu); -+ }, -+ -+ _isFocused: function() { -+ throw new Error('Not implemented'); -+ }, -+ -+ _updateStyle: function() { -+ if (this._isFocused()) -+ this.actor.add_style_class_name('focused'); -+ else -+ this.actor.remove_style_class_name('focused'); -+ }, -+ -+ _isWindowVisible: function(window) { -+ let workspace = global.screen.get_active_workspace(); -+ -+ return !window.skip_taskbar && window.located_on_workspace(workspace); -+ }, -+ -+ _updateVisibility: function() { -+ throw new Error('Not implemented'); -+ }, -+ -+ _getIconGeometry: function() { -+ let rect = new Meta.Rectangle(); -+ -+ [rect.x, rect.y] = this.actor.get_transformed_position(); -+ [rect.width, rect.height] = this.actor.get_transformed_size(); -+ -+ return rect; -+ }, -+ -+ _updateIconGeometry: function() { -+ throw new Error('Not implemented'); -+ }, -+ -+ _onDestroy: function() { -+ global.window_manager.disconnect(this._switchWorkspaceId); -+ } -+}); -+ -+ -+const WindowButton = new Lang.Class({ -+ Name: 'WindowButton', -+ Extends: BaseButton, -+ -+ _init: function(metaWindow) { -+ this.parent(); -+ -+ this.metaWindow = metaWindow; -+ this._updateVisibility(); -+ -+ this._windowTitle = new WindowTitle(this.metaWindow); -+ this.actor.set_child(this._windowTitle.actor); -+ -+ this._contextMenu = new WindowContextMenu(this.actor, this.metaWindow); -+ this._contextMenu.connect('open-state-changed', _onMenuStateChanged); -+ this._contextMenu.actor.hide(); -+ this._contextMenuManager.addMenu(this._contextMenu); -+ Main.uiGroup.add_actor(this._contextMenu.actor); -+ -+ this._workspaceChangedId = -+ this.metaWindow.connect('workspace-changed', -+ Lang.bind(this, this._updateVisibility)); -+ -+ this._notifyFocusId = -+ global.display.connect('notify::focus-window', -+ Lang.bind(this, this._updateStyle)); -+ this._updateStyle(); -+ }, -+ -+ _onClicked: function(actor, button) { - if (this._contextMenu.isOpen) { - this._contextMenu.close(); - return; -@@ -261,41 +327,30 @@ const WindowButton = new Lang.Class({ - _openMenu(this._contextMenu); - }, - -- _onPopupMenu: function(actor) { -- if (this._contextMenu.isOpen) -- return; -- _openMenu(this._contextMenu); -+ _isFocused: function() { -+ return global.display.focus_window == this.metaWindow; - }, - - _updateStyle: function() { -+ this.parent(); -+ - if (this.metaWindow.minimized) - this.actor.add_style_class_name('minimized'); - else - this.actor.remove_style_class_name('minimized'); -- -- if (global.display.focus_window == this.metaWindow) -- this.actor.add_style_class_name('focused'); -- else -- this.actor.remove_style_class_name('focused'); - }, - - _updateVisibility: function() { -- let workspace = global.screen.get_active_workspace(); -- this.actor.visible = this.metaWindow.located_on_workspace(workspace); -+ this.actor.visible = this._isWindowVisible(this.metaWindow); - }, - - _updateIconGeometry: function() { -- let rect = new Meta.Rectangle(); -- -- [rect.x, rect.y] = this.actor.get_transformed_position(); -- [rect.width, rect.height] = this.actor.get_transformed_size(); -- -- this.metaWindow.set_icon_geometry(rect); -+ this.metaWindow.set_icon_geometry(this._getIconGeometry()); - }, - - _onDestroy: function() { -+ this.parent(); - this.metaWindow.disconnect(this._workspaceChangedId); -- global.window_manager.disconnect(this._switchWorkspaceId); - global.display.disconnect(this._notifyFocusId); - this._contextMenu.destroy(); - } -@@ -306,14 +361,14 @@ const AppContextMenu = new Lang.Class({ - Name: 'AppContextMenu', - Extends: PopupMenu.PopupMenu, - -- _init: function(source, app) { -+ _init: function(source, appButton) { - this.parent(source, 0.5, St.Side.BOTTOM); - -- this._app = app; -+ this._appButton = appButton; - - this._minimizeItem = new PopupMenu.PopupMenuItem(_("Minimize all")); - this._minimizeItem.connect('activate', Lang.bind(this, function() { -- this._getWindowList().forEach(function(w) { -+ this._appButton.getWindowList().forEach(function(w) { - w.minimize(); - }); - })); -@@ -321,7 +376,7 @@ const AppContextMenu = new Lang.Class({ - - this._unminimizeItem = new PopupMenu.PopupMenuItem(_("Unminimize all")); - this._unminimizeItem.connect('activate', Lang.bind(this, function() { -- this._getWindowList().forEach(function(w) { -+ this._appButton.getWindowList().forEach(function(w) { - w.unminimize(); - }); - })); -@@ -329,7 +384,7 @@ const AppContextMenu = new Lang.Class({ - - this._maximizeItem = new PopupMenu.PopupMenuItem(_("Maximize all")); - this._maximizeItem.connect('activate', Lang.bind(this, function() { -- this._getWindowList().forEach(function(w) { -+ this._appButton.getWindowList().forEach(function(w) { - w.maximize(Meta.MaximizeFlags.HORIZONTAL | - Meta.MaximizeFlags.VERTICAL); - }); -@@ -338,7 +393,7 @@ const AppContextMenu = new Lang.Class({ - - this._unmaximizeItem = new PopupMenu.PopupMenuItem(_("Unmaximize all")); - this._unmaximizeItem.connect('activate', Lang.bind(this, function() { -- this._getWindowList().forEach(function(w) { -+ this._appButton.getWindowList().forEach(function(w) { - w.unmaximize(Meta.MaximizeFlags.HORIZONTAL | - Meta.MaximizeFlags.VERTICAL); - }); -@@ -347,22 +402,15 @@ const AppContextMenu = new Lang.Class({ - - let item = new PopupMenu.PopupMenuItem(_("Close all")); - item.connect('activate', Lang.bind(this, function() { -- this._getWindowList().forEach(function(w) { -+ this._appButton.getWindowList().forEach(function(w) { - w.delete(global.get_current_time()); - }); - })); - this.addMenuItem(item); - }, - -- _getWindowList: function() { -- let workspace = global.screen.get_active_workspace(); -- return this._app.get_windows().filter(function(win) { -- return !win.skip_taskbar && win.located_on_workspace(workspace); -- }); -- }, -- - open: function(animate) { -- let windows = this._getWindowList(); -+ let windows = this._appButton.getWindowList(); - this._minimizeItem.actor.visible = windows.some(function(w) { - return !w.minimized; - }); -@@ -382,21 +430,16 @@ const AppContextMenu = new Lang.Class({ - - const AppButton = new Lang.Class({ - Name: 'AppButton', -+ Extends: BaseButton, - - _init: function(app) { -+ this.parent(); -+ - this.app = app; -+ this._updateVisibility(); - - let stack = new St.Widget({ layout_manager: new Clutter.BinLayout() }); -- this.actor = new St.Button({ style_class: 'window-button', -- x_fill: true, -- can_focus: true, -- button_mask: St.ButtonMask.ONE | -- St.ButtonMask.THREE, -- child: stack }); -- this.actor._delegate = this; -- -- this.actor.connect('allocation-changed', -- Lang.bind(this, this._updateIconGeometry)); -+ this.actor.set_child(stack); - - this._singleWindowTitle = new St.Bin({ x_expand: true, - y_fill: true, -@@ -421,8 +464,7 @@ const AppButton = new Lang.Class({ - this._menuManager.addMenu(this._menu); - Main.uiGroup.add_actor(this._menu.actor); - -- this._contextMenuManager = new PopupMenu.PopupMenuManager(this); -- this._appContextMenu = new AppContextMenu(this.actor, this.app); -+ this._appContextMenu = new AppContextMenu(this.actor, this); - this._appContextMenu.connect('open-state-changed', _onMenuStateChanged); - this._appContextMenu.actor.hide(); - Main.uiGroup.add_actor(this._appContextMenu.actor); -@@ -433,14 +475,6 @@ const AppButton = new Lang.Class({ - function() { - this._icon.child = app.create_icon_texture(ICON_TEXTURE_SIZE); - })); -- this.actor.connect('clicked', Lang.bind(this, this._onClicked)); -- this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); -- this.actor.connect('popup-menu', Lang.bind(this, this._onPopupMenu)); -- -- this._switchWorkspaceId = -- global.window_manager.connect('switch-workspace', -- Lang.bind(this, this._updateVisibility)); -- this._updateVisibility(); - - this._windowsChangedId = - this.app.connect('windows-changed', -@@ -459,18 +493,12 @@ const AppButton = new Lang.Class({ - this.actor.visible = this.app.is_on_workspace(workspace); - }, - -- _updateStyle: function() { -- if (this._windowTracker.focus_app == this.app) -- this.actor.add_style_class_name('focused'); -- else -- this.actor.remove_style_class_name('focused'); -+ _isFocused: function() { -+ return this._windowTracker.focus_app == this.app; - }, - - _updateIconGeometry: function() { -- let rect = new Meta.Rectangle(); -- -- [rect.x, rect.y] = this.actor.get_transformed_position(); -- [rect.width, rect.height] = this.actor.get_transformed_size(); -+ let rect = this._getIconGeometry(); - - let windows = this.app.get_windows(); - windows.forEach(function(w) { -@@ -479,15 +507,14 @@ const AppButton = new Lang.Class({ - }, - - -- _getWindowList: function() { -- let workspace = global.screen.get_active_workspace(); -- return this.app.get_windows().filter(function(win) { -- return !win.skip_taskbar && win.located_on_workspace(workspace); -- }); -+ getWindowList: function() { -+ return this.app.get_windows().filter(Lang.bind(this, function(win) { -+ return this._isWindowVisible(win); -+ })); - }, - - _windowsChanged: function() { -- let windows = this._getWindowList(); -+ let windows = this.getWindowList(); - this._singleWindowTitle.visible = windows.length == 1; - this._multiWindowTitle.visible = !this._singleWindowTitle.visible; - -@@ -519,17 +546,6 @@ const AppButton = new Lang.Class({ - - }, - -- get active() { -- return this.actor.has_style_class_name('focused'); -- }, -- -- activate: function() { -- if (this.active) -- return; -- -- this._onClicked(this.actor, 1); -- }, -- - _onClicked: function(actor, button) { - let menuWasOpen = this._menu.isOpen; - if (menuWasOpen) -@@ -543,7 +559,7 @@ const AppButton = new Lang.Class({ - if (menuWasOpen) - return; - -- let windows = this._getWindowList(); -+ let windows = this.getWindowList(); - if (windows.length == 1) { - if (contextMenuWasOpen) - return; -@@ -567,20 +583,17 @@ const AppButton = new Lang.Class({ - } - }, - -- _onPopupMenu: function(actor) { -- if (this._menu.isOpen || this._contextMenu.isOpen) -- return; -- _openMenu(this._contextMenu); -+ _canOpenPopupMenu: function() { -+ return !this._menu.isOpen; - }, - -- - _onMenuActivate: function(menu, child) { - child._window.activate(global.get_current_time()); - }, - - _onDestroy: function() { -+ this.parent(); - this._textureCache.disconnect(this._iconThemeChangedId); -- global.window_manager.disconnect(this._switchWorkspaceId); - this._windowTracker.disconnect(this._notifyFocusId); - this.app.disconnect(this._windowsChangedId); - this._menu.destroy(); --- -2.5.0 - - -From 5669e5b591dc9b8f11f48d9d70fe87f0c1fcb0ae Mon Sep 17 00:00:00 2001 -From: Sylvain Pasche -Date: Sun, 16 Nov 2014 16:03:39 +0100 -Subject: [PATCH 4/5] window-list: Option to show the window list on all - monitors - -A new setting "show-on-all-monitors" (false by default) is available to -show window lists on all connected monitors. -The Extension object monitors conditions that require the list of -windows to be rebuilt. The WindowList and Button classes have a new -"perMonitor" property that indicates they should handle windows on -their own monitor only. - -https://bugzilla.gnome.org/show_bug.cgi?id=737486 ---- - extensions/window-list/extension.js | 183 ++++++++++++++++----- - ...ome.shell.extensions.window-list.gschema.xml.in | 8 + - extensions/window-list/prefs.js | 20 ++- - 3 files changed, 162 insertions(+), 49 deletions(-) - -diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js -index 897f448..2de46c8 100644 ---- a/extensions/window-list/extension.js -+++ b/extensions/window-list/extension.js -@@ -53,7 +53,7 @@ function _onMenuStateChanged(menu, isOpen) { - - let [x, y,] = global.get_pointer(); - let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y); -- if (Me.stateObj.windowListContains(actor)) -+ if (Me.stateObj.someWindowListContains(actor)) - actor.sync_hover(); - } - -@@ -199,7 +199,10 @@ const BaseButton = new Lang.Class({ - Name: 'BaseButton', - Abstract: true, - -- _init: function() { -+ _init: function(perMonitor, monitorIndex) { -+ this._perMonitor = perMonitor; -+ this._monitorIndex = monitorIndex; -+ - this.actor = new St.Button({ style_class: 'window-button', - x_fill: true, - y_fill: true, -@@ -219,6 +222,15 @@ const BaseButton = new Lang.Class({ - this._switchWorkspaceId = - global.window_manager.connect('switch-workspace', - Lang.bind(this, this._updateVisibility)); -+ -+ if (this._perMonitor) { -+ this._windowEnteredMonitorId = -+ global.screen.connect('window-entered-monitor', -+ Lang.bind(this, this._windowEnteredOrLeftMonitor)); -+ this._windowLeftMonitorId = -+ global.screen.connect('window-left-monitor', -+ Lang.bind(this, this._windowEnteredOrLeftMonitor)); -+ } - }, - - get active() { -@@ -257,10 +269,16 @@ const BaseButton = new Lang.Class({ - this.actor.remove_style_class_name('focused'); - }, - -+ _windowEnteredOrLeftMonitor: function(metaScreen, monitorIndex, metaWindow) { -+ throw new Error('Not implemented'); -+ }, -+ - _isWindowVisible: function(window) { - let workspace = global.screen.get_active_workspace(); - -- return !window.skip_taskbar && window.located_on_workspace(workspace); -+ return !window.skip_taskbar && -+ window.located_on_workspace(workspace) && -+ (!this._perMonitor || window.get_monitor() == this._monitorIndex); - }, - - _updateVisibility: function() { -@@ -282,6 +300,14 @@ const BaseButton = new Lang.Class({ - - _onDestroy: function() { - global.window_manager.disconnect(this._switchWorkspaceId); -+ -+ if (this._windowEnteredMonitorId) -+ global.screen.disconnect(this._windowEnteredMonitorId); -+ this._windowEnteredMonitorId = 0; -+ -+ if (this._windowLeftMonitorId) -+ global.screen.disconnect(this._windowLeftMonitorId); -+ this._windowLeftMonitorId = 0; - } - }); - -@@ -290,8 +316,8 @@ const WindowButton = new Lang.Class({ - Name: 'WindowButton', - Extends: BaseButton, - -- _init: function(metaWindow) { -- this.parent(); -+ _init: function(metaWindow, perMonitor, monitorIndex) { -+ this.parent(perMonitor, monitorIndex); - - this.metaWindow = metaWindow; - this._updateVisibility(); -@@ -340,6 +366,11 @@ const WindowButton = new Lang.Class({ - this.actor.remove_style_class_name('minimized'); - }, - -+ _windowEnteredOrLeftMonitor: function(metaScreen, monitorIndex, metaWindow) { -+ if (monitorIndex == this._monitorIndex && metaWindow == this.metaWindow) -+ this._updateVisibility(); -+ }, -+ - _updateVisibility: function() { - this.actor.visible = this._isWindowVisible(this.metaWindow); - }, -@@ -432,8 +463,8 @@ const AppButton = new Lang.Class({ - Name: 'AppButton', - Extends: BaseButton, - -- _init: function(app) { -- this.parent(); -+ _init: function(app, perMonitor, monitorIndex) { -+ this.parent(perMonitor, monitorIndex); - - this.app = app; - this._updateVisibility(); -@@ -488,9 +519,22 @@ const AppButton = new Lang.Class({ - this._updateStyle(); - }, - -+ _windowEnteredOrLeftMonitor: function(metaScreen, monitorIndex, metaWindow) { -+ if (this._windowTracker.get_window_app(metaWindow) == this.app && -+ monitorIndex == this._monitorIndex) { -+ this._updateVisibility(); -+ this._windowsChanged(); -+ } -+ }, -+ - _updateVisibility: function() { -- let workspace = global.screen.get_active_workspace(); -- this.actor.visible = this.app.is_on_workspace(workspace); -+ if (!this._perMonitor) { -+ // fast path: use ShellApp API to avoid iterating over all windows. -+ let workspace = global.screen.get_active_workspace(); -+ this.actor.visible = this.app.is_on_workspace(workspace); -+ } else { -+ this.actor.visible = this.getWindowList().length >= 1; -+ } - }, - - _isFocused: function() { -@@ -779,7 +823,10 @@ const WorkspaceIndicator = new Lang.Class({ - const WindowList = new Lang.Class({ - Name: 'WindowList', - -- _init: function() { -+ _init: function(perMonitor, monitor) { -+ this._perMonitor = perMonitor; -+ this._monitor = monitor; -+ - this.actor = new St.Widget({ name: 'panel', - style_class: 'bottom-panel', - reactive: true, -@@ -813,27 +860,36 @@ const WindowList = new Lang.Class({ - this._workspaceIndicator = new WorkspaceIndicator(); - indicatorsBox.add(this._workspaceIndicator.container, { expand: false, y_fill: true }); - -+ this._workspaceSettings = new Gio.Settings({ schema_id: 'org.gnome.shell.overrides' }); -+ this._workspacesOnlyOnPrimaryChangedId = -+ this._workspaceSettings.connect('changed::workspaces-only-on-primary', -+ Lang.bind(this, this._updateWorkspaceIndicatorVisibility)); -+ this._updateWorkspaceIndicatorVisibility(); -+ - this._menuManager = new PopupMenu.PopupMenuManager(this); - this._menuManager.addMenu(this._workspaceIndicator.menu); - -- this._trayButton = new TrayButton(); -- indicatorsBox.add(this._trayButton.actor, { expand: false }); -+ this._isOnBottomMonitor = this._monitor == Main.layoutManager.bottomMonitor; -+ -+ if (this._isOnBottomMonitor) { -+ this._trayButton = new TrayButton(); -+ indicatorsBox.add(this._trayButton.actor, { expand: false }); -+ } - - Main.layoutManager.addChrome(this.actor, { affectsStruts: true, - trackFullscreen: true }); - Main.uiGroup.set_child_above_sibling(this.actor, Main.layoutManager.trayBox); - Main.ctrlAltTabManager.addGroup(this.actor, _("Window List"), 'start-here-symbolic'); - -+ this.actor.width = this._monitor.width; -+ this.actor.connect('notify::height', Lang.bind(this, this._updatePosition)); -+ this._updatePosition(); -+ - this._appSystem = Shell.AppSystem.get_default(); - this._appStateChangedId = - this._appSystem.connect('app-state-changed', - Lang.bind(this, this._onAppStateChanged)); - -- this._monitorsChangedId = -- Main.layoutManager.connect('monitors-changed', -- Lang.bind(this, this._updatePosition)); -- this.actor.connect('notify::height', Lang.bind(this, this._updatePosition)); -- this._updatePosition(); - - this._keyboardVisiblechangedId = - Main.layoutManager.connect('keyboard-visible-changed', -@@ -872,8 +928,6 @@ const WindowList = new Lang.Class({ - this._updateMessageTrayAnchor(); - })); - -- this._isOnBottomMonitor = Main.layoutManager.primaryIndex == Main.layoutManager.bottomIndex; -- - if (this._isOnBottomMonitor) { - let actor = this.actor; - this._bottomHoverChangedId = -@@ -943,6 +997,17 @@ const WindowList = new Lang.Class({ - children[active].activate(); - }, - -+ _updatePosition: function() { -+ this.actor.set_position(this._monitor.x, -+ this._monitor.y + this._monitor.height - this.actor.height); -+ }, -+ -+ _updateWorkspaceIndicatorVisibility: function() { -+ this._workspaceIndicator.actor.visible = -+ this._monitor == Main.layoutManager.primaryMonitor || -+ !this._workspaceSettings.get_boolean('workspaces-only-on-primary'); -+ }, -+ - _getPreferredUngroupedWindowListWidth: function() { - if (this._windowList.get_n_children() == 0) - return this._windowList.get_preferred_width(-1)[1]; -@@ -952,13 +1017,21 @@ const WindowList = new Lang.Class({ - let spacing = this._windowList.layout_manager.spacing; - - let workspace = global.screen.get_active_workspace(); -- let nWindows = global.display.get_tab_list(Meta.TabList.NORMAL, workspace).length; -+ let windows = global.display.get_tab_list(Meta.TabList.NORMAL, workspace); -+ if (this._perMonitor) { -+ windows = windows.filter(Lang.bind(this, function(window) { -+ return window.get_monitor() == this._monitor.index; -+ })); -+ } -+ let nWindows = windows.length; -+ if (nWindows == 0) -+ return this._windowList.get_preferred_width(-1)[1]; - - return nWindows * childWidth + (nWindows - 1) * spacing; - }, - - _getMaxWindowListWidth: function() { -- let indicatorsBox = this._trayButton.actor.get_parent(); -+ let indicatorsBox = this._workspaceIndicator.actor.get_parent(); - return this.actor.width - indicatorsBox.get_preferred_width(-1)[1]; - }, - -@@ -1009,14 +1082,6 @@ const WindowList = new Lang.Class({ - } - }, - -- _updatePosition: function() { -- let monitor = Main.layoutManager.primaryMonitor; -- if (!monitor) -- return; -- this.actor.width = monitor.width; -- this.actor.set_position(monitor.x, monitor.y + monitor.height - this.actor.height); -- }, -- - _updateKeyboardAnchor: function() { - if (!Main.keyboard.actor) - return; -@@ -1046,7 +1111,7 @@ const WindowList = new Lang.Class({ - }, - - _addApp: function(app) { -- let button = new AppButton(app); -+ let button = new AppButton(app, this._perMonitor, this._monitor.index); - this._windowList.layout_manager.pack(button.actor, - true, true, true, - Clutter.BoxAlignment.START, -@@ -1079,7 +1144,7 @@ const WindowList = new Lang.Class({ - return; - } - -- let button = new WindowButton(win); -+ let button = new WindowButton(win, this._perMonitor, this._monitor.index); - this._windowList.layout_manager.pack(button.actor, - true, true, true, - Clutter.BoxAlignment.START, -@@ -1188,6 +1253,8 @@ const WindowList = new Lang.Class({ - }, - - _onDestroy: function() { -+ this._workspaceSettings.disconnect(this._workspacesOnlyOnPrimaryChangedId); -+ - this._workspaceIndicator.destroy(); - - Main.ctrlAltTabManager.removeGroup(this.actor); -@@ -1195,9 +1262,6 @@ const WindowList = new Lang.Class({ - this._appSystem.disconnect(this._appStateChangedId); - this._appStateChangedId = 0; - -- Main.layoutManager.disconnect(this._monitorsChangedId); -- this._monitorsChangedId = 0; -- - Main.layoutManager.disconnect(this._keyboardVisiblechangedId); - this._keyboardVisiblechangedId = 0; - -@@ -1244,34 +1308,69 @@ const Extension = new Lang.Class({ - Name: 'Extension', - - _init: function() { -- this._windowList = null; -+ this._windowLists = null; - this._injections = {}; - }, - - enable: function() { -- this._windowList = new WindowList(); -+ this._windowLists = []; - - this._injections['_trayDwellTimeout'] = - MessageTray.MessageTray.prototype._trayDwellTimeout; - MessageTray.MessageTray.prototype._trayDwellTimeout = function() { - return false; - }; -+ -+ this._settings = Convenience.getSettings(); -+ this._showOnAllMonitorsChangedId = -+ this._settings.connect('changed::show-on-all-monitors', -+ Lang.bind(this, this._buildWindowLists)); -+ -+ this._monitorsChangedId = -+ Main.layoutManager.connect('monitors-changed', -+ Lang.bind(this, this._buildWindowLists)); -+ -+ this._buildWindowLists(); -+ }, -+ -+ _buildWindowLists: function() { -+ this._windowLists.forEach(function(windowList) { -+ windowList.actor.destroy(); -+ }); -+ this._windowLists = []; -+ -+ let showOnAllMonitors = this._settings.get_boolean('show-on-all-monitors'); -+ -+ Main.layoutManager.monitors.forEach(Lang.bind(this, function(monitor) { -+ if (showOnAllMonitors || monitor == Main.layoutManager.primaryMonitor) -+ this._windowLists.push(new WindowList(showOnAllMonitors, monitor)); -+ })); - }, - - disable: function() { -- if (!this._windowList) -+ if (!this._windowLists) - return; - -- this._windowList.actor.hide(); -- this._windowList.actor.destroy(); -- this._windowList = null; -+ this._settings.disconnect(this._showOnAllMonitorsChangedId); -+ this._showOnAllMonitorsChangedId = 0; -+ -+ Main.layoutManager.disconnect(this._monitorsChangedId); -+ this._monitorsChangedId = 0; -+ -+ this._windowLists.forEach(function(windowList) { -+ windowList.actor.hide(); -+ windowList.actor.destroy(); -+ }); -+ this._windowLists = null; - - for (let prop in this._injections) - MessageTray.MessageTray.prototype[prop] = this._injections[prop]; - }, - -- windowListContains: function(actor) { -- return this._windowList.actor.contains(actor); -+ someWindowListContains: function(actor) { -+ return this._windowLists.some(function(windowList) { -+ return windowList.actor.contains(actor); -+ }); - } - }); - -diff --git a/extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in b/extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in -index d5bbdf4..f736e36 100644 ---- a/extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in -+++ b/extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml.in -@@ -15,5 +15,13 @@ - window list. Possible values are "never", "auto" and "always". - - -+ -+ false -+ <_summary>Show the window list on all monitors -+ <_description> -+ Whether to show the window list on all connected monitors or -+ only on the primary one. -+ -+ - - -diff --git a/extensions/window-list/prefs.js b/extensions/window-list/prefs.js -index 3db09c3..dc20065 100644 ---- a/extensions/window-list/prefs.js -+++ b/extensions/window-list/prefs.js -@@ -1,5 +1,6 @@ - // -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- - -+const Gio = imports.gi.Gio; - const GObject = imports.gi.GObject; - const Gtk = imports.gi.Gtk; - const Lang = imports.lang; -@@ -19,25 +20,25 @@ function init() { - const WindowListPrefsWidget = new GObject.Class({ - Name: 'WindowList.Prefs.Widget', - GTypeName: 'WindowListPrefsWidget', -- Extends: Gtk.Frame, -+ Extends: Gtk.Grid, - - _init: function(params) { - this.parent(params); - -- this.shadow_type = Gtk.ShadowType.NONE; - this.margin = 24; -+ this.row_spacing = 6; -+ this.orientation = Gtk.Orientation.VERTICAL; - -- let title = '' + _("Window Grouping") + ''; -- let titleLabel = new Gtk.Label({ use_markup: true, label: title }); -- this.set_label_widget(titleLabel); -+ let groupingLabel = '' + _("Window Grouping") + ''; -+ this.add(new Gtk.Label({ label: groupingLabel, use_markup: true, -+ halign: Gtk.Align.START })); - - let align = new Gtk.Alignment({ left_padding: 12 }); - this.add(align); - - let grid = new Gtk.Grid({ orientation: Gtk.Orientation.VERTICAL, - row_spacing: 6, -- column_spacing: 6, -- margin_top: 6 }); -+ column_spacing: 6 }); - align.add(grid); - - this._settings = Convenience.getSettings(); -@@ -70,6 +71,11 @@ const WindowListPrefsWidget = new GObject.Class({ - this._settings.set_string('grouping-mode', mode); - })); - } -+ -+ let check = new Gtk.CheckButton({ label: _("Show on all monitors"), -+ margin_top: 6 }); -+ this._settings.bind('show-on-all-monitors', check, 'active', Gio.SettingsBindFlags.DEFAULT); -+ this.add(check); - } - }); - --- -2.5.0 - - -From 2761b1344c771673634e929a866cacdb01ab4caa Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Florian=20M=C3=BCllner?= -Date: Fri, 19 Dec 2014 14:58:52 +0100 -Subject: [PATCH 5/5] window-list: Do not hardcode overrides schema - -Classic mode uses a different overrides schema, so make sure we use the -correct setting instead of hardcoding the usual org.gnome.shell.overrides -schema. - -https://bugzilla.gnome.org/show_bug.cgi?id=737486 ---- - extensions/window-list/extension.js | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - -diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js -index 2de46c8..128af39 100644 ---- a/extensions/window-list/extension.js -+++ b/extensions/window-list/extension.js -@@ -860,7 +860,7 @@ const WindowList = new Lang.Class({ - this._workspaceIndicator = new WorkspaceIndicator(); - indicatorsBox.add(this._workspaceIndicator.container, { expand: false, y_fill: true }); - -- this._workspaceSettings = new Gio.Settings({ schema_id: 'org.gnome.shell.overrides' }); -+ this._workspaceSettings = this._getWorkspaceSettings(); - this._workspacesOnlyOnPrimaryChangedId = - this._workspaceSettings.connect('changed::workspaces-only-on-primary', - Lang.bind(this, this._updateWorkspaceIndicatorVisibility)); -@@ -972,6 +972,13 @@ const WindowList = new Lang.Class({ - this._groupingModeChanged(); - }, - -+ _getWorkspaceSettings: function() { -+ let settings = global.get_overrides_settings(); -+ if (settings.list_keys().indexOf('workspaces-only-on-primary') > -1) -+ return settings; -+ return new Gio.Settings({ schema_id: 'org.gnome.mutter' }); -+ }, -+ - _onScrollEvent: function(actor, event) { - let direction = event.get_scroll_direction(); - let diff = 0; --- -2.5.0 - diff --git a/SOURCES/resurrect-system-monitor.patch b/SOURCES/resurrect-system-monitor.patch new file mode 100644 index 0000000..3080359 --- /dev/null +++ b/SOURCES/resurrect-system-monitor.patch @@ -0,0 +1,804 @@ +From 9493ca34aa624d13818cd917fb190ef543cc845d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 17 May 2017 19:13:50 +0200 +Subject: [PATCH 1/4] extensions: Resurrect systemMonitor extension + +The extension was removed upstream because: + - it hooks into the message tray that was removed + - it was known to have performance issues + - there are plenty of alternatives + +Those aren't good enough reasons for dropping it downstream +as well though, so we need to bring it back ... + +This reverts commit c9a6421f362cd156cf731289eadc11f44f6970ac. +--- + README | 4 + + configure.ac | 8 +- + extensions/systemMonitor/Makefile.am | 3 + + extensions/systemMonitor/extension.js | 376 ++++++++++++++++++++++++++++++ + extensions/systemMonitor/metadata.json.in | 11 + + extensions/systemMonitor/stylesheet.css | 35 +++ + 6 files changed, 436 insertions(+), 1 deletion(-) + create mode 100644 extensions/systemMonitor/Makefile.am + create mode 100644 extensions/systemMonitor/extension.js + create mode 100644 extensions/systemMonitor/metadata.json.in + create mode 100644 extensions/systemMonitor/stylesheet.css + +diff --git a/README b/README +index cc53a8d..e2bea7a 100644 +--- a/README ++++ b/README +@@ -57,6 +57,10 @@ places-menu + + Shows a status Indicator for navigating to Places. + ++systemMonitor ++ ++ An message tray indicator showing CPU and memory loads. ++ + user-theme + + Loads a shell theme from ~/.themes//gnome-shell. +diff --git a/configure.ac b/configure.ac +index 7944b52..ecb2fd6 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -31,7 +31,7 @@ AC_SUBST([SHELL_VERSION]) + dnl keep this in alphabetic order + CLASSIC_EXTENSIONS="apps-menu places-menu alternate-tab launch-new-instance window-list" + DEFAULT_EXTENSIONS="$CLASSIC_EXTENSIONS drive-menu screenshot-window-sizer windowsNavigator workspace-indicator" +-ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows dash-to-dock example native-window-placement panel-favorites top-icons updates-dialog user-theme" ++ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows dash-to-dock example native-window-placement panel-favorites systemMonitor top-icons updates-dialog user-theme" + AC_SUBST(CLASSIC_EXTENSIONS, [$CLASSIC_EXTENSIONS]) + AC_SUBST(ALL_EXTENSIONS, [$ALL_EXTENSIONS]) + AC_ARG_ENABLE([extensions], +@@ -62,6 +62,11 @@ AM_CONDITIONAL([CLASSIC_MODE], [test x"$enable_classic_mode" != xno]) + ENABLED_EXTENSIONS= + for e in $enable_extensions; do + case $e in ++ systemMonitor) ++ PKG_CHECK_MODULES(GTOP, libgtop-2.0 >= 2.28.3, ++ [ENABLED_EXTENSIONS="$ENABLED_EXTENSIONS $e"], ++ [AC_MSG_WARN([libgtop-2.0 not found, disabling systemMonitor])]) ++ ;; + dnl keep this in alphabetic order + alternate-tab|apps-menu|auto-move-windows|dash-to-dock|drive-menu|example|launch-new-instance|native-window-placement|panel-favorites|places-menu|screenshot-window-sizer|top-icons|updates-dialog|user-theme|window-list|windowsNavigator|workspace-indicator) + ENABLED_EXTENSIONS="$ENABLED_EXTENSIONS $e" +@@ -89,6 +94,7 @@ AC_CONFIG_FILES([ + extensions/panel-favorites/Makefile + extensions/places-menu/Makefile + extensions/screenshot-window-sizer/Makefile ++ extensions/systemMonitor/Makefile + extensions/top-icons/Makefile + extensions/updates-dialog/Makefile + extensions/user-theme/Makefile +diff --git a/extensions/systemMonitor/Makefile.am b/extensions/systemMonitor/Makefile.am +new file mode 100644 +index 0000000..50ce6d2 +--- /dev/null ++++ b/extensions/systemMonitor/Makefile.am +@@ -0,0 +1,3 @@ ++EXTENSION_ID = systemMonitor ++ ++include ../../extension.mk +diff --git a/extensions/systemMonitor/extension.js b/extensions/systemMonitor/extension.js +new file mode 100644 +index 0000000..58a2f57 +--- /dev/null ++++ b/extensions/systemMonitor/extension.js +@@ -0,0 +1,376 @@ ++/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ ++ ++const Clutter = imports.gi.Clutter; ++const GTop = imports.gi.GTop; ++const Lang = imports.lang; ++const Mainloop = imports.mainloop; ++const St = imports.gi.St; ++const Shell = imports.gi.Shell; ++ ++const Main = imports.ui.main; ++const Tweener = imports.ui.tweener; ++ ++const Gettext = imports.gettext.domain('gnome-shell-extensions'); ++const _ = Gettext.gettext; ++ ++const ExtensionUtils = imports.misc.extensionUtils; ++const Me = ExtensionUtils.getCurrentExtension(); ++const Convenience = Me.imports.convenience; ++ ++const INDICATOR_UPDATE_INTERVAL = 500; ++const INDICATOR_NUM_GRID_LINES = 3; ++ ++const ITEM_LABEL_SHOW_TIME = 0.15; ++const ITEM_LABEL_HIDE_TIME = 0.1; ++const ITEM_HOVER_TIMEOUT = 300; ++ ++const Indicator = new Lang.Class({ ++ Name: 'SystemMonitor.Indicator', ++ ++ _init: function() { ++ this._initValues(); ++ this.drawing_area = new St.DrawingArea({ reactive: true }); ++ this.drawing_area.connect('repaint', Lang.bind(this, this._draw)); ++ this.drawing_area.connect('button-press-event', function() { ++ let app = Shell.AppSystem.get_default().lookup_app('gnome-system-monitor.desktop'); ++ app.open_new_window(-1); ++ return true; ++ }); ++ ++ this.actor = new St.Bin({ style_class: "extension-systemMonitor-indicator-area", ++ reactive: true, track_hover: true, ++ x_fill: true, y_fill: true }); ++ this.actor.add_actor(this.drawing_area); ++ ++ this._timeout = Mainloop.timeout_add(INDICATOR_UPDATE_INTERVAL, Lang.bind(this, function () { ++ this._updateValues(); ++ this.drawing_area.queue_repaint(); ++ return true; ++ })); ++ }, ++ ++ showLabel: function() { ++ if (this.label == null) ++ return; ++ ++ this.label.opacity = 0; ++ this.label.show(); ++ ++ let [stageX, stageY] = this.actor.get_transformed_position(); ++ ++ let itemWidth = this.actor.allocation.x2 - this.actor.allocation.x1; ++ let itemHeight = this.actor.allocation.y2 - this.actor.allocation.y1; ++ ++ let labelWidth = this.label.width; ++ let labelHeight = this.label.height; ++ let xOffset = Math.floor((itemWidth - labelWidth) / 2) ++ ++ let x = stageX + xOffset; ++ ++ let node = this.label.get_theme_node(); ++ let yOffset = node.get_length('-y-offset'); ++ ++ let y = stageY - this.label.get_height() - yOffset; ++ ++ this.label.set_position(x, y); ++ Tweener.addTween(this.label, ++ { opacity: 255, ++ time: ITEM_LABEL_SHOW_TIME, ++ transition: 'easeOutQuad', ++ }); ++ }, ++ ++ setLabelText: function(text) { ++ if (this.label == null) ++ this.label = new St.Label({ style_class: 'extension-systemMonitor-indicator-label'}); ++ ++ this.label.set_text(text); ++ Main.layoutManager.addChrome(this.label); ++ this.label.hide(); ++ }, ++ ++ hideLabel: function () { ++ Tweener.addTween(this.label, ++ { opacity: 0, ++ time: ITEM_LABEL_HIDE_TIME, ++ transition: 'easeOutQuad', ++ onComplete: Lang.bind(this, function() { ++ this.label.hide(); ++ }) ++ }); ++ }, ++ ++ destroy: function() { ++ Mainloop.source_remove(this._timeout); ++ ++ this.actor.destroy(); ++ if (this.label) ++ this.label.destroy(); ++ }, ++ ++ _initValues: function() { ++ }, ++ ++ _updateValues: function() { ++ }, ++ ++ _draw: function(area) { ++ let [width, height] = area.get_surface_size(); ++ let themeNode = this.actor.get_theme_node(); ++ let cr = area.get_context(); ++ ++ //draw the background grid ++ let color = themeNode.get_color(this.gridColor); ++ let gridOffset = Math.floor(height / (INDICATOR_NUM_GRID_LINES + 1)); ++ for (let i = 1; i <= INDICATOR_NUM_GRID_LINES; ++i) { ++ cr.moveTo(0, i * gridOffset + .5); ++ cr.lineTo(width, i * gridOffset + .5); ++ } ++ Clutter.cairo_set_source_color(cr, color); ++ cr.setLineWidth(1); ++ cr.setDash([4,1], 0); ++ cr.stroke(); ++ ++ //draw the foreground ++ ++ function makePath(values, reverse, nudge) { ++ if (nudge == null) { ++ nudge = 0; ++ } ++ //if we are going in reverse, we are completing the bottom of a chart, so use lineTo ++ if (reverse) { ++ cr.lineTo(values.length - 1, (1 - values[values.length - 1]) * height + nudge); ++ for (let k = values.length - 2; k >= 0; --k) { ++ cr.lineTo(k, (1 - values[k]) * height + nudge); ++ } ++ } else { ++ cr.moveTo(0, (1 - values[0]) * height + nudge); ++ for (let k = 1; k < values.length; ++k) { ++ cr.lineTo(k, (1 - values[k]) * height + nudge); ++ } ++ ++ } ++ } ++ ++ let renderStats = this.renderStats; ++ ++ // Make sure we don't have more sample points than pixels ++ renderStats.map(Lang.bind(this, function(k){ ++ let stat = this.stats[k]; ++ if (stat.values.length > width) { ++ stat.values = stat.values.slice(stat.values.length - width, stat.values.length); ++ } ++ })); ++ ++ for (let i = 0; i < renderStats.length; ++i) { ++ let stat = this.stats[renderStats[i]]; ++ // We outline at full opacity and fill with 40% opacity ++ let outlineColor = themeNode.get_color(stat.color); ++ let color = new Clutter.Color(outlineColor); ++ color.alpha = color.alpha * .4; ++ ++ // Render the background between us and the next level ++ makePath(stat.values, false); ++ // If there is a process below us, render the cpu between us and it, otherwise, ++ // render to the bottom of the chart ++ if (i == renderStats.length - 1) { ++ cr.lineTo(stat.values.length - 1, height); ++ cr.lineTo(0, height); ++ cr.closePath(); ++ } else { ++ let nextStat = this.stats[renderStats[i+1]]; ++ makePath(nextStat.values, true); ++ } ++ cr.closePath() ++ Clutter.cairo_set_source_color(cr, color); ++ cr.fill(); ++ ++ // Render the outline of this level ++ makePath(stat.values, false, .5); ++ Clutter.cairo_set_source_color(cr, outlineColor); ++ cr.setLineWidth(1.0); ++ cr.setDash([], 0); ++ cr.stroke(); ++ } ++ } ++}); ++ ++const CpuIndicator = new Lang.Class({ ++ Name: 'SystemMonitor.CpuIndicator', ++ Extends: Indicator, ++ ++ _init: function() { ++ this.parent(); ++ ++ this.gridColor = '-grid-color'; ++ this.renderStats = [ 'cpu-user', 'cpu-sys', 'cpu-iowait' ]; ++ ++ // Make sure renderStats is sorted as necessary for rendering ++ let renderStatOrder = {'cpu-total': 0, 'cpu-user': 1, 'cpu-sys': 2, 'cpu-iowait': 3}; ++ this.renderStats = this.renderStats.sort(function(a,b) { ++ return renderStatOrder[a] - renderStatOrder[b]; ++ }); ++ ++ this.setLabelText(_("CPU")); ++ }, ++ ++ _initValues: function() { ++ this._prev = new GTop.glibtop_cpu; ++ GTop.glibtop_get_cpu(this._prev); ++ ++ this.stats = { ++ 'cpu-user': {color: '-cpu-user-color', values: []}, ++ 'cpu-sys': {color: '-cpu-sys-color', values: []}, ++ 'cpu-iowait': {color: '-cpu-iowait-color', values: []}, ++ 'cpu-total': {color: '-cpu-total-color', values: []} ++ }; ++ }, ++ ++ _updateValues: function() { ++ let cpu = new GTop.glibtop_cpu; ++ let t = 0.0; ++ GTop.glibtop_get_cpu(cpu); ++ let total = cpu.total - this._prev.total; ++ let user = cpu.user - this._prev.user; ++ let sys = cpu.sys - this._prev.sys; ++ let iowait = cpu.iowait - this._prev.iowait; ++ let idle = cpu.idle - this._prev.idle; ++ ++ t += iowait / total; ++ this.stats['cpu-iowait'].values.push(t); ++ t += sys / total; ++ this.stats['cpu-sys'].values.push(t); ++ t += user / total; ++ this.stats['cpu-user'].values.push(t); ++ this.stats['cpu-total'].values.push(1 - idle / total); ++ ++ this._prev = cpu; ++ } ++}); ++ ++const MemoryIndicator = new Lang.Class({ ++ Name: 'SystemMonitor.MemoryIndicator', ++ Extends: Indicator, ++ ++ _init: function() { ++ this.parent(); ++ ++ this.gridColor = '-grid-color'; ++ this.renderStats = [ 'mem-user', 'mem-other', 'mem-cached' ]; ++ ++ // Make sure renderStats is sorted as necessary for rendering ++ let renderStatOrder = { 'mem-cached': 0, 'mem-other': 1, 'mem-user': 2 }; ++ this.renderStats = this.renderStats.sort(function(a,b) { ++ return renderStatOrder[a] - renderStatOrder[b]; ++ }); ++ ++ this.setLabelText(_("Memory")); ++ }, ++ ++ _initValues: function() { ++ this.mem = new GTop.glibtop_mem; ++ this.stats = { ++ 'mem-user': { color: "-mem-user-color", values: [] }, ++ 'mem-other': { color: "-mem-other-color", values: [] }, ++ 'mem-cached': { color: "-mem-cached-color", values: [] } ++ }; ++ }, ++ ++ _updateValues: function() { ++ GTop.glibtop_get_mem(this.mem); ++ ++ let t = this.mem.user / this.mem.total; ++ this.stats['mem-user'].values.push(t); ++ t += (this.mem.used - this.mem.user - this.mem.cached) / this.mem.total; ++ this.stats['mem-other'].values.push(t); ++ t += this.mem.cached / this.mem.total; ++ this.stats['mem-cached'].values.push(t); ++ } ++}); ++ ++const INDICATORS = [CpuIndicator, MemoryIndicator]; ++ ++const Extension = new Lang.Class({ ++ Name: 'SystemMonitor.Extension', ++ ++ _init: function() { ++ Convenience.initTranslations(); ++ ++ this._showLabelTimeoutId = 0; ++ this._resetHoverTimeoutId = 0; ++ this._labelShowing = false; ++ }, ++ ++ enable: function() { ++ this._box = new St.BoxLayout({ style_class: 'extension-systemMonitor-container', ++ x_align: Clutter.ActorAlign.START, ++ x_expand: true }); ++ this._indicators = [ ]; ++ ++ for (let i = 0; i < INDICATORS.length; i++) { ++ let indicator = new (INDICATORS[i])(); ++ ++ indicator.actor.connect('notify::hover', Lang.bind(this, function() { ++ this._onHover(indicator); ++ })); ++ this._box.add_actor(indicator.actor); ++ this._indicators.push(indicator); ++ } ++ ++ this._boxHolder = new St.BoxLayout({ x_expand: true, ++ y_expand: true, ++ x_align: Clutter.ActorAlign.START, ++ }); ++ let menuButton = Main.messageTray._messageTrayMenuButton.actor; ++ Main.messageTray.actor.remove_child(menuButton); ++ Main.messageTray.actor.add_child(this._boxHolder); ++ ++ this._boxHolder.add_child(this._box); ++ this._boxHolder.add_child(menuButton); ++ }, ++ ++ disable: function() { ++ this._indicators.forEach(function(i) { i.destroy(); }); ++ ++ let menuButton = Main.messageTray._messageTrayMenuButton.actor; ++ this._boxHolder.remove_child(menuButton); ++ Main.messageTray.actor.add_child(menuButton); ++ ++ this._box.destroy(); ++ this._boxHolder.destroy(); ++ }, ++ ++ _onHover: function (item) { ++ if (item.actor.get_hover()) { ++ if (this._showLabelTimeoutId == 0) { ++ let timeout = this._labelShowing ? 0 : ITEM_HOVER_TIMEOUT; ++ this._showLabelTimeoutId = Mainloop.timeout_add(timeout, ++ Lang.bind(this, function() { ++ this._labelShowing = true; ++ item.showLabel(); ++ return false; ++ })); ++ if (this._resetHoverTimeoutId > 0) { ++ Mainloop.source_remove(this._resetHoverTimeoutId); ++ this._resetHoverTimeoutId = 0; ++ } ++ } ++ } else { ++ if (this._showLabelTimeoutId > 0) ++ Mainloop.source_remove(this._showLabelTimeoutId); ++ this._showLabelTimeoutId = 0; ++ item.hideLabel(); ++ if (this._labelShowing) { ++ this._resetHoverTimeoutId = Mainloop.timeout_add(ITEM_HOVER_TIMEOUT, ++ Lang.bind(this, function() { ++ this._labelShowing = false; ++ return false; ++ })); ++ } ++ } ++ }, ++}); ++ ++function init() { ++ return new Extension(); ++} +diff --git a/extensions/systemMonitor/metadata.json.in b/extensions/systemMonitor/metadata.json.in +new file mode 100644 +index 0000000..fa75007 +--- /dev/null ++++ b/extensions/systemMonitor/metadata.json.in +@@ -0,0 +1,11 @@ ++{ ++ "shell-version": ["@shell_current@" ], ++ "uuid": "@uuid@", ++ "extension-id": "@extension_id@", ++ "settings-schema": "@gschemaname@", ++ "gettext-domain": "@gettext_domain@", ++ "original-author": "zaspire@rambler.ru", ++ "name": "SystemMonitor", ++ "description": "System monitor showing CPU and memory usage in the message tray.", ++ "url": "@url@" ++} +diff --git a/extensions/systemMonitor/stylesheet.css b/extensions/systemMonitor/stylesheet.css +new file mode 100644 +index 0000000..13f95ec +--- /dev/null ++++ b/extensions/systemMonitor/stylesheet.css +@@ -0,0 +1,35 @@ ++.extension-systemMonitor-container { ++ spacing: 5px; ++ padding-left: 5px; ++ padding-right: 5px; ++ padding-bottom: 10px; ++ padding-top: 10px; ++} ++ ++.extension-systemMonitor-indicator-area { ++ border: 1px solid #8d8d8d; ++ border-radius: 3px; ++ width: 100px; ++ /* message tray is 72px, so 20px padding of the container, ++ 2px of border, makes it 50px */ ++ height: 50px; ++ -grid-color: #575757; ++ -cpu-total-color: rgb(0,154,62); ++ -cpu-user-color: rgb(69,154,0); ++ -cpu-sys-color: rgb(255,253,81); ++ -cpu-iowait-color: rgb(210,148,0); ++ -mem-user-color: rgb(210,148,0); ++ -mem-cached-color: rgb(90,90,90); ++ -mem-other-color: rgb(205,203,41); ++ background-color: #1e1e1e; ++} ++ ++.extension-systemMonitor-indicator-label { ++ border-radius: 7px; ++ padding: 4px 12px; ++ background-color: rgba(0,0,0,0.9); ++ text-align: center; ++ -y-offset: 8px; ++ font-size: 9pt; ++ font-weight: bold; ++} +-- +2.12.2 + + +From 6b31a97ee501b4242d3c763a20de0577bf3f879d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 17 May 2017 19:31:58 +0200 +Subject: [PATCH 2/4] systemMonitor: Move indicators to calendar + +The message tray joined the invisible choir, so we have to find +a new home for the extension UI. The message list in the calendar +drop-down looks like the best option, given that it replaced the +old tray (and also took over the old keyboard shortcut to bring +it up quickly). +--- + extensions/systemMonitor/extension.js | 56 ++++++++++++++++----------------- + extensions/systemMonitor/stylesheet.css | 14 --------- + 2 files changed, 28 insertions(+), 42 deletions(-) + +diff --git a/extensions/systemMonitor/extension.js b/extensions/systemMonitor/extension.js +index 58a2f57..01146fa 100644 +--- a/extensions/systemMonitor/extension.js ++++ b/extensions/systemMonitor/extension.js +@@ -4,10 +4,12 @@ const Clutter = imports.gi.Clutter; + const GTop = imports.gi.GTop; + const Lang = imports.lang; + const Mainloop = imports.mainloop; ++const Signals = imports.signals; + const St = imports.gi.St; + const Shell = imports.gi.Shell; + + const Main = imports.ui.main; ++const MessageList = imports.ui.messageList; + const Tweener = imports.ui.tweener; + + const Gettext = imports.gettext.domain('gnome-shell-extensions'); +@@ -29,18 +31,21 @@ const Indicator = new Lang.Class({ + + _init: function() { + this._initValues(); +- this.drawing_area = new St.DrawingArea({ reactive: true }); ++ this.drawing_area = new St.DrawingArea(); + this.drawing_area.connect('repaint', Lang.bind(this, this._draw)); +- this.drawing_area.connect('button-press-event', function() { ++ ++ this.actor = new St.Button({ style_class: "message message-content extension-systemMonitor-indicator-area", ++ x_expand: true, x_fill: true, ++ y_fill: true, can_focus: true }); ++ this.actor.add_actor(this.drawing_area); ++ ++ this.actor.connect('clicked', function() { + let app = Shell.AppSystem.get_default().lookup_app('gnome-system-monitor.desktop'); + app.open_new_window(-1); +- return true; +- }); + +- this.actor = new St.Bin({ style_class: "extension-systemMonitor-indicator-area", +- reactive: true, track_hover: true, +- x_fill: true, y_fill: true }); +- this.actor.add_actor(this.drawing_area); ++ Main.overview.hide(); ++ Main.panel.closeCalendar(); ++ }); + + this._timeout = Mainloop.timeout_add(INDICATOR_UPDATE_INTERVAL, Lang.bind(this, function () { + this._updateValues(); +@@ -73,6 +78,7 @@ const Indicator = new Lang.Class({ + let y = stageY - this.label.get_height() - yOffset; + + this.label.set_position(x, y); ++ this.label.get_parent().set_child_above_sibling(this.label, null); + Tweener.addTween(this.label, + { opacity: 255, + time: ITEM_LABEL_SHOW_TIME, +@@ -100,6 +106,14 @@ const Indicator = new Lang.Class({ + }); + }, + ++ /* MessageList.Message boilerplate */ ++ canClose: function() { ++ return false; ++ }, ++ ++ clear: function() { ++ }, ++ + destroy: function() { + Mainloop.source_remove(this._timeout); + +@@ -194,6 +208,7 @@ const Indicator = new Lang.Class({ + } + } + }); ++Signals.addSignalMethods(Indicator.prototype); // For MessageList.Message compat + + const CpuIndicator = new Lang.Class({ + Name: 'SystemMonitor.CpuIndicator', +@@ -302,9 +317,7 @@ const Extension = new Lang.Class({ + }, + + enable: function() { +- this._box = new St.BoxLayout({ style_class: 'extension-systemMonitor-container', +- x_align: Clutter.ActorAlign.START, +- x_expand: true }); ++ this._section = new MessageList.MessageListSection(_("System Monitor")); + this._indicators = [ ]; + + for (let i = 0; i < INDICATORS.length; i++) { +@@ -313,31 +326,18 @@ const Extension = new Lang.Class({ + indicator.actor.connect('notify::hover', Lang.bind(this, function() { + this._onHover(indicator); + })); +- this._box.add_actor(indicator.actor); ++ this._section.addMessage(indicator, false); + this._indicators.push(indicator); + } + +- this._boxHolder = new St.BoxLayout({ x_expand: true, +- y_expand: true, +- x_align: Clutter.ActorAlign.START, +- }); +- let menuButton = Main.messageTray._messageTrayMenuButton.actor; +- Main.messageTray.actor.remove_child(menuButton); +- Main.messageTray.actor.add_child(this._boxHolder); +- +- this._boxHolder.add_child(this._box); +- this._boxHolder.add_child(menuButton); ++ Main.panel.statusArea.dateMenu._messageList._addSection(this._section); ++ this._section.actor.get_parent().set_child_at_index(this._section.actor, 0); + }, + + disable: function() { + this._indicators.forEach(function(i) { i.destroy(); }); + +- let menuButton = Main.messageTray._messageTrayMenuButton.actor; +- this._boxHolder.remove_child(menuButton); +- Main.messageTray.actor.add_child(menuButton); +- +- this._box.destroy(); +- this._boxHolder.destroy(); ++ Main.panel.statusArea.dateMenu._messageList._removeSection(this._section); + }, + + _onHover: function (item) { +diff --git a/extensions/systemMonitor/stylesheet.css b/extensions/systemMonitor/stylesheet.css +index 13f95ec..978ac12 100644 +--- a/extensions/systemMonitor/stylesheet.css ++++ b/extensions/systemMonitor/stylesheet.css +@@ -1,17 +1,4 @@ +-.extension-systemMonitor-container { +- spacing: 5px; +- padding-left: 5px; +- padding-right: 5px; +- padding-bottom: 10px; +- padding-top: 10px; +-} +- + .extension-systemMonitor-indicator-area { +- border: 1px solid #8d8d8d; +- border-radius: 3px; +- width: 100px; +- /* message tray is 72px, so 20px padding of the container, +- 2px of border, makes it 50px */ + height: 50px; + -grid-color: #575757; + -cpu-total-color: rgb(0,154,62); +@@ -21,7 +8,6 @@ + -mem-user-color: rgb(210,148,0); + -mem-cached-color: rgb(90,90,90); + -mem-other-color: rgb(205,203,41); +- background-color: #1e1e1e; + } + + .extension-systemMonitor-indicator-label { +-- +2.12.2 + + +From c39dfc4aaef29c41a045552aebbdd88e22627aa2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 18 May 2017 16:20:07 +0200 +Subject: [PATCH 3/4] systemMonitor: Handle clicks on section title + +While on 3.24.x only the event section still has a clickable title, +it's a generic message list feature in previous versions. It's easy +enough to support with a small subclass, so use that instead of +the generic baseclass. + +Fixes: #3 +--- + extensions/systemMonitor/extension.js | 20 +++++++++++++++++++- + 1 file changed, 19 insertions(+), 1 deletion(-) + +diff --git a/extensions/systemMonitor/extension.js b/extensions/systemMonitor/extension.js +index 01146fa..cc49cbb 100644 +--- a/extensions/systemMonitor/extension.js ++++ b/extensions/systemMonitor/extension.js +@@ -303,6 +303,24 @@ const MemoryIndicator = new Lang.Class({ + } + }); + ++const SystemMonitorSection = new Lang.Class({ ++ Name: 'SystemMonitorSection', ++ Extends: MessageList.MessageListSection, ++ ++ _init: function() { ++ this.parent(_("System Monitor")); ++ }, ++ ++ _onTitleClicked: function() { ++ this.parent(); ++ ++ let appSys = Shell.AppSystem.get_default(); ++ let app = appSys.lookup_app('gnome-system-monitor.desktop'); ++ if (app) ++ app.open_new_window(-1); ++ } ++}); ++ + const INDICATORS = [CpuIndicator, MemoryIndicator]; + + const Extension = new Lang.Class({ +@@ -317,7 +335,7 @@ const Extension = new Lang.Class({ + }, + + enable: function() { +- this._section = new MessageList.MessageListSection(_("System Monitor")); ++ this._section = new SystemMonitorSection(); + this._indicators = [ ]; + + for (let i = 0; i < INDICATORS.length; i++) { +-- +2.12.2 + + +From a35f8412cc18493c63e98f6984dc10b9d74b5faf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 18 May 2017 18:00:17 +0200 +Subject: [PATCH 4/4] systemMonitor: Provide classic styling + +The indicator tooltips currently don't work out in classic mode +(dark text on dark background), so provide some mode-specific +style. + +Fixes: #4 +--- + extensions/systemMonitor/Makefile.am | 6 ++++++ + extensions/systemMonitor/classic.css | 6 ++++++ + 2 files changed, 12 insertions(+) + create mode 100644 extensions/systemMonitor/classic.css + +diff --git a/extensions/systemMonitor/Makefile.am b/extensions/systemMonitor/Makefile.am +index 50ce6d2..64a61df 100644 +--- a/extensions/systemMonitor/Makefile.am ++++ b/extensions/systemMonitor/Makefile.am +@@ -1,3 +1,9 @@ + EXTENSION_ID = systemMonitor + ++EXTRA_MODULES = ++ ++if CLASSIC_MODE ++ EXTRA_MODULES += classic.css ++endif ++ + include ../../extension.mk +diff --git a/extensions/systemMonitor/classic.css b/extensions/systemMonitor/classic.css +new file mode 100644 +index 0000000..946863d +--- /dev/null ++++ b/extensions/systemMonitor/classic.css +@@ -0,0 +1,6 @@ ++@import url("stylesheet.css"); ++ ++.extension-systemMonitor-indicator-label { ++ background-color: rgba(237,237,237,0.9); ++ border: 1px solid #a1a1a1; ++} +-- +2.12.2 + diff --git a/SOURCES/scale-window-list-with-text.patch b/SOURCES/scale-window-list-with-text.patch deleted file mode 100644 index d9eb84b..0000000 --- a/SOURCES/scale-window-list-with-text.patch +++ /dev/null @@ -1,165 +0,0 @@ -From 2a30c690fc53987a623141c8ba6b86c82fc1be0c Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Florian=20M=C3=BCllner?= -Date: Fri, 12 Jun 2015 20:23:00 +0200 -Subject: [PATCH 1/4] window-list: Expand window buttons vertically - -The window-list's fixed height currently allows us to get away without -expanding buttons, however this won't be the case anymore once we start -adapting the list with the text scaling. So fix up the code to do what -was always the intention anyway. - -https://bugzilla.gnome.org/show_bug.cgi?id=703585 ---- - extensions/window-list/extension.js | 10 +++++++--- - 1 file changed, 7 insertions(+), 3 deletions(-) - -diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js -index b22f8bf..5ceccc1 100644 ---- a/extensions/window-list/extension.js -+++ b/extensions/window-list/extension.js -@@ -140,11 +140,12 @@ const WindowTitle = new Lang.Class({ - - _init: function(metaWindow) { - this._metaWindow = metaWindow; -- this.actor = new St.BoxLayout({ style_class: 'window-button-box' }); -+ this.actor = new St.BoxLayout({ style_class: 'window-button-box', -+ x_expand: true, y_expand: true }); - - this._icon = new St.Bin({ style_class: 'window-button-icon' }); - this.actor.add(this._icon); -- this._label = new St.Label(); -+ this._label = new St.Label({ y_align: Clutter.ActorAlign.CENTER }); - this.actor.add(this._label); - - this._textureCache = St.TextureCache.get_default(); -@@ -202,6 +203,7 @@ const WindowButton = new Lang.Class({ - this._windowTitle = new WindowTitle(this.metaWindow); - this.actor = new St.Button({ style_class: 'window-button', - x_fill: true, -+ y_fill: true, - can_focus: true, - button_mask: St.ButtonMask.ONE | - St.ButtonMask.THREE, -@@ -396,6 +398,7 @@ const AppButton = new Lang.Class({ - Lang.bind(this, this._updateIconGeometry)); - - this._singleWindowTitle = new St.Bin({ x_expand: true, -+ y_fill: true, - x_align: St.Align.START }); - stack.add_actor(this._singleWindowTitle); - -@@ -406,7 +409,8 @@ const AppButton = new Lang.Class({ - this._icon = new St.Bin({ style_class: 'window-button-icon', - child: app.create_icon_texture(ICON_TEXTURE_SIZE) }); - this._multiWindowTitle.add(this._icon); -- this._multiWindowTitle.add(new St.Label({ text: app.get_name() })); -+ this._multiWindowTitle.add(new St.Label({ text: app.get_name(), -+ y_align: Clutter.ActorAlign.CENTER })); - - this._menuManager = new PopupMenu.PopupMenuManager(this); - this._menu = new PopupMenu.PopupMenu(this.actor, 0.5, St.Side.BOTTOM); --- -2.5.0 - - -From e03552c1a104f0a59e630810b056de4790e0f537 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Florian=20M=C3=BCllner?= -Date: Fri, 12 Jun 2015 18:26:17 +0200 -Subject: [PATCH 2/4] window-list: Reposition on height changes - -The window list position depends on both the monitor geometry and the -list height, however changes to the latter are currently ignored. For -the time being this doesn't matter due to the list's fixed height, but -we are about to scale the list with the text, so reposition the list -on height changes. - -https://bugzilla.gnome.org/show_bug.cgi?id=703585 ---- - extensions/window-list/extension.js | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js -index 5ceccc1..9347813 100644 ---- a/extensions/window-list/extension.js -+++ b/extensions/window-list/extension.js -@@ -818,6 +818,7 @@ const WindowList = new Lang.Class({ - this._monitorsChangedId = - Main.layoutManager.connect('monitors-changed', - Lang.bind(this, this._updatePosition)); -+ this.actor.connect('notify::height', Lang.bind(this, this._updatePosition)); - this._updatePosition(); - - this._keyboardVisiblechangedId = --- -2.5.0 - - -From ec0950adeeb36402ee9f1e7998284cd21100d28a Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Florian=20M=C3=BCllner?= -Date: Fri, 12 Jun 2015 19:18:28 +0200 -Subject: [PATCH 3/4] window-list: Use font-relative sizes for width/height - -Just like the top bar, the window list should scale according to -the font scaling factor, so convert the existing pixel sizes into -font-relative ones. - -https://bugzilla.gnome.org/show_bug.cgi?id=703585 ---- - extensions/window-list/stylesheet.css | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/extensions/window-list/stylesheet.css b/extensions/window-list/stylesheet.css -index 2b6be8d..f5285cb 100644 ---- a/extensions/window-list/stylesheet.css -+++ b/extensions/window-list/stylesheet.css -@@ -2,7 +2,7 @@ - /* .window-button-icon height + - .window-button vertical padding + - .window-button > StWidget vertical padding) */ -- height: 30px; -+ height: 2.25em; - } - - .window-list { -@@ -27,8 +27,8 @@ - } - - .window-button > StWidget { -- -st-natural-width: 250px; -- max-width: 250px; -+ -st-natural-width: 18.75em; -+ max-width: 18.75em; - color: #bbb; - background-color: black; - border-radius: 4px; --- -2.5.0 - - -From d17c72c3b42e72e82303643d4a8ee35054bb6676 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Florian=20M=C3=BCllner?= -Date: Fri, 4 Sep 2015 19:56:23 +0200 -Subject: [PATCH 4/4] window-list: Use font-relative size in classic style - -Commit 85b7049376a3 fixed this for the regular session, but forgot to also -update the classic style. ---- - extensions/window-list/classic.css | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/extensions/window-list/classic.css b/extensions/window-list/classic.css -index fd84371..321c5fb 100644 ---- a/extensions/window-list/classic.css -+++ b/extensions/window-list/classic.css -@@ -3,7 +3,7 @@ - #panel.bottom-panel { - border-top-width: 1px; - border-bottom-width: 0px; -- height: 32px !important; -+ height: 2.25em !important; - } - - .bottom-panel .window-button > StWidget { --- -2.5.0 - diff --git a/SOURCES/support-headless-mode.patch b/SOURCES/support-headless-mode.patch deleted file mode 100644 index 9f7d449..0000000 --- a/SOURCES/support-headless-mode.patch +++ /dev/null @@ -1,25 +0,0 @@ -From acee50af7dd6e8f4bea30314bc9dc5f8d212d2f6 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Florian=20M=C3=BCllner?= -Date: Thu, 23 Jul 2015 16:00:55 +0200 -Subject: [PATCH] window-list: Handle the no-monitor case - ---- - extensions/window-list/extension.js | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js -index 9347813..28feda6 100644 ---- a/extensions/window-list/extension.js -+++ b/extensions/window-list/extension.js -@@ -979,6 +979,8 @@ const WindowList = new Lang.Class({ - - _updatePosition: function() { - let monitor = Main.layoutManager.primaryMonitor; -+ if (!monitor) -+ return; - this.actor.width = monitor.width; - this.actor.set_position(monitor.x, monitor.y + monitor.height - this.actor.height); - }, --- -2.4.3 - diff --git a/SPECS/gnome-shell-extensions.spec b/SPECS/gnome-shell-extensions.spec index 43d382f..23c978a 100644 --- a/SPECS/gnome-shell-extensions.spec +++ b/SPECS/gnome-shell-extensions.spec @@ -5,18 +5,18 @@ %global pkg_prefix gnome-shell-extension Name: gnome-shell-extensions -Version: 3.14.4 -Release: 21%{?dist} +Version: 3.22.2 +Release: 10%{?dist} Summary: Modify and extend GNOME Shell functionality and behavior Group: User Interface/Desktops # The entire source code is GPLv2+ except lib/convenience.js which is BSD License: GPLv2+ and BSD -URL: http://live.gnome.org/GnomeShell/Extensions +URL: http://wiki.gnome.org/Projects/GnomeShell/Extensions Source0: http://ftp.gnome.org/pub/GNOME/sources/%{name}/%{major_version}/%{name}-%{version}.tar.xz -BuildRequires: desktop-file-utils # BuildRequires: gnome-common -BuildRequires: intltool +BuildRequires: autoconf automake +BuildRequires: gettext >= 0.19.6 BuildRequires: pkgconfig(gnome-desktop-3.0) BuildRequires: pkgconfig(libgtop-2.0) Requires: gnome-shell >= %{min_gs_version} @@ -24,19 +24,12 @@ BuildArch: noarch Patch1: 0001-classic-shade-panel-in-overview.patch Patch2: 0001-apps-menu-add-logo-icon-to-Applications-menu.patch -Patch3: 0001-Use-a-proper-arrows-instead-of-UTF8.patch -Patch4: add-extra-extensions.patch -Patch5: 0001-apps-menu-Fix-call-to-open_new_window.patch -Patch6: scale-window-list-with-text.patch -Patch7: support-headless-mode.patch -Patch8: 0001-window-list-Don-t-consider-skip-taskbar-windows-for-.patch -Patch9: 0001-workspace-indicator-Use-consistent-workspace-numberi.patch -Patch10: fix-apps-menu-panel-shortcut.patch -Patch11: multi-monitor-window-list.patch -Patch12: 0001-apps-menu-Explicitly-set-label_actor.patch -Patch13: 0001-alternateTab-Adjust-to-gnome-shell-changes.patch -Patch14: gnome-shell-extension-translations.patch -Patch15: 0001-apps-menu-Handle-.desktop-files-from-non-standard-di.patch +Patch3: add-extra-extensions.patch +Patch4: 0001-apps-menu-Explicitly-set-label_actor.patch +Patch5: 0001-window-list-Hide-workspace-indicator-when-there-s-1-.patch +Patch6: apps-menu-desktop-dnd.patch +Patch7: resurrect-system-monitor.patch +Patch8: 0001-loginDialog-make-info-messages-themed.patch %description GNOME Shell Extensions is a collection of extensions providing additional and @@ -76,8 +69,9 @@ Obsoletes: %{pkg_prefix}-static-workspaces < 3.9.3-1 %description -n %{pkg_prefix}-common GNOME Shell Extensions is a collection of extensions providing additional and -optional functionality to GNOME Shell. Common files and directories needed by -extensions are provided here. +optional functionality to GNOME Shell. + +This package provides common data files shared by various extensions. %package -n gnome-classic-session @@ -224,7 +218,8 @@ Requires: %{pkg_prefix}-common = %{version}-%{release} Requires: libgtop2 %description -n %{pkg_prefix}-systemMonitor -This GNOME Shell extension is a message tray indicator for CPU and memory usage. +This GNOME Shell extension is a message tray indicator for CPU and memory usage + %package -n %{pkg_prefix}-top-icons @@ -293,19 +288,12 @@ workspaces. %patch1 -p1 -b .shade-panel-in-overview %patch2 -p1 -b .brand-applications-menu -%patch3 -p1 -b .fix-menu-arrows -%patch4 -p1 -b .add-extra-extensions -%patch5 -p1 -b .fix-open-new-window -%patch6 -p1 -b .scale-window-list -%patch7 -p1 -b .support-headless-mode -%patch8 -p1 -b .fix-window-list-sorting -%patch9 -p1 -b .use-consistent-workspace-numbering -%patch10 -p1 -b .fix-apps-menu-panel-shortcut -%patch11 -p1 -b .multi-monitor-window-list -%patch12 -p1 -b .fix-menu-category-accessibility -%patch13 -p1 -b .adjust-to-switcher-changes -%patch14 -p1 -b .update-translations -%patch15 -p1 -b .fix-menu-entries-from-non-standard-locations +%patch3 -p1 -b .add-extra-extensions +%patch4 -p1 -b .fix-menu-category-accessibility +%patch5 -p1 -b .hide-indicator-in-single-workspace-case +%patch6 -p1 -b .apps-menu-desktop-dnd +%patch7 -p1 -b .apps-menu-desktop-dnd +%patch8 -p1 -b .fix-pam-info-messages %build @@ -326,20 +314,15 @@ rm $RPM_BUILD_ROOT%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.exampl %find_lang %{name} -%check -desktop-file-validate $RPM_BUILD_ROOT%{_datadir}/applications/gnome-shell-classic.desktop - - %files -n %{pkg_prefix}-common -f %{name}.lang %doc COPYING NEWS README -%dir %{_datadir}/gnome-shell/extensions/ %files -n gnome-classic-session -%{_datadir}/applications/gnome-shell-classic.desktop %{_datadir}/gnome-session/sessions/gnome-classic.session %{_datadir}/gnome-shell/modes/classic.json %{_datadir}/gnome-shell/theme/*.svg +%{_datadir}/gnome-shell/theme/gnome-classic-high-contrast.css %{_datadir}/gnome-shell/theme/gnome-classic.css %{_datadir}/xsessions/gnome-classic.desktop %{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.classic-overrides.gschema.xml @@ -451,6 +434,15 @@ fi /usr/bin/glib-compile-schemas %{_datadir}/glib-2.0/schemas/ &>/dev/null || : +%postun -n %{pkg_prefix}-screenshot-window-sizer +if [ $1 -eq 0 ]; then + /usr/bin/glib-compile-schemas %{_datadir}/glib-2.0/schemas/ &>/dev/null || : +fi + +%posttrans -n %{pkg_prefix}-screenshot-window-sizer +/usr/bin/glib-compile-schemas %{_datadir}/glib-2.0/schemas/ &>/dev/null || : + + %postun -n %{pkg_prefix}-updates-dialog if [ $1 -eq 0 ]; then /usr/bin/glib-compile-schemas %{_datadir}/glib-2.0/schemas/ &>/dev/null || : @@ -477,7 +469,48 @@ fi %posttrans -n %{pkg_prefix}-window-list /usr/bin/glib-compile-schemas %{_datadir}/glib-2.0/schemas/ &>/dev/null || : + %changelog +* Mon Jun 26 2017 Ray Strode - 3.22.2-10 +- Fix pam info messages at the unlock screen + Related: #1449359 + +* Sat Jun 24 2017 Florian Müllner - 3.22.3-9 +- Update dash-to-dock to latest upstream +- Resolves: #1464614 + +* Wed May 17 2017 Florian Müllner - 3.22.3-8 +- Resurrect system monitor extension +- Resolves: #1452319 + +* Fri Apr 28 2017 Florian Müllner - 3.22.3-7 +- Include DND improvements from upstream +- Resolves: #1236601 + +* Wed Apr 05 2017 Florian Müllner - 3.22.2-6 +- Update last patch to not rely on newer JS API +- Resolves: #1236601 + +* Tue Mar 21 2017 Florian Müllner - 3.22.2-5 +- Allow creating desktop launchers via DND from apps menu +- Resolves: #1236601 + +* Tue Mar 14 2017 Florian Müllner - 3.22.2-4 +- Fix downstream branding +- Resolves: #1386960 + +* Tue Mar 14 2017 Florian Müllner - 3.22.2-3 +- Hide workspace indicator in window list if there's just 1 workspace +- Resolves: #1414817 + +* Tue Mar 14 2017 Florian Müllner - 3.22.2-2 +- Re-add downstream patches +- Resolves: #1386960 + +* Wed Nov 16 2016 Kalev Lember - 3.22.2-1 +- Update to 3.22.2 +- Resolves: #1386960 + * Wed Sep 07 2016 Florian Müllner - 3.14.4-21 - Improve menu category accessibility further Related: rhbz#1263128