From d97e40a53d1844c84c895ede9a102e6d9d73ec1d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 20 May 2015 17:44:50 +0200
Subject: [PATCH 1/8] Add top-icons extension
---
extensions/top-icons/extension.js | 96 +++++++++++++++++++++++++++
extensions/top-icons/meson.build | 5 ++
extensions/top-icons/metadata.json.in | 10 +++
extensions/top-icons/stylesheet.css | 1 +
meson.build | 1 +
5 files changed, 113 insertions(+)
create mode 100644 extensions/top-icons/extension.js
create mode 100644 extensions/top-icons/meson.build
create mode 100644 extensions/top-icons/metadata.json.in
create mode 100644 extensions/top-icons/stylesheet.css
diff --git a/extensions/top-icons/extension.js b/extensions/top-icons/extension.js
new file mode 100644
index 0000000..a8eec13
--- /dev/null
+++ b/extensions/top-icons/extension.js
@@ -0,0 +1,96 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported init */
+
+const { Clutter, Shell, St } = imports.gi;
+const Main = imports.ui.main;
+const PanelMenu = imports.ui.panelMenu;
+const System = imports.system;
+
+const PANEL_ICON_SIZE = 16;
+
+const STANDARD_TRAY_ICON_IMPLEMENTATIONS = [
+ 'bluetooth-applet',
+ 'gnome-sound-applet',
+ 'nm-applet',
+ 'gnome-power-manager',
+ 'keyboard',
+ 'a11y-keyboard',
+ 'kbd-scrolllock',
+ 'kbd-numlock',
+ 'kbd-capslock',
+ 'ibus-ui-gtk'
+];
+
+class SysTray {
+ constructor() {
+ this._icons = [];
+ this._tray = null;
+ }
+
+ _onTrayIconAdded(o, icon) {
+ let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : '';
+ if (STANDARD_TRAY_ICON_IMPLEMENTATIONS.includes(wmClass))
+ return;
+
+ let button = new PanelMenu.Button(0.5, null, true);
+
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ let iconSize = PANEL_ICON_SIZE * scaleFactor;
+
+ icon.set({
+ width: iconSize,
+ height: iconSize,
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER
+ });
+
+ let iconBin = new St.Widget({
+ layout_manager: new Clutter.BinLayout()
+ });
+ iconBin.add_actor(icon);
+ button.add_actor(iconBin);
+
+ this._icons.push(icon);
+
+ button.connect('button-release-event', (actor, event) => {
+ icon.click(event);
+ });
+ button.connect('key-press-event', (actor, event) => {
+ icon.click(event);
+ });
+
+ icon.connect('destroy', () => {
+ button.destroy();
+ });
+
+ let role = wmClass || `${icon}`;
+ Main.panel.addToStatusArea(role, button);
+ }
+
+ _onTrayIconRemoved(o, icon) {
+ let parent = icon.get_parent();
+ parent.destroy();
+ this._icons.splice(this._icons.indexOf(icon), 1);
+ }
+
+ enable() {
+ this._tray = new Shell.TrayManager();
+ this._tray.connect('tray-icon-added',
+ this._onTrayIconAdded.bind(this));
+ this._tray.connect('tray-icon-removed',
+ this._onTrayIconRemoved.bind(this));
+ this._tray.manage_screen(Main.panel);
+ }
+
+ disable() {
+ this._icons.forEach(icon => icon.get_parent().destroy());
+ this._icons = [];
+
+ this._tray = null;
+ System.gc(); // force finalizing tray to unmanage screen
+ }
+}
+
+function init() {
+ return new SysTray();
+}
diff --git a/extensions/top-icons/meson.build b/extensions/top-icons/meson.build
new file mode 100644
index 0000000..48504f6
--- /dev/null
+++ b/extensions/top-icons/meson.build
@@ -0,0 +1,5 @@
+extension_data += configure_file(
+ input: metadata_name + '.in',
+ output: metadata_name,
+ configuration: metadata_conf
+)
diff --git a/extensions/top-icons/metadata.json.in b/extensions/top-icons/metadata.json.in
new file mode 100644
index 0000000..f1e2436
--- /dev/null
+++ b/extensions/top-icons/metadata.json.in
@@ -0,0 +1,10 @@
+{
+"extension-id": "@extension_id@",
+"uuid": "@uuid@",
+"settings-schema": "@gschemaname@",
+"gettext-domain": "@gettext_domain@",
+"name": "Top Icons",
+"description": "Shows legacy tray icons on top",
+"shell-version": [ "@shell_current@" ],
+"url": "http://94.247.144.115/repo/topicons/"
+}
diff --git a/extensions/top-icons/stylesheet.css b/extensions/top-icons/stylesheet.css
new file mode 100644
index 0000000..25134b6
--- /dev/null
+++ b/extensions/top-icons/stylesheet.css
@@ -0,0 +1 @@
+/* This extensions requires no special styling */
diff --git a/meson.build b/meson.build
index b987f2d..6050c32 100644
--- a/meson.build
+++ b/meson.build
@@ -50,6 +50,7 @@ all_extensions = default_extensions
all_extensions += [
'auto-move-windows',
'native-window-placement',
+ 'top-icons',
'user-theme'
]
--
2.21.0
From 3035a9e961fdae1bd242664130fbf55ce67131ce Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 20 May 2015 18:05:41 +0200
Subject: [PATCH 2/8] Add dash-to-dock extension
---
extensions/dash-to-dock/Settings.ui | 3335 +++++++++++++++++
extensions/dash-to-dock/appIconIndicators.js | 1102 ++++++
extensions/dash-to-dock/appIcons.js | 1172 ++++++
extensions/dash-to-dock/dash.js | 1171 ++++++
extensions/dash-to-dock/docking.js | 1853 +++++++++
extensions/dash-to-dock/extension.js | 23 +
extensions/dash-to-dock/intellihide.js | 321 ++
extensions/dash-to-dock/launcherAPI.js | 239 ++
extensions/dash-to-dock/media/glossy.svg | 139 +
.../media/highlight_stacked_bg.svg | 82 +
.../media/highlight_stacked_bg_h.svg | 82 +
extensions/dash-to-dock/media/logo.svg | 528 +++
extensions/dash-to-dock/meson.build | 23 +
extensions/dash-to-dock/metadata.json.in | 12 +
....shell.extensions.dash-to-dock.gschema.xml | 540 +++
extensions/dash-to-dock/prefs.js | 861 +++++
extensions/dash-to-dock/stylesheet.css | 175 +
extensions/dash-to-dock/theming.js | 569 +++
extensions/dash-to-dock/utils.js | 258 ++
extensions/dash-to-dock/windowPreview.js | 578 +++
meson.build | 1 +
21 files changed, 13064 insertions(+)
create mode 100644 extensions/dash-to-dock/Settings.ui
create mode 100644 extensions/dash-to-dock/appIconIndicators.js
create mode 100644 extensions/dash-to-dock/appIcons.js
create mode 100644 extensions/dash-to-dock/dash.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/launcherAPI.js
create mode 100644 extensions/dash-to-dock/media/glossy.svg
create mode 100644 extensions/dash-to-dock/media/highlight_stacked_bg.svg
create mode 100644 extensions/dash-to-dock/media/highlight_stacked_bg_h.svg
create mode 100644 extensions/dash-to-dock/media/logo.svg
create mode 100644 extensions/dash-to-dock/meson.build
create mode 100644 extensions/dash-to-dock/metadata.json.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/extensions/dash-to-dock/Settings.ui b/extensions/dash-to-dock/Settings.ui
new file mode 100644
index 0000000..c141eff
--- /dev/null
+++ b/extensions/dash-to-dock/Settings.ui
@@ -0,0 +1,3335 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface>
+ <requires lib="gtk+" version="3.18"/>
+ <object class="GtkAdjustment" id="animation_time_adjustment">
+ <property name="upper">1</property>
+ <property name="step_increment">0.050000000000000003</property>
+ <property name="page_increment">0.25</property>
+ </object>
+ <object class="GtkBox" id="box_middle_click_options">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame" id="frame_middle_click_options">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="listbox_middle_click_options">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow11">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="buitin_theme7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_description4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">When set to minimize, double clicking minimizes all the windows of the application.</property>
+ <property name="wrap">True</property>
+ <property name="max_width_chars">40</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_label4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Shift+Click action</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="shift_click_action_combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <items>
+ <item translatable="yes">Raise window</item>
+ <item translatable="yes">Minimize window</item>
+ <item translatable="yes">Launch new instance</item>
+ <item translatable="yes">Cycle through windows</item>
+ <item translatable="yes">Minimize or overview</item>
+ <item translatable="yes">Show window previews</item>
+ <item translatable="yes">Minimize or show previews</item>
+ <item translatable="yes">Focus or show previews</item>
+ <item translatable="yes">Quit</item>
+ </items>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_middle_click">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="grid_middle_click">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="description_middle_click">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Behavior for Middle-Click.</property>
+ <property name="wrap">True</property>
+ <property name="max_width_chars">40</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_middle_click">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Middle-Click action</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="middle_click_action_combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <items>
+ <item translatable="yes">Raise window</item>
+ <item translatable="yes">Minimize window</item>
+ <item translatable="yes">Launch new instance</item>
+ <item translatable="yes">Cycle through windows</item>
+ <item translatable="yes">Minimize or overview</item>
+ <item translatable="yes">Show window previews</item>
+ <item translatable="yes">Minimize or show previews</item>
+ <item translatable="yes">Focus or show previews</item>
+ <item translatable="yes">Quit</item>
+ </items>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_shift_middle_click">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="grid_shift_middle_click">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="description_shift_middle_click">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Behavior for Shift+Middle-Click.</property>
+ <property name="wrap">True</property>
+ <property name="max_width_chars">40</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_shift_middle_click">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Shift+Middle-Click action</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="shift_middle_click_action_combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <items>
+ <item translatable="yes">Raise window</item>
+ <item translatable="yes">Minimize window</item>
+ <item translatable="yes">Launch new instance</item>
+ <item translatable="yes">Cycle through windows</item>
+ <item translatable="yes">Minimize or overview</item>
+ <item translatable="yes">Show window previews</item>
+ <item translatable="yes">Minimize or show previews</item>
+ <item translatable="yes">Focus or show previews</item>
+ <item translatable="yes">Quit</item>
+ </items>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="custom_opacity_adjustement">
+ <property name="upper">1</property>
+ <property name="step_increment">0.01</property>
+ <property name="page_increment">0.10000000000000001</property>
+ </object>
+ <object class="GtkAdjustment" id="dock_size_adjustment">
+ <property name="lower">0.33000000000000002</property>
+ <property name="upper">1</property>
+ <property name="step_increment">0.01</property>
+ <property name="page_increment">0.10000000000000001</property>
+ </object>
+ <object class="GtkAdjustment" id="dot_border_width_adjustment">
+ <property name="upper">10</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">5</property>
+ </object>
+ <object class="GtkBox" id="running_dots_advance_settings_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="listbox7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow10">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkBox" id="dot_style_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="unity_backlit_items_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="label" translatable="yes">Enable Unity7 like glossy backlit items</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="unity_backlit_items_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="valign">center</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="dominant_color_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Use dominant color</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="dominant_color_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="dot_style_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Customize indicator style</property>
+ <property name="justify">fill</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="dot_style_settings_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_bottom">1</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkBox" id="dot_color_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="dot_color_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Color</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkColorButton" id="dot_color_colorbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="border_color_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="dot_border_color_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Border color</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkColorButton" id="dot_border_color_colorbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_boder_width_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="dot_border_width_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Border width</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="dot_border_width_spin_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">dot_border_width_adjustment</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="hide_timeout_adjustment">
+ <property name="upper">1</property>
+ <property name="step_increment">0.050000000000000003</property>
+ <property name="page_increment">0.25</property>
+ </object>
+ <object class="GtkAdjustment" id="icon_size_adjustment">
+ <property name="lower">16</property>
+ <property name="upper">128</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkNotebook" id="settings_notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="margin_left">6</property>
+ <property name="margin_right">6</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkBox" id="position_and_size">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">24</property>
+ <property name="margin_right">24</property>
+ <property name="margin_top">24</property>
+ <property name="margin_bottom">24</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="GtkFrame" id="dock_display">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="dock_display_listbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="dock_monitor_listboxrow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="dock_monitor_grid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="dock_monitor_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Show the dock on</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="dock_monitor_combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <signal name="changed" handler="dock_display_combo_changed_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="multi_monitor_button">
+ <property name="label" translatable="yes">Show on all monitors.</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="margin_top">12</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="dock_position_listboxrow">
+ <property name="width_request">100</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkBox" id="dock_position_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="dock_position_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Position on screen</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="dock_position_butttons_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkRadioButton" id="position_left_button">
+ <property name="label" translatable="yes">Left</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="position_left_button_toggled_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="position_bottom_button">
+ <property name="label" translatable="yes">Bottom</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">center</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">position_left_button</property>
+ <signal name="toggled" handler="position_bottom_button_toggled_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="position_top_button">
+ <property name="label" translatable="yes">Top</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">center</property>
+ <property name="xalign">0</property>
+ <property name="image_position">bottom</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">position_left_button</property>
+ <signal name="toggled" handler="position_top_button_toggled_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="position_right_button">
+ <property name="label" translatable="yes">Right</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="valign">center</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">position_left_button</property>
+ <signal name="toggled" handler="position_right_button_toggled_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="intelligent_autohide_frame">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="listbox3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow14">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="intelligent_autohide_grid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_description7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Hide the dock when it obstructs a window of the current application. More refined settings are available.</property>
+ <property name="wrap">True</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_label8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Intelligent autohide</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="intelligent_autohide_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="xalign">0.46000000834465027</property>
+ <child>
+ <object class="GtkImage" id="image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="intelligent_autohide_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="size_frame">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="size_listbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="dock_size_listboxrow">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="dock_size_grid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="dock_size_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Dock size limit</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="dock_size_scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="valign">baseline</property>
+ <property name="hexpand">True</property>
+ <property name="adjustment">dock_size_adjustment</property>
+ <property name="round_digits">0</property>
+ <property name="digits">2</property>
+ <property name="value_pos">right</property>
+ <signal name="format-value" handler="dock_size_scale_format_value_cb" swapped="no"/>
+ <signal name="value-changed" handler="dock_size_scale_value_changed_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="dock_size_extend_checkbutton">
+ <property name="label" translatable="yes">Panel mode: extend to the screen edge</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="margin_top">12</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="icon_size_listboxrow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="icon_size_grid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_label6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Icon size limit</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="icon_size_scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="valign">baseline</property>
+ <property name="hexpand">True</property>
+ <property name="adjustment">icon_size_adjustment</property>
+ <property name="round_digits">1</property>
+ <property name="digits">0</property>
+ <property name="value_pos">right</property>
+ <signal name="format-value" handler="icon_size_scale_format_value_cb" swapped="no"/>
+ <signal name="value-changed" handler="icon_size_scale_value_changed_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="icon_size_fixed_checkbutton">
+ <property name="label" translatable="yes">Fixed icon size: scroll to reveal other icons</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="margin_top">12</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="general_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Position and size</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="apps">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">24</property>
+ <property name="margin_right">24</property>
+ <property name="margin_top">24</property>
+ <property name="margin_bottom">24</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="GtkFrame" id="customize_theme1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="listbox9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow6">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="shrink_dash1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="show_favorite_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Show favorite applications</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow16">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="shrink_dash2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="show_running_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Show running applications</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="application_button_isolation_button">
+ <property name="label" translatable="yes">Isolate workspaces.</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="margin_top">12</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="application_button_monitor_isolation_button">
+ <property name="label" translatable="yes">Isolate monitors.</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="margin_top">12</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="windows_preview_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="margin_top">3</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ <child>
+ <object class="GtkLabel" id="windows_previews_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Show open windows previews.</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow17">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="shrink_dash3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="show_applications_button_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_description1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">If disabled, these settings are accessible from gnome-tweak-tool or the extension website.</property>
+ <property name="wrap">True</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_label3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Show <i>Applications</i> icon</property>
+ <property name="use_markup">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="application_button_first_button">
+ <property name="label" translatable="yes">Move the applications button at the beginning of the dock.</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="margin_top">12</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="application_button_animation_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="margin_top">3</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.43000000715255737</property>
+ <property name="draw_indicator">True</property>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Animate <i>Show Applications</i>.</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="launchers_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Launchers</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="behaviour">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">24</property>
+ <property name="margin_right">24</property>
+ <property name="margin_top">24</property>
+ <property name="margin_bottom">24</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="GtkFrame" id="hot_keys_frame">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="hot_keys_listbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="hot_keys_listboxrow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="hot_keys_grid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="hot_keys_description">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Enable Super+(0-9) as shortcuts to activate apps. It can also be used together with Shift and Ctrl.</property>
+ <property name="use_markup">True</property>
+ <property name="wrap">True</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="hot_keys_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Use keyboard shortcuts to activate apps</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="overlay_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="overlay_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="xalign">0.46000000834465027</property>
+ <child>
+ <object class="GtkImage" id="image_overlay">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="hot_keys_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="built_in_theme_frame3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="listbox6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow9">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="buitin_theme5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_description5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Behaviour when clicking on the icon of a running application.</property>
+ <property name="wrap">True</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_label5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Click action</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="click_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="middle_click_options_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkImage" id="middle_click_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="click_action_combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <items>
+ <item translatable="yes">Raise window</item>
+ <item translatable="yes">Minimize</item>
+ <item translatable="yes">Launch new instance</item>
+ <item translatable="yes">Cycle through windows</item>
+ <item translatable="yes">Minimize or overview</item>
+ <item translatable="yes">Show window previews</item>
+ <item translatable="yes">Minimize or show previews</item>
+ <item translatable="yes">Focus or show previews</item>
+ </items>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="built_in_theme_frame_scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="listbox5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="buitin_theme_scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_description_scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Behaviour when scrolling on the icon of an application.</property>
+ <property name="wrap">True</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_label_scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Scroll action</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="click_box_scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkComboBoxText" id="scroll_action_combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <items>
+ <item translatable="yes">Do nothing</item>
+ <item translatable="yes">Cycle through windows</item>
+ <item translatable="yes">Switch workspace</item>
+ </items>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="behaviour_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Behavior</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="appearance">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">24</property>
+ <property name="margin_right">24</property>
+ <property name="margin_top">24</property>
+ <property name="margin_bottom">24</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="GtkFrame" id="built_in_theme_frame">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="listbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="buitin_theme">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_description">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Few customizations meant to integrate the dock with the default GNOME theme. Alternatively, specific options can be enabled below.</property>
+ <property name="wrap">True</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Use built-in theme</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="builtin_theme_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="customize_theme">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="listbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="shrink_dash">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="shrink_dash_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_description">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Save space reducing padding and border radius.</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Shrink the dash</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="running_dots">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="running_dots_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Customize windows counter indicators</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="running_indicators_advance_settings_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="xalign">0.46000000834465027</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="running_indicators_combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <items>
+ <item translatable="yes">Default</item>
+ <item translatable="yes">Dots</item>
+ <item translatable="yes">Squares</item>
+ <item translatable="yes">Dashes</item>
+ <item translatable="yes">Segmented</item>
+ <item translatable="yes">Solid</item>
+ <item translatable="yes">Ciliora</item>
+ <item translatable="yes">Metro</item>
+ </items>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="custom_background_color_listboxrow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="custom_background_color_grid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="custom_background_color_description">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Set the background color for the dash.</property>
+ <property name="wrap">True</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="custom_background_color_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Customize the dash color</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="custom_background_color_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkColorButton" id="custom_background_color">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="xalign">0.46000000834465027</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="custom_background_color_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow4">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkGrid" id="customize_opacity">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="customize_opacity_description">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Tune the dash background opacity.</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="customize_opacity_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Customize opacity</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="customize_opacity_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="dynamic_opacity_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkImage" id="dynamic_opacity_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="customize_opacity_combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <items>
+ <item translatable="yes">Default</item>
+ <item translatable="yes">Fixed</item>
+ <item translatable="yes">Dynamic</item>
+ </items>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="custom_opacity">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="custom_opacity_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Opacity</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="custom_opacity_scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">custom_opacity_adjustement</property>
+ <property name="lower_stepper_sensitivity">on</property>
+ <property name="restrict_to_fill_level">False</property>
+ <property name="fill_level">0</property>
+ <property name="round_digits">0</property>
+ <property name="digits">2</property>
+ <property name="value_pos">right</property>
+ <signal name="format-value" handler="custom_opacity_scale_format_value_cb" swapped="no"/>
+ <signal name="value-changed" handler="custom_opacity_scale_value_changed_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_rnd_border">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="force_straight_corner_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="xpad">12</property>
+ <property name="label" translatable="yes">Force straight corner
+</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="force_straight_corner_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="valign">center</property>
+ <property name="margin_left">3</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">12</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="appearance_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Appearance</property>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="about">
+ <property name="can_focus">False</property>
+ <property name="margin_top">24</property>
+ <property name="margin_bottom">24</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">5</property>
+ <child>
+ <object class="GtkImage" id="logo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="pixbuf">./media/logo.svg</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">10</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="extension_name">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label"><b>Dash to Dock</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">center</property>
+ <child>
+ <object class="GtkLabel" id="extension_version_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes">version: </property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="extension_version">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label">...</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="extension_description">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Moves the dash out of the overview transforming it in a dock</property>
+ <property name="justify">center</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">center</property>
+ <property name="spacing">5</property>
+ <child>
+ <object class="GtkLabel" id="label15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Created by</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label16">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">Michele (<a href="mailto:micxgx@gmail.com">micxgx@gmail.com</a>)</property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLinkButton" id="homepage_link">
+ <property name="label" translatable="yes">Webpage</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events"/>
+ <property name="halign">center</property>
+ <property name="relief">none</property>
+ <property name="uri">https://micheleg.github.io/dash-to-dock/</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="valign">end</property>
+ <property name="label" translatable="yes"><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></property>
+ <property name="use_markup">True</property>
+ <property name="justify">center</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">6</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="about_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">About</property>
+ </object>
+ <packing>
+ <property name="position">4</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="max_opacity_adjustement">
+ <property name="upper">1</property>
+ <property name="step_increment">0.01</property>
+ <property name="page_increment">0.10000000000000001</property>
+ </object>
+ <object class="GtkAdjustment" id="min_opacity_adjustement">
+ <property name="upper">1</property>
+ <property name="step_increment">0.01</property>
+ <property name="page_increment">0.10000000000000001</property>
+ </object>
+ <object class="GtkBox" id="advanced_transparency_dialog">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame" id="advanced_transparency_frame">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="advanced_transparency_listbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="advanced_transparency_listboxrow">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkBox" id="advanced_transparency_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkGrid" id="advanced_transparency_grid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="customize_alphas_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="customize_alphas_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Customize minimum and maximum opacity values</property>
+ <property name="justify">fill</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="min_alpha_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="min_alpha_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Minimum opacity</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="min_alpha_scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">min_opacity_adjustement</property>
+ <property name="lower_stepper_sensitivity">on</property>
+ <property name="restrict_to_fill_level">False</property>
+ <property name="fill_level">0</property>
+ <property name="round_digits">0</property>
+ <property name="digits">2</property>
+ <property name="value_pos">right</property>
+ <signal name="format-value" handler="min_opacity_scale_format_value_cb" swapped="no"/>
+ <signal name="value-changed" handler="min_opacity_scale_value_changed_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="max_alpha_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="max_alpha_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Maximum opacity</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="max_alpha_scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">max_opacity_adjustement</property>
+ <property name="lower_stepper_sensitivity">on</property>
+ <property name="restrict_to_fill_level">False</property>
+ <property name="fill_level">0</property>
+ <property name="round_digits">0</property>
+ <property name="digits">2</property>
+ <property name="value_pos">right</property>
+ <signal name="format-value" handler="max_opacity_scale_format_value_cb" swapped="no"/>
+ <signal name="value-changed" handler="max_opacity_scale_value_changed_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="pressure_threshold_adjustment">
+ <property name="upper">1000</property>
+ <property name="step_increment">50</property>
+ <property name="page_increment">250</property>
+ </object>
+ <object class="GtkAdjustment" id="shortcut_time_adjustment">
+ <property name="upper">10</property>
+ <property name="step_increment">0.25</property>
+ <property name="page_increment">1</property>
+ </object>
+ <object class="GtkBox" id="box_overlay_shortcut">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame" id="frame_overlay_show_dock">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="listbox_overlay_shortcut">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_overlay_shortcut">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="grid_overlay">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="overlay_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="overlay_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Number overlay</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="overlay_description">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Temporarily show the application numbers over the icons, corresponding to the shortcut.</property>
+ <property name="wrap">True</property>
+ <property name="max_width_chars">40</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_show_dock">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="grid_show_dock">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="show_dock_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="show_dock_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Show the dock if it is hidden</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="show_dock_description">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">If using autohide, the dock will appear for a short time when triggering the shortcut.</property>
+ <property name="wrap">True</property>
+ <property name="max_width_chars">40</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_extra_shortcut">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="grid_shortcut">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkEntry" id="shortcut_entry">
+ <property name="can_focus">True</property>
+ <property name="valign">center</property>
+ <property name="width_chars">12</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shortcut_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Shortcut for the options above</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shortcut_description">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Syntax: <Shift>, <Ctrl>, <Alt>, <Super></property>
+ <property name="wrap">True</property>
+ <property name="max_width_chars">40</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_timeout">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="grid_timeout">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="hexpand">True</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSpinButton" id="timeout_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="halign">end</property>
+ <property name="adjustment">shortcut_time_adjustment</property>
+ <property name="digits">3</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shortcut_timeout_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Hide timeout (s)</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="show_timeout_adjustment">
+ <property name="upper">1</property>
+ <property name="step_increment">0.050000000000000003</property>
+ <property name="page_increment">0.25</property>
+ </object>
+ <object class="GtkBox" id="intelligent_autohide_advanced_settings_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame" id="intelligent_autohide_advanced_settings_frame">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="listbox4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow8">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="buitin_theme2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_description2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Show the dock by mouse hover on the screen edge.</property>
+ <property name="wrap">True</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Autohide</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="autohide_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="require_pressure_checkbutton">
+ <property name="label" translatable="yes">Push to show: require pressure to show the dock</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="autohide_enable_in_fullscreen_checkbutton">
+ <property name="label" translatable="yes">Enable in fullscreen mode</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="margin_top">12</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow15">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="intellihide_grid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_description3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Show the dock when it doesn't obstruct application windows.</property>
+ <property name="wrap">True</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_label3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Dodge windows</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="intellihide_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="intellihide_mode_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkRadioButton" id="all_windows_radio_button">
+ <property name="label" translatable="yes">All windows</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="margin_top">12</property>
+ <property name="xalign">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="all_windows_radio_button_toggled_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="focus_application_windows_radio_button">
+ <property name="label" translatable="yes">Only focused application's windows</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">all_windows_radio_button</property>
+ <signal name="toggled" handler="focus_application_windows_radio_button_toggled_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="maximized_windows_radio_button">
+ <property name="label" translatable="yes">Only maximized windows</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">all_windows_radio_button</property>
+ <signal name="toggled" handler="maximized_windows_radio_button_toggled_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow5">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="grid2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="hexpand">True</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSpinButton" id="animation_duration_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="halign">end</property>
+ <property name="adjustment">animation_time_adjustment</property>
+ <property name="digits">3</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Animation duration (s)</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="hide_timeout_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="halign">end</property>
+ <property name="adjustment">hide_timeout_adjustment</property>
+ <property name="digits">3</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="show_timeout_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="halign">end</property>
+ <property name="adjustment">show_timeout_adjustment</property>
+ <property name="digits">3</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="pressure_threshold_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="text">0.000</property>
+ <property name="adjustment">pressure_threshold_adjustment</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Hide timeout (s)</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="show_timeout_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Show timeout (s)</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="pressure_threshold_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Pressure threshold</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/extensions/dash-to-dock/appIconIndicators.js b/extensions/dash-to-dock/appIconIndicators.js
new file mode 100644
index 0000000..ddff512
--- /dev/null
+++ b/extensions/dash-to-dock/appIconIndicators.js
@@ -0,0 +1,1102 @@
+const Cogl = imports.gi.Cogl;
+const Cairo = imports.cairo;
+const Clutter = imports.gi.Clutter;
+const GdkPixbuf = imports.gi.GdkPixbuf
+const Gio = imports.gi.Gio;
+const Gtk = imports.gi.Gtk;
+const Pango = imports.gi.Pango;
+const Shell = imports.gi.Shell;
+const St = imports.gi.St;
+
+const Util = imports.misc.util;
+
+const Me = imports.misc.extensionUtils.getCurrentExtension();
+const Utils = Me.imports.utils;
+
+let tracker = Shell.WindowTracker.get_default();
+
+const RunningIndicatorStyle = {
+ DEFAULT: 0,
+ DOTS: 1,
+ SQUARES: 2,
+ DASHES: 3,
+ SEGMENTED: 4,
+ SOLID: 5,
+ CILIORA: 6,
+ METRO: 7
+};
+
+const MAX_WINDOWS_CLASSES = 4;
+
+
+/*
+ * This is the main indicator class to be used. The desired bahviour is
+ * obtained by composing the desired classes below based on the settings.
+ *
+ */
+var AppIconIndicator = class DashToDock_AppIconIndicator {
+
+ constructor(source, settings) {
+ this._indicators = [];
+
+ // Unity indicators always enabled for now
+ let unityIndicator = new UnityIndicator(source, settings);
+ this._indicators.push(unityIndicator);
+
+ // Choose the style for the running indicators
+ let runningIndicator = null;
+ let runningIndicatorStyle;
+
+ if (settings.get_boolean('apply-custom-theme' )) {
+ runningIndicatorStyle = RunningIndicatorStyle.DOTS;
+ } else {
+ runningIndicatorStyle = settings.get_enum('running-indicator-style');
+ }
+
+ switch (runningIndicatorStyle) {
+ case RunningIndicatorStyle.DEFAULT:
+ runningIndicator = new RunningIndicatorDefault(source, settings);
+ break;
+
+ case RunningIndicatorStyle.DOTS:
+ runningIndicator = new RunningIndicatorDots(source, settings);
+ break;
+
+ case RunningIndicatorStyle.SQUARES:
+ runningIndicator = new RunningIndicatorSquares(source, settings);
+ break;
+
+ case RunningIndicatorStyle.DASHES:
+ runningIndicator = new RunningIndicatorDashes(source, settings);
+ break;
+
+ case RunningIndicatorStyle.SEGMENTED:
+ runningIndicator = new RunningIndicatorSegmented(source, settings);
+ break;
+
+ case RunningIndicatorStyle.SOLID:
+ runningIndicator = new RunningIndicatorSolid(source, settings);
+ break;
+
+ case RunningIndicatorStyle.CILIORA:
+ runningIndicator = new RunningIndicatorCiliora(source, settings);
+ break;
+
+ case RunningIndicatorStyle.METRO:
+ runningIndicator = new RunningIndicatorMetro(source, settings);
+ break;
+
+ default:
+ runningIndicator = new RunningIndicatorBase(source, settings);
+ }
+
+ this._indicators.push(runningIndicator);
+ }
+
+ update() {
+ for (let i=0; i<this._indicators.length; i++){
+ let indicator = this._indicators[i];
+ indicator.update();
+ }
+ }
+
+ destroy() {
+ for (let i=0; i<this._indicators.length; i++){
+ let indicator = this._indicators[i];
+ indicator.destroy();
+ }
+ }
+}
+
+/*
+ * Base class to be inherited by all indicators of any kind
+*/
+var IndicatorBase = class DashToDock_IndicatorBase {
+
+ constructor(source, settings) {
+ this._settings = settings;
+ this._source = source;
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+
+ this._sourceDestroyId = this._source.actor.connect('destroy', () => {
+ this._signalsHandler.destroy();
+ });
+ }
+
+ update() {
+ }
+
+ destroy() {
+ this._source.actor.disconnect(this._sourceDestroyId);
+ this._signalsHandler.destroy();
+ }
+};
+
+/*
+ * A base indicator class for running style, from which all other EunningIndicators should derive,
+ * providing some basic methods, variables definitions and their update, css style classes handling.
+ *
+ */
+var RunningIndicatorBase = class DashToDock_RunningIndicatorBase extends IndicatorBase {
+
+ constructor(source, settings) {
+ super(source, settings)
+
+ this._side = Utils.getPosition(this._settings);
+ this._nWindows = 0;
+
+ this._dominantColorExtractor = new DominantColorExtractor(this._source.app);
+
+ // These statuses take into account the workspace/monitor isolation
+ this._isFocused = false;
+ this._isRunning = false;
+ }
+
+ update() {
+ // Limit to 1 to MAX_WINDOWS_CLASSES windows classes
+ this._nWindows = Math.min(this._source.getInterestingWindows().length, MAX_WINDOWS_CLASSES);
+
+ // 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._source.app && this._nWindows > 0)
+ this._isFocused = true;
+ else
+ this._isFocused = false;
+
+ // In the case of workspace isolation, we need to hide the dots of apps with
+ // no windows in the current workspace
+ if (this._source.app.state != Shell.AppState.STOPPED && this._nWindows > 0)
+ this._isRunning = true;
+ else
+ this._isRunning = false;
+
+ this._updateCounterClass();
+ this._updateFocusClass();
+ this._updateDefaultDot();
+ }
+
+ _updateCounterClass() {
+ for (let i = 1; i <= MAX_WINDOWS_CLASSES; i++) {
+ let className = 'running' + i;
+ if (i != this._nWindows)
+ this._source.actor.remove_style_class_name(className);
+ else
+ this._source.actor.add_style_class_name(className);
+ }
+ }
+
+ _updateFocusClass() {
+ if (this._isFocused)
+ this._source.actor.add_style_class_name('focused');
+ else
+ this._source.actor.remove_style_class_name('focused');
+ }
+
+ _updateDefaultDot() {
+ if (this._isRunning)
+ this._source._dot.show();
+ else
+ this._source._dot.hide();
+ }
+
+ _hideDefaultDot() {
+ // I use opacity to hide the default dot because the show/hide function
+ // are used by the parent class.
+ this._source._dot.opacity = 0;
+ }
+
+ _restoreDefaultDot() {
+ this._source._dot.opacity = 255;
+ }
+
+ _enableBacklight() {
+
+ let colorPalette = this._dominantColorExtractor._getColorPalette();
+
+ // Fallback
+ if (colorPalette === null) {
+ this._source._iconContainer.set_style(
+ 'border-radius: 5px;' +
+ 'background-gradient-direction: vertical;' +
+ 'background-gradient-start: #e0e0e0;' +
+ 'background-gradient-end: darkgray;'
+ );
+
+ return;
+ }
+
+ this._source._iconContainer.set_style(
+ 'border-radius: 5px;' +
+ 'background-gradient-direction: vertical;' +
+ 'background-gradient-start: ' + colorPalette.original + ';' +
+ 'background-gradient-end: ' + colorPalette.darker + ';'
+ );
+
+ }
+
+ _disableBacklight() {
+ this._source._iconContainer.set_style(null);
+ }
+
+ destroy() {
+ this._disableBacklight();
+ // Remove glossy background if the children still exists
+ if (this._source._iconContainer.get_children().length > 1)
+ this._source._iconContainer.get_children()[1].set_style(null);
+ this._restoreDefaultDot();
+
+ super.destroy();
+ }
+};
+
+// We add a css class so third parties themes can limit their indicaor customization
+// to the case we do nothing
+var RunningIndicatorDefault = class DashToDock_RunningIndicatorDefault extends RunningIndicatorBase {
+
+ constructor(source, settings) {
+ super(source, settings);
+ this._source.actor.add_style_class_name('default');
+ }
+
+ destory() {
+ this._source.actor.remove_style_class_name('default');
+ super.destroy();
+ }
+};
+
+var RunningIndicatorDots = class DashToDock_RunningIndicatorDots extends RunningIndicatorBase {
+
+ constructor(source, settings) {
+ super(source, settings)
+
+ this._hideDefaultDot();
+
+ this._area = new St.DrawingArea({x_expand: true, y_expand: true});
+
+ // We draw for the bottom case and rotate the canvas for other placements
+ //set center of rotatoins to the center
+ this._area.set_pivot_point(0.5, 0.5);
+ // prepare transformation matrix
+ let m = new Cogl.Matrix();
+ m.init_identity();
+
+ switch (this._side) {
+ case St.Side.TOP:
+ m.xx = -1;
+ m.rotate(180, 0, 0, 1);
+ break
+
+ case St.Side.BOTTOM:
+ // nothing
+ break;
+
+ case St.Side.LEFT:
+ m.yy = -1;
+ m.rotate(90, 0, 0, 1);
+ break;
+
+ case St.Side.RIGHT:
+ m.rotate(-90, 0, 0, 1);
+ break
+ }
+
+ this._area.set_transform(m);
+
+ this._area.connect('repaint', this._updateIndicator.bind(this));
+ this._source._iconContainer.add_child(this._area);
+
+ let keys = ['custom-theme-running-dots-color',
+ 'custom-theme-running-dots-border-color',
+ 'custom-theme-running-dots-border-width',
+ 'custom-theme-customize-running-dots',
+ 'unity-backlit-items',
+ 'running-indicator-dominant-color'];
+
+ keys.forEach(function(key) {
+ this._signalsHandler.add([
+ this._settings,
+ 'changed::' + key,
+ this.update.bind(this)
+ ]);
+ }, this);
+
+ // Apply glossy background
+ // TODO: move to enable/disableBacklit to apply itonly to the running apps?
+ // TODO: move to css class for theming support
+ this._glossyBackgroundStyle = 'background-image: url(\'' + Me.path + '/media/glossy.svg\');' +
+ 'background-size: contain;';
+ }
+
+ update() {
+ super.update();
+
+ // Enable / Disable the backlight of running apps
+ if (!this._settings.get_boolean('apply-custom-theme') && this._settings.get_boolean('unity-backlit-items')) {
+ this._source._iconContainer.get_children()[1].set_style(this._glossyBackgroundStyle);
+ if (this._isRunning)
+ this._enableBacklight();
+ else
+ this._disableBacklight();
+ } else {
+ this._disableBacklight();
+ this._source._iconContainer.get_children()[1].set_style(null);
+ }
+
+ if (this._area)
+ this._area.queue_repaint();
+ }
+
+ _computeStyle() {
+
+ let [width, height] = this._area.get_surface_size();
+ this._width = height;
+ this._height = width;
+
+ // By defaut re-use the style - background color, and border width and color -
+ // of the default dot
+ let themeNode = this._source._dot.get_theme_node();
+ this._borderColor = themeNode.get_border_color(this._side);
+ this._borderWidth = themeNode.get_border_width(this._side);
+ this._bodyColor = themeNode.get_background_color();
+
+ if (!this._settings.get_boolean('apply-custom-theme')) {
+ // Adjust for the backlit case
+ if (this._settings.get_boolean('unity-backlit-items')) {
+ // Use dominant color for dots too if the backlit is enables
+ let colorPalette = this._dominantColorExtractor._getColorPalette();
+
+ // Slightly adjust the styling
+ this._borderWidth = 2;
+
+ if (colorPalette !== null) {
+ this._borderColor = Clutter.color_from_string(colorPalette.lighter)[1] ;
+ this._bodyColor = Clutter.color_from_string(colorPalette.darker)[1];
+ } else {
+ // Fallback
+ this._borderColor = Clutter.color_from_string('white')[1];
+ this._bodyColor = Clutter.color_from_string('gray')[1];
+ }
+ }
+
+ // Apply dominant color if requested
+ if (this._settings.get_boolean('running-indicator-dominant-color')) {
+ let colorPalette = this._dominantColorExtractor._getColorPalette();
+ if (colorPalette !== null) {
+ this._bodyColor = Clutter.color_from_string(colorPalette.original)[1];
+ }
+ }
+
+ // Finally, use customize style if requested
+ if (this._settings.get_boolean('custom-theme-customize-running-dots')) {
+ this._borderColor = Clutter.color_from_string(this._settings.get_string('custom-theme-running-dots-border-color'))[1];
+ this._borderWidth = this._settings.get_int('custom-theme-running-dots-border-width');
+ this._bodyColor = Clutter.color_from_string(this._settings.get_string('custom-theme-running-dots-color'))[1];
+ }
+ }
+
+ // Define the radius as an arbitrary size, but keep large enough to account
+ // for the drawing of the border.
+ this._radius = Math.max(this._width/22, this._borderWidth/2);
+ this._padding = 0; // distance from the margin
+ this._spacing = this._radius + this._borderWidth; // separation between the dots
+ }
+
+ _updateIndicator() {
+
+ let area = this._area;
+ let cr = this._area.get_context();
+
+ this._computeStyle();
+ this._drawIndicator(cr);
+ cr.$dispose();
+ }
+
+ _drawIndicator(cr) {
+ // Draw the required numbers of dots
+ let n = this._nWindows;
+
+ cr.setLineWidth(this._borderWidth);
+ Clutter.cairo_set_source_color(cr, this._borderColor);
+
+ // draw for the bottom case:
+ cr.translate((this._width - (2*n)*this._radius - (n-1)*this._spacing)/2, this._height - this._padding);
+ for (let i = 0; i < n; i++) {
+ cr.newSubPath();
+ cr.arc((2*i+1)*this._radius + i*this._spacing, -this._radius - this._borderWidth/2, this._radius, 0, 2*Math.PI);
+ }
+
+ cr.strokePreserve();
+ Clutter.cairo_set_source_color(cr, this._bodyColor);
+ cr.fill();
+ }
+
+ destroy() {
+ this._area.destroy();
+ super.destroy();
+ }
+};
+
+// Adapted from dash-to-panel by Jason DeRose
+// https://github.com/jderose9/dash-to-panel
+var RunningIndicatorCiliora = class DashToDock_RunningIndicatorCiliora extends RunningIndicatorDots {
+
+ _drawIndicator(cr) {
+ if (this._isRunning) {
+
+ let size = Math.max(this._width/20, this._borderWidth);
+ let spacing = size; // separation between the dots
+ let lineLength = this._width - (size*(this._nWindows-1)) - (spacing*(this._nWindows-1));
+ let padding = this._borderWidth;
+ // For the backlit case here we don't want the outer border visible
+ if (this._settings.get_boolean('unity-backlit-items') && !this._settings.get_boolean('custom-theme-customize-running-dots'))
+ padding = 0;
+ let yOffset = this._height - padding - size;
+
+ cr.setLineWidth(this._borderWidth);
+ Clutter.cairo_set_source_color(cr, this._borderColor);
+
+ cr.translate(0, yOffset);
+ cr.newSubPath();
+ cr.rectangle(0, 0, lineLength, size);
+ for (let i = 1; i < this._nWindows; i++) {
+ cr.newSubPath();
+ cr.rectangle(lineLength + (i*spacing) + ((i-1)*size), 0, size, size);
+ }
+
+ cr.strokePreserve();
+ Clutter.cairo_set_source_color(cr, this._bodyColor);
+ cr.fill();
+ }
+ }
+};
+
+// Adapted from dash-to-panel by Jason DeRose
+// https://github.com/jderose9/dash-to-panel
+var RunningIndicatorSegmented = class DashToDock_RunningIndicatorSegmented extends RunningIndicatorDots {
+
+ _drawIndicator(cr) {
+ if (this._isRunning) {
+ let size = Math.max(this._width/20, this._borderWidth);
+ let spacing = Math.ceil(this._width/18); // separation between the dots
+ let dashLength = Math.ceil((this._width - ((this._nWindows-1)*spacing))/this._nWindows);
+ let lineLength = this._width - (size*(this._nWindows-1)) - (spacing*(this._nWindows-1));
+ let padding = this._borderWidth;
+ // For the backlit case here we don't want the outer border visible
+ if (this._settings.get_boolean('unity-backlit-items') && !this._settings.get_boolean('custom-theme-customize-running-dots'))
+ padding = 0;
+ let yOffset = this._height - padding - size;
+
+ cr.setLineWidth(this._borderWidth);
+ Clutter.cairo_set_source_color(cr, this._borderColor);
+
+ cr.translate(0, yOffset);
+ for (let i = 0; i < this._nWindows; i++) {
+ cr.newSubPath();
+ cr.rectangle(i*dashLength + i*spacing, 0, dashLength, size);
+ }
+
+ cr.strokePreserve();
+ Clutter.cairo_set_source_color(cr, this._bodyColor);
+ cr.fill()
+ }
+ }
+};
+
+// Adapted from dash-to-panel by Jason DeRose
+// https://github.com/jderose9/dash-to-panel
+var RunningIndicatorSolid = class DashToDock_RunningIndicatorSolid extends RunningIndicatorDots {
+
+ _drawIndicator(cr) {
+ if (this._isRunning) {
+
+ let size = Math.max(this._width/20, this._borderWidth);
+ let padding = this._borderWidth;
+ // For the backlit case here we don't want the outer border visible
+ if (this._settings.get_boolean('unity-backlit-items') && !this._settings.get_boolean('custom-theme-customize-running-dots'))
+ padding = 0;
+ let yOffset = this._height - padding - size;
+
+ cr.setLineWidth(this._borderWidth);
+ Clutter.cairo_set_source_color(cr, this._borderColor);
+
+ cr.translate(0, yOffset);
+ cr.newSubPath();
+ cr.rectangle(0, 0, this._width, size);
+
+ cr.strokePreserve();
+ Clutter.cairo_set_source_color(cr, this._bodyColor);
+ cr.fill();
+
+ }
+ }
+};
+
+// Adapted from dash-to-panel by Jason DeRose
+// https://github.com/jderose9/dash-to-panel
+var RunningIndicatorSquares = class DashToDock_RunningIndicatorSquares extends RunningIndicatorDots {
+
+ _drawIndicator(cr) {
+ if (this._isRunning) {
+ let size = Math.max(this._width/11, this._borderWidth);
+ let padding = this._borderWidth;
+ let spacing = Math.ceil(this._width/18); // separation between the dots
+ let yOffset = this._height - padding - size;
+
+ cr.setLineWidth(this._borderWidth);
+ Clutter.cairo_set_source_color(cr, this._borderColor);
+
+ cr.translate(Math.floor((this._width - this._nWindows*size - (this._nWindows-1)*spacing)/2), yOffset);
+ for (let i = 0; i < this._nWindows; i++) {
+ cr.newSubPath();
+ cr.rectangle(i*size + i*spacing, 0, size, size);
+ }
+ cr.strokePreserve();
+ Clutter.cairo_set_source_color(cr, this._bodyColor);
+ cr.fill();
+ }
+ }
+}
+
+// Adapted from dash-to-panel by Jason DeRose
+// https://github.com/jderose9/dash-to-panel
+var RunningIndicatorDashes = class DashToDock_RunningIndicatorDashes extends RunningIndicatorDots {
+
+ _drawIndicator(cr) {
+ if (this._isRunning) {
+ let size = Math.max(this._width/20, this._borderWidth);
+ let padding = this._borderWidth;
+ let spacing = Math.ceil(this._width/18); // separation between the dots
+ let dashLength = Math.floor(this._width/4) - spacing;
+ let yOffset = this._height - padding - size;
+
+ cr.setLineWidth(this._borderWidth);
+ Clutter.cairo_set_source_color(cr, this._borderColor);
+
+ cr.translate(Math.floor((this._width - this._nWindows*dashLength - (this._nWindows-1)*spacing)/2), yOffset);
+ for (let i = 0; i < this._nWindows; i++) {
+ cr.newSubPath();
+ cr.rectangle(i*dashLength + i*spacing, 0, dashLength, size);
+ }
+
+ cr.strokePreserve();
+ Clutter.cairo_set_source_color(cr, this._bodyColor);
+ cr.fill();
+ }
+ }
+}
+
+// Adapted from dash-to-panel by Jason DeRose
+// https://github.com/jderose9/dash-to-panel
+var RunningIndicatorMetro = class DashToDock_RunningIndicatorMetro extends RunningIndicatorDots {
+
+ constructor(source, settings) {
+ super(source, settings);
+ this._source.actor.add_style_class_name('metro');
+ }
+
+ destroy() {
+ this._source.actor.remove_style_class_name('metro');
+ super.destroy();
+ }
+
+ _drawIndicator(cr) {
+ if (this._isRunning) {
+ let size = Math.max(this._width/20, this._borderWidth);
+ let padding = 0;
+ // For the backlit case here we don't want the outer border visible
+ if (this._settings.get_boolean('unity-backlit-items') && !this._settings.get_boolean('custom-theme-customize-running-dots'))
+ padding = 0;
+ let yOffset = this._height - padding - size;
+
+ let n = this._nWindows;
+ if(n <= 1) {
+ cr.translate(0, yOffset);
+ Clutter.cairo_set_source_color(cr, this._bodyColor);
+ cr.newSubPath();
+ cr.rectangle(0, 0, this._width, size);
+ cr.fill();
+ } else {
+ let blackenedLength = (1/48)*this._width; // need to scale with the SVG for the stacked highlight
+ let darkenedLength = this._isFocused ? (2/48)*this._width : (10/48)*this._width;
+ let blackenedColor = this._bodyColor.shade(.3);
+ let darkenedColor = this._bodyColor.shade(.7);
+
+ cr.translate(0, yOffset);
+
+ Clutter.cairo_set_source_color(cr, this._bodyColor);
+ cr.newSubPath();
+ cr.rectangle(0, 0, this._width - darkenedLength - blackenedLength, size);
+ cr.fill();
+ Clutter.cairo_set_source_color(cr, blackenedColor);
+ cr.newSubPath();
+ cr.rectangle(this._width - darkenedLength - blackenedLength, 0, 1, size);
+ cr.fill();
+ Clutter.cairo_set_source_color(cr, darkenedColor);
+ cr.newSubPath();
+ cr.rectangle(this._width - darkenedLength, 0, darkenedLength, size);
+ cr.fill();
+ }
+ }
+ }
+}
+
+/*
+ * Unity like notification and progress indicators
+ */
+var UnityIndicator = class DashToDock_UnityIndicator extends IndicatorBase {
+
+ constructor(source, settings) {
+
+ super(source, settings);
+
+ this._notificationBadgeLabel = new St.Label();
+ this._notificationBadgeBin = new St.Bin({
+ child: this._notificationBadgeLabel,
+ x_align: St.Align.END, y_align: St.Align.START,
+ x_expand: true, y_expand: true
+ });
+ this._notificationBadgeLabel.add_style_class_name('notification-badge');
+ this._notificationBadgeCount = 0;
+ this._notificationBadgeBin.hide();
+
+ this._source._iconContainer.add_child(this._notificationBadgeBin);
+ this._source._iconContainer.connect('allocation-changed', this.updateNotificationBadge.bind(this));
+
+ this._remoteEntries = [];
+ this._source.remoteModel.lookupById(this._source.app.id).forEach(
+ (entry) => {
+ this.insertEntryRemote(entry);
+ }
+ );
+
+ this._signalsHandler.add([
+ this._source.remoteModel,
+ 'entry-added',
+ this._onLauncherEntryRemoteAdded.bind(this)
+ ], [
+ this._source.remoteModel,
+ 'entry-removed',
+ this._onLauncherEntryRemoteRemoved.bind(this)
+ ])
+ }
+
+ _onLauncherEntryRemoteAdded(remoteModel, entry) {
+ if (!entry || !entry.appId())
+ return;
+ if (this._source && this._source.app && this._source.app.id == entry.appId()) {
+ this.insertEntryRemote(entry);
+ }
+ }
+
+ _onLauncherEntryRemoteRemoved(remoteModel, entry) {
+ if (!entry || !entry.appId())
+ return;
+
+ if (this._source && this._source.app && this._source.app.id == entry.appId()) {
+ this.removeEntryRemote(entry);
+ }
+ }
+
+ updateNotificationBadge() {
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ let [minWidth, natWidth] = this._source._iconContainer.get_preferred_width(-1);
+ let logicalNatWidth = natWidth / scaleFactor;
+ let font_size = Math.max(10, Math.round(logicalNatWidth / 5));
+ let margin_left = Math.round(logicalNatWidth / 4);
+
+ this._notificationBadgeLabel.set_style(
+ 'font-size: ' + font_size + 'px;' +
+ 'margin-left: ' + margin_left + 'px;'
+ );
+
+ this._notificationBadgeBin.width = Math.round(logicalNatWidth - margin_left);
+ this._notificationBadgeLabel.clutter_text.ellipsize = Pango.EllipsizeMode.MIDDLE;
+ }
+
+ _notificationBadgeCountToText(count) {
+ if (count <= 9999) {
+ return count.toString();
+ } else if (count < 1e5) {
+ let thousands = count / 1e3;
+ return thousands.toFixed(1).toString() + "k";
+ } else if (count < 1e6) {
+ let thousands = count / 1e3;
+ return thousands.toFixed(0).toString() + "k";
+ } else if (count < 1e8) {
+ let millions = count / 1e6;
+ return millions.toFixed(1).toString() + "M";
+ } else if (count < 1e9) {
+ let millions = count / 1e6;
+ return millions.toFixed(0).toString() + "M";
+ } else {
+ let billions = count / 1e9;
+ return billions.toFixed(1).toString() + "B";
+ }
+ }
+
+ setNotificationBadge(count) {
+ this._notificationBadgeCount = count;
+ let text = this._notificationBadgeCountToText(count);
+ this._notificationBadgeLabel.set_text(text);
+ }
+
+ toggleNotificationBadge(activate) {
+ if (activate && this._notificationBadgeCount > 0) {
+ this.updateNotificationBadge();
+ this._notificationBadgeBin.show();
+ }
+ else
+ this._notificationBadgeBin.hide();
+ }
+
+ _showProgressOverlay() {
+ if (this._progressOverlayArea) {
+ this._updateProgressOverlay();
+ return;
+ }
+
+ this._progressOverlayArea = new St.DrawingArea({x_expand: true, y_expand: true});
+ this._progressOverlayArea.connect('repaint', () => {
+ this._drawProgressOverlay(this._progressOverlayArea);
+ });
+
+ this._source._iconContainer.add_child(this._progressOverlayArea);
+ this._updateProgressOverlay();
+ }
+
+ _hideProgressOverlay() {
+ if (this._progressOverlayArea)
+ this._progressOverlayArea.destroy();
+ this._progressOverlayArea = null;
+ }
+
+ _updateProgressOverlay() {
+ if (this._progressOverlayArea)
+ this._progressOverlayArea.queue_repaint();
+ }
+
+ _drawProgressOverlay(area) {
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ let [surfaceWidth, surfaceHeight] = area.get_surface_size();
+ let cr = area.get_context();
+
+ let iconSize = this._source.icon.iconSize * scaleFactor;
+
+ let x = Math.floor((surfaceWidth - iconSize) / 2);
+ let y = Math.floor((surfaceHeight - iconSize) / 2);
+
+ let lineWidth = Math.floor(1.0 * scaleFactor);
+ let padding = Math.floor(iconSize * 0.05);
+ let width = iconSize - 2.0*padding;
+ let height = Math.floor(Math.min(18.0*scaleFactor, 0.20*iconSize));
+ x += padding;
+ y += iconSize - height - padding;
+
+ cr.setLineWidth(lineWidth);
+
+ // Draw the outer stroke
+ let stroke = new Cairo.LinearGradient(0, y, 0, y + height);
+ let fill = null;
+ stroke.addColorStopRGBA(0.5, 0.5, 0.5, 0.5, 0.1);
+ stroke.addColorStopRGBA(0.9, 0.8, 0.8, 0.8, 0.4);
+ Utils.drawRoundedLine(cr, x + lineWidth/2.0, y + lineWidth/2.0, width, height, true, true, stroke, fill);
+
+ // Draw the background
+ x += lineWidth;
+ y += lineWidth;
+ width -= 2.0*lineWidth;
+ height -= 2.0*lineWidth;
+
+ stroke = Cairo.SolidPattern.createRGBA(0.20, 0.20, 0.20, 0.9);
+ fill = new Cairo.LinearGradient(0, y, 0, y + height);
+ fill.addColorStopRGBA(0.4, 0.25, 0.25, 0.25, 1.0);
+ fill.addColorStopRGBA(0.9, 0.35, 0.35, 0.35, 1.0);
+ Utils.drawRoundedLine(cr, x + lineWidth/2.0, y + lineWidth/2.0, width, height, true, true, stroke, fill);
+
+ // Draw the finished bar
+ x += lineWidth;
+ y += lineWidth;
+ width -= 2.0*lineWidth;
+ height -= 2.0*lineWidth;
+
+ let finishedWidth = Math.ceil(this._progress * width);
+ stroke = Cairo.SolidPattern.createRGBA(0.8, 0.8, 0.8, 1.0);
+ fill = Cairo.SolidPattern.createRGBA(0.9, 0.9, 0.9, 1.0);
+
+ if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
+ Utils.drawRoundedLine(cr, x + lineWidth/2.0 + width - finishedWidth, y + lineWidth/2.0, finishedWidth, height, true, true, stroke, fill);
+ else
+ Utils.drawRoundedLine(cr, x + lineWidth/2.0, y + lineWidth/2.0, finishedWidth, height, true, true, stroke, fill);
+
+ cr.$dispose();
+ }
+
+ setProgress(progress) {
+ this._progress = Math.min(Math.max(progress, 0.0), 1.0);
+ this._updateProgressOverlay();
+ }
+
+ toggleProgressOverlay(activate) {
+ if (activate) {
+ this._showProgressOverlay();
+ }
+ else {
+ this._hideProgressOverlay();
+ }
+ }
+
+ insertEntryRemote(remote) {
+ if (!remote || this._remoteEntries.indexOf(remote) !== -1)
+ return;
+
+ this._remoteEntries.push(remote);
+ this._selectEntryRemote(remote);
+ }
+
+ removeEntryRemote(remote) {
+ if (!remote || this._remoteEntries.indexOf(remote) == -1)
+ return;
+
+ this._remoteEntries.splice(this._remoteEntries.indexOf(remote), 1);
+
+ if (this._remoteEntries.length > 0) {
+ this._selectEntryRemote(this._remoteEntries[this._remoteEntries.length-1]);
+ } else {
+ this.setNotificationBadge(0);
+ this.toggleNotificationBadge(false);
+ this.setProgress(0);
+ this.toggleProgressOverlay(false);
+ }
+ }
+
+ _selectEntryRemote(remote) {
+ if (!remote)
+ return;
+
+ this._signalsHandler.removeWithLabel('entry-remotes');
+
+ this._signalsHandler.addWithLabel('entry-remotes',
+ [
+ remote,
+ 'count-changed',
+ (remote, value) => {
+ this.setNotificationBadge(value);
+ }
+ ], [
+ remote,
+ 'count-visible-changed',
+ (remote, value) => {
+ this.toggleNotificationBadge(value);
+ }
+ ], [
+ remote,
+ 'progress-changed',
+ (remote, value) => {
+ this.setProgress(value);
+ }
+ ], [
+ remote,
+ 'progress-visible-changed',
+ (remote, value) => {
+ this.toggleProgressOverlay(value);
+ }
+ ]);
+
+ this.setNotificationBadge(remote.count());
+ this.toggleNotificationBadge(remote.countVisible());
+ this.setProgress(remote.progress());
+ this.toggleProgressOverlay(remote.progressVisible());
+ }
+}
+
+
+// We need an icons theme object, this is the only way I managed to get
+// pixel buffers that can be used for calculating the backlight color
+let themeLoader = null;
+
+// Global icon cache. Used for Unity7 styling.
+let iconCacheMap = new Map();
+// Max number of items to store
+// We don't expect to ever reach this number, but let's put an hard limit to avoid
+// even the remote possibility of the cached items to grow indefinitely.
+const MAX_CACHED_ITEMS = 1000;
+// When the size exceed it, the oldest 'n' ones are deleted
+const BATCH_SIZE_TO_DELETE = 50;
+// The icon size used to extract the dominant color
+const DOMINANT_COLOR_ICON_SIZE = 64;
+
+// Compute dominant color frim the app icon.
+// The color is cached for efficiency.
+var DominantColorExtractor = class DashToDock_DominantColorExtractor {
+
+ constructor(app) {
+ this._app = app;
+ }
+
+ /**
+ * Try to get the pixel buffer for the current icon, if not fail gracefully
+ */
+ _getIconPixBuf() {
+ let iconTexture = this._app.create_icon_texture(16);
+
+ if (themeLoader === null) {
+ let ifaceSettings = new Gio.Settings({ schema: "org.gnome.desktop.interface" });
+
+ themeLoader = new Gtk.IconTheme(),
+ themeLoader.set_custom_theme(ifaceSettings.get_string('icon-theme')); // Make sure the correct theme is loaded
+ }
+
+ // Unable to load the icon texture, use fallback
+ if (iconTexture instanceof St.Icon === false) {
+ return null;
+ }
+
+ iconTexture = iconTexture.get_gicon();
+
+ // Unable to load the icon texture, use fallback
+ if (iconTexture === null) {
+ return null;
+ }
+
+ if (iconTexture instanceof Gio.FileIcon) {
+ // Use GdkPixBuf to load the pixel buffer from the provided file path
+ return GdkPixbuf.Pixbuf.new_from_file(iconTexture.get_file().get_path());
+ }
+
+ // Get the pixel buffer from the icon theme
+ let icon_info = themeLoader.lookup_icon(iconTexture.get_names()[0], DOMINANT_COLOR_ICON_SIZE, 0);
+ if (icon_info !== null)
+ return icon_info.load_icon();
+ else
+ return null;
+ }
+
+ /**
+ * The backlight color choosing algorithm was mostly ported to javascript from the
+ * Unity7 C++ source of Canonicals:
+ * https://bazaar.launchpad.net/~unity-team/unity/trunk/view/head:/launcher/LauncherIcon.cpp
+ * so it more or less works the same way.
+ */
+ _getColorPalette() {
+ if (iconCacheMap.get(this._app.get_id())) {
+ // We already know the answer
+ return iconCacheMap.get(this._app.get_id());
+ }
+
+ let pixBuf = this._getIconPixBuf();
+ if (pixBuf == null)
+ return null;
+
+ let pixels = pixBuf.get_pixels(),
+ offset = 0;
+
+ let total = 0,
+ rTotal = 0,
+ gTotal = 0,
+ bTotal = 0;
+
+ let resample_y = 1,
+ resample_x = 1;
+
+ // Resampling of large icons
+ // We resample icons larger than twice the desired size, as the resampling
+ // to a size s
+ // DOMINANT_COLOR_ICON_SIZE < s < 2*DOMINANT_COLOR_ICON_SIZE,
+ // most of the case exactly DOMINANT_COLOR_ICON_SIZE as the icon size is tipycally
+ // a multiple of it.
+ let width = pixBuf.get_width();
+ let height = pixBuf.get_height();
+
+ // Resample
+ if (height >= 2* DOMINANT_COLOR_ICON_SIZE)
+ resample_y = Math.floor(height/DOMINANT_COLOR_ICON_SIZE);
+
+ if (width >= 2* DOMINANT_COLOR_ICON_SIZE)
+ resample_x = Math.floor(width/DOMINANT_COLOR_ICON_SIZE);
+
+ if (resample_x !==1 || resample_y !== 1)
+ pixels = this._resamplePixels(pixels, resample_x, resample_y);
+
+ // computing the limit outside the for (where it would be repeated at each iteration)
+ // for performance reasons
+ let limit = pixels.length;
+ for (let offset = 0; offset < limit; offset+=4) {
+ let r = pixels[offset],
+ g = pixels[offset + 1],
+ b = pixels[offset + 2],
+ a = pixels[offset + 3];
+
+ let saturation = (Math.max(r,g, b) - Math.min(r,g, b));
+ let relevance = 0.1 * 255 * 255 + 0.9 * a * saturation;
+
+ rTotal += r * relevance;
+ gTotal += g * relevance;
+ bTotal += b * relevance;
+
+ total += relevance;
+ }
+
+ total = total * 255;
+
+ let r = rTotal / total,
+ g = gTotal / total,
+ b = bTotal / total;
+
+ let hsv = Utils.ColorUtils.RGBtoHSV(r * 255, g * 255, b * 255);
+
+ if (hsv.s > 0.15)
+ hsv.s = 0.65;
+ hsv.v = 0.90;
+
+ let rgb = Utils.ColorUtils.HSVtoRGB(hsv.h, hsv.s, hsv.v);
+
+ // Cache the result.
+ let backgroundColor = {
+ lighter: Utils.ColorUtils.ColorLuminance(rgb.r, rgb.g, rgb.b, 0.2),
+ original: Utils.ColorUtils.ColorLuminance(rgb.r, rgb.g, rgb.b, 0),
+ darker: Utils.ColorUtils.ColorLuminance(rgb.r, rgb.g, rgb.b, -0.5)
+ };
+
+ if (iconCacheMap.size >= MAX_CACHED_ITEMS) {
+ //delete oldest cached values (which are in order of insertions)
+ let ctr=0;
+ for (let key of iconCacheMap.keys()) {
+ if (++ctr > BATCH_SIZE_TO_DELETE)
+ break;
+ iconCacheMap.delete(key);
+ }
+ }
+
+ iconCacheMap.set(this._app.get_id(), backgroundColor);
+
+ return backgroundColor;
+ }
+
+ /**
+ * Downsample large icons before scanning for the backlight color to
+ * improve performance.
+ *
+ * @param pixBuf
+ * @param pixels
+ * @param resampleX
+ * @param resampleY
+ *
+ * @return [];
+ */
+ _resamplePixels (pixels, resampleX, resampleY) {
+ let resampledPixels = [];
+ // computing the limit outside the for (where it would be repeated at each iteration)
+ // for performance reasons
+ let limit = pixels.length / (resampleX * resampleY) / 4;
+ for (let i = 0; i < limit; i++) {
+ let pixel = i * resampleX * resampleY;
+
+ resampledPixels.push(pixels[pixel * 4]);
+ resampledPixels.push(pixels[pixel * 4 + 1]);
+ resampledPixels.push(pixels[pixel * 4 + 2]);
+ resampledPixels.push(pixels[pixel * 4 + 3]);
+ }
+
+ return resampledPixels;
+ }
+};
diff --git a/extensions/dash-to-dock/appIcons.js b/extensions/dash-to-dock/appIcons.js
new file mode 100644
index 0000000..3b28304
--- /dev/null
+++ b/extensions/dash-to-dock/appIcons.js
@@ -0,0 +1,1172 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Clutter = imports.gi.Clutter;
+const GdkPixbuf = imports.gi.GdkPixbuf
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Gtk = imports.gi.Gtk;
+const Signals = imports.signals;
+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;
+const AppIconIndicators = Me.imports.appIconIndicators;
+
+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,
+ MINIMIZE_OR_PREVIEWS: 6,
+ FOCUS_OR_PREVIEWS: 7,
+ QUIT: 8,
+};
+
+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);
+ * - Customized indicators for running applications in place of 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
+ */
+var MyAppIcon = class DashToDock_AppIcon extends AppDisplay.AppIcon {
+
+ // settings are required inside.
+ constructor(settings, remoteModel, app, monitorIndex, iconParams) {
+ super(app, 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.remoteModel = remoteModel;
+ this._indicator = null;
+
+ this._updateIndicatorStyle();
+
+ // 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._windowsChangedId = this.app.connect('windows-changed',
+ this.onWindowsChanged.bind(this));
+ this._focusAppChangeId = tracker.connect('notify::focus-app',
+ this._onFocusAppChanged.bind(this));
+
+ // In Wayland sessions, this signal is needed to track the state of windows dragged
+ // from one monitor to another. As this is triggered quite often (whenever a new winow
+ // of any application opened or moved to a different desktop),
+ // we restrict this signal to the case when 'isolate-monitors' is true,
+ // and if there are at least 2 monitors.
+ if (this._dtdSettings.get_boolean('isolate-monitors') &&
+ Main.layoutManager.monitors.length > 1) {
+ this._signalsHandler.removeWithLabel('isolate-monitors');
+ this._signalsHandler.addWithLabel('isolate-monitors', [
+ global.display,
+ 'window-entered-monitor',
+ this._onWindowEntered.bind(this)
+ ]);
+ }
+
+ this._progressOverlayArea = null;
+ this._progress = 0;
+
+ let keys = ['apply-custom-theme',
+ 'running-indicator-style',
+ ];
+
+ keys.forEach(function(key) {
+ this._signalsHandler.add([
+ this._dtdSettings,
+ 'changed::' + key,
+ this._updateIndicatorStyle.bind(this)
+ ]);
+ }, this);
+
+ this._dtdSettings.connect('changed::scroll-action', () => {
+ this._optionalScrollCycleWindows();
+ });
+ this._optionalScrollCycleWindows();
+
+ this._numberOverlay();
+
+ this._previewMenuManager = null;
+ this._previewMenu = null;
+ }
+
+ _onDestroy() {
+ super._onDestroy();
+
+ // 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
+
+ if (this._windowsChangedId > 0)
+ this.app.disconnect(this._windowsChangedId);
+ this._windowsChangedId = 0;
+
+ if (this._focusAppChangeId > 0) {
+ tracker.disconnect(this._focusAppChangeId);
+ this._focusAppChangeId = 0;
+ }
+
+ this._signalsHandler.destroy();
+
+ if (this._scrollEventHandler)
+ this.actor.disconnect(this._scrollEventHandler);
+ }
+
+ // TOOD Rename this function
+ _updateIndicatorStyle() {
+
+ if (this._indicator !== null) {
+ this._indicator.destroy();
+ this._indicator = null;
+ }
+ this._indicator = new AppIconIndicators.AppIconIndicator(this, this._dtdSettings);
+ this._indicator.update();
+ }
+
+ _onWindowEntered(metaScreen, monitorIndex, metaWin) {
+ let app = Shell.WindowTracker.get_default().get_window_app(metaWin);
+ if (app && app.get_id() == this.app.get_id())
+ this.onWindowsChanged();
+ }
+
+ _optionalScrollCycleWindows() {
+ 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',
+ this.onScrollEvent.bind(this));
+ }
+
+ onScrollEvent(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, () => {
+ 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() {
+
+ if (this._menu && this._menu.isOpen)
+ this._menu.update();
+
+ this._indicator.update();
+ this.updateIconGeometry();
+ }
+
+ /**
+ * Update taraget for minimization animation
+ */
+ updateIconGeometry() {
+ // 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);
+ });
+ }
+
+ _updateRunningStyle() {
+ // The logic originally in this function has been moved to
+ // AppIconIndicatorBase._updateDefaultDot(). However it cannot be removed as
+ // it called by the parent constructor.
+ }
+
+ popupMenu() {
+ this._removeMenuTimeout();
+ this.actor.fake_release();
+ this._draggable.fakeRelease();
+
+ if (!this._menu) {
+ this._menu = new MyAppIconMenu(this, this._dtdSettings);
+ this._menu.connect('activate-window', (menu, window) => {
+ this.activateWindow(window);
+ });
+ this._menu.connect('open-state-changed', (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', () => {
+ 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() {
+ this._indicator.update();
+ }
+
+ activate(button) {
+ 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
+ super.activate(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.FOCUS_OR_PREVIEWS:
+ if (this.app == focusedApp &&
+ (windows.length > 1 || modifiers || button != 1)) {
+ this._windowPreviews();
+ } else {
+ // Activate the first window
+ let w = windows[0];
+ Main.activateWindow(w);
+ }
+ 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.MINIMIZE_OR_PREVIEWS:
+ // 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 (!Main.overview._shown){
+ 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);
+ }
+ } else {
+ // Launch previews when multiple windows are present
+ 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() {
+ return this.actor.hover && (!this._menu || !this._menu.isOpen) &&
+ (!this._previewMenu || !this._previewMenu.isOpen);
+ }
+
+ _windowPreviews() {
+ 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', (menu, isPoppedUp) => {
+ if (!isPoppedUp)
+ this._onMenuPoppedDown();
+ });
+ let id = Main.overview.connect('hiding', () => {
+ 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(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();
+ }
+ }
+
+ _numberOverlay() {
+ // 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() {
+ // 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(number) {
+ this._numberOverlayOrder = number;
+ this._numberOverlayLabel.set_text(number.toString());
+ }
+
+ toggleNumberOverlay(activate) {
+ if (activate && this._numberOverlayOrder > -1) {
+ this.updateNumberOverlay();
+ this._numberOverlayBin.show();
+ }
+ else
+ this._numberOverlayBin.hide();
+ }
+
+ _minimizeWindow(param) {
+ // Param true make all app windows minimize
+ let windows = this.getInterestingWindows();
+ let current_workspace = global.workspace_manager.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() {
+ // 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.workspace_manager.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() {
+ let windows = this.getInterestingWindows();
+ for (let i = 0; i < windows.length; i++)
+ windows[i].delete(global.get_current_time());
+ }
+
+ _cycleThroughWindows(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() {
+ 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() {
+ 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 = class DashToDock_MyAppIconMenu extends AppDisplay.AppIconMenu {
+
+ constructor(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
+ super(source);
+
+ // Change the initialized side where required.
+ this._arrowSide = side;
+ this._boxPointer._arrowSide = side;
+ this._boxPointer._userArrowSide = side;
+
+ this._dtdSettings = settings;
+ }
+
+ _redisplay() {
+ 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', () => {
+ 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', () => {
+ 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', (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', () => {
+ let favs = AppFavorites.getAppFavorites();
+ favs.removeFavorite(this._source.app.get_id());
+ });
+ } else {
+ let item = this._appendMenuItem(_("Add to Favorites"));
+ item.connect('activate', () => {
+ 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', () => {
+ 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 {
+ super._redisplay();
+ }
+
+ // quit menu
+ this._appendSeparator();
+ this._quitfromDashMenuItem = this._appendMenuItem(_("Quit"));
+ this._quitfromDashMenuItem.connect('activate', () => {
+ 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() {
+
+ 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 %d Windows").format(windows.length));
+
+ 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(this._updateSeparatorVisibility.bind(this));
+ }
+
+
+ }
+
+ _populateAllWindowMenu(windows) {
+
+ this._allWindowsMenuItem.menu.removeAll();
+
+ if (windows.length > 0) {
+
+ let activeWorkspace = global.workspace_manager.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', () => {
+ this.emit('activate-window', window);
+ });
+
+ // This is to achieve a more gracefull transition when the last windows is closed.
+ item.connect('destroy', () => {
+ 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.workspace_manager.get_active_workspace_index();
+ });
+
+ if (settings.get_boolean('isolate-monitors'))
+ windows = windows.filter(function(w) {
+ return w.get_monitor() == monitorIndex;
+ });
+
+ return windows;
+}
+
+/**
+ * A wrapper class around the ShowAppsIcon class.
+ *
+ * - Pass settings to the constructor
+ * - set label position based on dash orientation (Note, I am reusing most machinery of the appIcon class)
+ * - implement a popupMenu based on the AppIcon code (Note, I am reusing most machinery of the appIcon class)
+ *
+ * I can't subclass the original object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973.
+ * thus use this pattern where the real showAppsIcon object is encaptulated, and a reference to it will be properly wired upon
+ * use of this class in place of the original showAppsButton.
+ *
+ */
+
+var ShowAppsIconWrapper = class DashToDock_ShowAppsIconWrapper {
+ constructor(settings) {
+ this._dtdSettings = settings;
+ this.realShowAppsIcon = new Dash.ShowAppsIcon();
+
+ /* the variable equivalent to toggleButton has a different name in the appIcon class
+ (actor): duplicate reference to easily reuse appIcon methods */
+ this.actor = this.realShowAppsIcon.toggleButton;
+
+ // Re-use appIcon methods
+ this._removeMenuTimeout = AppDisplay.AppIcon.prototype._removeMenuTimeout;
+ this._setPopupTimeout = AppDisplay.AppIcon.prototype._setPopupTimeout;
+ this._onButtonPress = AppDisplay.AppIcon.prototype._onButtonPress;
+ this._onKeyboardPopupMenu = AppDisplay.AppIcon.prototype._onKeyboardPopupMenu;
+ this._onLeaveEvent = AppDisplay.AppIcon.prototype._onLeaveEvent;
+ this._onTouchEvent = AppDisplay.AppIcon.prototype._onTouchEvent;
+ this._onMenuPoppedDown = AppDisplay.AppIcon.prototype._onMenuPoppedDown;
+
+ // No action on clicked (showing of the appsview is controlled elsewhere)
+ this._onClicked = (actor, button) => {
+ this._removeMenuTimeout();
+ };
+
+ this.actor.connect('leave-event', this._onLeaveEvent.bind(this));
+ this.actor.connect('button-press-event', this._onButtonPress.bind(this));
+ this.actor.connect('touch-event', this._onTouchEvent.bind(this));
+ this.actor.connect('clicked', this._onClicked.bind(this));
+ this.actor.connect('popup-menu', this._onKeyboardPopupMenu.bind(this));
+
+ this._menu = null;
+ this._menuManager = new PopupMenu.PopupMenuManager(this);
+ this._menuTimeoutId = 0;
+
+ this.realShowAppsIcon._dtdSettings = settings;
+ this.realShowAppsIcon.showLabel = itemShowLabel;
+ }
+
+ popupMenu() {
+ this._removeMenuTimeout();
+ this.actor.fake_release();
+
+ if (!this._menu) {
+ this._menu = new MyShowAppsIconMenu(this, this._dtdSettings);
+ this._menu.connect('open-state-changed', (menu, isPoppedUp) => {
+ if (!isPoppedUp)
+ this._onMenuPoppedDown();
+ });
+ let id = Main.overview.connect('hiding', () => {
+ 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;
+ }
+};
+Signals.addSignalMethods(ShowAppsIconWrapper.prototype);
+
+
+/**
+ * A menu for the showAppsIcon
+ */
+var MyShowAppsIconMenu = class DashToDock_MyShowAppsIconMenu extends MyAppIconMenu {
+ _redisplay() {
+ 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/dash.js b/extensions/dash-to-dock/dash.js
new file mode 100644
index 0000000..a646256
--- /dev/null
+++ b/extensions/dash-to-dock/dash.js
@@ -0,0 +1,1171 @@
+// -*- 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 GObject = imports.gi.GObject;
+const Gtk = imports.gi.Gtk;
+const Signals = imports.signals;
+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
+ */
+var MyDashActor = GObject.registerClass(
+class DashToDock_MyDashActor extends St.Widget {
+
+ _init(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
+ });
+
+ super._init({
+ name: 'dash',
+ layout_manager: layout,
+ clip_to_allocation: true
+ });
+
+ // Since we are usually visible but not usually changing, make sure
+ // most repaint requests don't actually require us to repaint anything.
+ // This saves significant CPU when repainting the screen.
+ this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
+ }
+
+ vfunc_allocate(box, flags) {
+ this.set_allocation(box, flags);
+ let contentBox = box;
+ let availWidth = contentBox.x2 - contentBox.x1;
+ let availHeight = contentBox.y2 - contentBox.y1;
+
+ let [appIcons, showAppsButton] = this.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);
+ }
+ }
+
+ vfunc_get_preferred_width(forHeight) {
+ // 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.layout_manager.get_preferred_width(this, forHeight);
+
+ let themeNode = this.get_theme_node();
+ let [, showAppsButton] = this.get_children();
+ let [minWidth, ] = showAppsButton.get_preferred_height(forHeight);
+
+ return [minWidth, natWidth];
+ }
+
+ vfunc_get_preferred_height(forWidth) {
+ // 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.layout_manager.get_preferred_height(this, forWidth);
+
+ let themeNode = this.get_theme_node();
+ let [, showAppsButton] = this.get_children();
+ let [minHeight, ] = showAppsButton.get_preferred_height(forWidth);
+
+ return [minHeight, 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.
+ */
+var MyDash = class DashToDock_MyDash {
+
+ constructor(settings, remoteModel, monitorIndex) {
+ this._dtdSettings = settings;
+
+ // Initialize icon variables and size
+ this._maxHeight = -1;
+ this.iconSize = this._dtdSettings.get_int('dash-max-icon-size');
+ this._availableIconSizes = baseIconSizes;
+ this._shownInitially = false;
+ this._initializeIconSize(this.iconSize);
+
+ this._remoteModel = remoteModel;
+ 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._container = new MyDashActor(settings);
+ 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', this._onScrollEvent.bind(this));
+
+ 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);
+
+ // Create a wrapper around the real showAppsIcon in order to add a popupMenu.
+ let showAppsIconWrapper = new AppIcons.ShowAppsIconWrapper(this._dtdSettings);
+ showAppsIconWrapper.connect('menu-state-changed', (showAppsIconWrapper, opened) => {
+ this._itemMenuStateChanged(showAppsIconWrapper, opened);
+ });
+ // an instance of the showAppsIcon class is encapsulated in the wrapper
+ this._showAppsIcon = showAppsIconWrapper.realShowAppsIcon;
+
+ this._showAppsIcon.childScale = 1;
+ this._showAppsIcon.childOpacity = 255;
+ this._showAppsIcon.icon.setIconSize(this.iconSize);
+ this._hookUpLabel(this._showAppsIcon);
+
+ 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', () => {
+ if (this._maxHeight != this.actor.width)
+ this._queueRedisplay();
+ this._maxHeight = this.actor.width;
+ });
+ }
+ else {
+ this.actor.connect('notify::height', () => {
+ 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', this._updateAppsIconGeometry.bind(this));
+ let scrollViewAdjustment = this._isHorizontal ? this._scrollView.hscroll.adjustment : this._scrollView.vscroll.adjustment;
+ scrollViewAdjustment.connect('notify::value', this._updateAppsIconGeometry.bind(this));
+
+ this._workId = Main.initializeDeferredWork(this._box, this._redisplay.bind(this));
+
+ this._settings = new Gio.Settings({
+ schema_id: 'org.gnome.shell'
+ });
+
+ this._appSystem = Shell.AppSystem.get_default();
+
+ this._signalsHandler.add([
+ this._appSystem,
+ 'installed-changed',
+ () => {
+ AppFavorites.getAppFavorites().reload();
+ this._queueRedisplay();
+ }
+ ], [
+ AppFavorites.getAppFavorites(),
+ 'changed',
+ this._queueRedisplay.bind(this)
+ ], [
+ this._appSystem,
+ 'app-state-changed',
+ this._queueRedisplay.bind(this)
+ ], [
+ Main.overview,
+ 'item-drag-begin',
+ this._onDragBegin.bind(this)
+ ], [
+ Main.overview,
+ 'item-drag-end',
+ this._onDragEnd.bind(this)
+ ], [
+ Main.overview,
+ 'item-drag-cancelled',
+ this._onDragCancelled.bind(this)
+ ]);
+ }
+
+ destroy() {
+ this._signalsHandler.destroy();
+ }
+
+ _onScrollEvent(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;
+
+ // 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() {
+ this._dragCancelled = false;
+ this._dragMonitor = {
+ dragMotion: this._onDragMotion.bind(this)
+ };
+ 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() {
+ this._dragCancelled = true;
+ this._endDrag();
+ }
+
+ _onDragEnd() {
+ if (this._dragCancelled)
+ return;
+
+ this._endDrag();
+ }
+
+ _endDrag() {
+ this._clearDragPlaceholder();
+ this._clearEmptyDropTarget();
+ this._showAppsIcon.setDragApp(null);
+ DND.removeDragMonitor(this._dragMonitor);
+ }
+
+ _onDragMotion(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(apps) {
+ let ids = {};
+ for (let i = 0; i < apps.length; i++)
+ ids[apps[i].get_id()] = apps[i];
+ return ids;
+ }
+
+ _queueRedisplay() {
+ Main.queueDeferredWork(this._workId);
+ }
+
+ _hookUpLabel(item, appIcon) {
+ item.child.connect('notify::hover', () => {
+ this._syncLabel(item, appIcon);
+ });
+
+ let id = Main.overview.connect('hiding', () => {
+ this._labelShowing = false;
+ item.hideLabel();
+ });
+ item.child.connect('destroy', function() {
+ Main.overview.disconnect(id);
+ });
+
+ if (appIcon) {
+ appIcon.connect('sync-tooltip', () => {
+ this._syncLabel(item, appIcon);
+ });
+ }
+ }
+
+ _createAppItem(app) {
+ let appIcon = new AppIcons.MyAppIcon(this._dtdSettings, this._remoteModel, app, this._monitorIndex,
+ { setSizeManually: true,
+ showLabel: false });
+
+ if (appIcon._draggable) {
+ appIcon._draggable.connect('drag-begin', () => {
+ appIcon.actor.opacity = 50;
+ });
+ appIcon._draggable.connect('drag-end', () => {
+ appIcon.actor.opacity = 255;
+ });
+ }
+
+ appIcon.connect('menu-state-changed', (appIcon, opened) => {
+ this._itemMenuStateChanged(item, opened);
+ });
+
+ let item = new Dash.DashItemContainer();
+
+ extendDashItemContainer(item, this._dtdSettings);
+ item.setChild(appIcon.actor);
+
+ appIcon.actor.connect('notify::hover', () => {
+ if (appIcon.actor.hover) {
+ this._ensureAppIconVisibilityTimeoutId = Mainloop.timeout_add(100, () => {
+ 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', (actor) => {
+ ensureActorVisibleInScrollView(this._scrollView, actor);
+ });
+
+ appIcon.actor.connect('key-focus-in', (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() {
+ // 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() {
+ let appIcons = this.getAppIcons();
+ appIcons.forEach(function(icon) {
+ icon.updateIconGeometry();
+ });
+ }
+
+ _itemMenuStateChanged(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(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, () => {
+ 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, () => {
+ this._labelShowing = false;
+ this._resetHoverTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._resetHoverTimeoutId, '[gnome-shell] this._labelShowing');
+ }
+ }
+ }
+
+ _adjustIconSize() {
+ // 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() {
+ 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() {
+ 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(activate) {
+ let appIcons = this.getAppIcons();
+ appIcons.forEach(function(icon) {
+ icon.toggleNumberOverlay(activate);
+ });
+ }
+
+ _initializeIconSize(max_size) {
+ 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<max_size);
+ });
+ this._availableIconSizes.push(max_size);
+ }
+ }
+
+ setIconSize(max_size, doNotAnimate) {
+ this._initializeIconSize(max_size);
+
+ if (doNotAnimate)
+ this._shownInitially = false;
+
+ this._queueRedisplay();
+ }
+
+ /**
+ * Reset the displayed apps icon to mantain the correct order when changing
+ * show favorites/show running settings
+ */
+ resetAppIcons() {
+ let children = this._box.get_children().filter(function(actor) {
+ return actor.child &&
+ actor.child._delegate &&
+ actor.child._delegate.icon;
+ });
+ for (let i = 0; i < children.length; i++) {
+ let item = children[i];
+ item.destroy();
+ }
+
+ // to avoid ugly animations, just suppress them like when dash is first loaded.
+ this._shownInitially = false;
+ this._redisplay();
+
+ }
+
+ _clearDragPlaceholder() {
+ if (this._dragPlaceholder) {
+ this._animatingPlaceholdersCount++;
+ this._dragPlaceholder.animateOutAndDestroy();
+ this._dragPlaceholder.connect('destroy', () => {
+ this._animatingPlaceholdersCount--;
+ });
+ this._dragPlaceholder = null;
+ }
+ this._dragPlaceholderPos = -1;
+ }
+
+ _clearEmptyDropTarget() {
+ if (this._emptyDropTarget) {
+ this._emptyDropTarget.animateOutAndDestroy();
+ this._emptyDropTarget = null;
+ }
+ }
+
+ handleDragOver(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 DND.DragMotionResult.NO_DROP;
+
+ if (!this._settings.is_writable('favorite-apps') || !this._dtdSettings.get_boolean('show-favorites'))
+ return DND.DragMotionResult.NO_DROP;
+
+ let favorites = AppFavorites.getAppFavorites().getFavorites();
+ let numFavorites = favorites.length;
+
+ let favPos = favorites.indexOf(app);
+
+ let children = this._box.get_children();
+ let numChildren = children.length;
+ let boxHeight = 0;
+ for (let i = 0; i < numChildren; i++)
+ boxHeight += this._isHorizontal?children[i].width:children[i].height;
+
+ // Keep the placeholder out of the index calculation; assuming that
+ // the remove target has the same size as "normal" items, we don't
+ // need to do the same adjustment there.
+ if (this._dragPlaceholder) {
+ boxHeight -= this._isHorizontal?this._dragPlaceholder.width:this._dragPlaceholder.height;
+ numChildren--;
+ }
+
+ let pos;
+ if (!this._emptyDropTarget) {
+ pos = Math.floor((this._isHorizontal?x:y) * numChildren / boxHeight);
+ if (pos > 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(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, () => {
+ let appFavorites = AppFavorites.getAppFavorites();
+ if (srcIsFavorite)
+ appFavorites.moveFavoriteToPos(id, favPos);
+ else
+ appFavorites.addFavoriteAtPos(id, favPos);
+ return false;
+ });
+
+ return true;
+ }
+
+ showShowAppsButton() {
+ this.showAppsButton.visible = true
+ this.showAppsButton.set_width(-1)
+ this.showAppsButton.set_height(-1)
+ }
+
+ hideShowAppsButton() {
+ 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/docking.js b/extensions/dash-to-dock/docking.js
new file mode 100644
index 0000000..d35094b
--- /dev/null
+++ b/extensions/dash-to-dock/docking.js
@@ -0,0 +1,1853 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Clutter = imports.gi.Clutter;
+const GLib = imports.gi.GLib;
+const GObject = imports.gi.GObject;
+const Gtk = imports.gi.Gtk;
+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 ExtensionUtils = imports.misc.extensionUtils;
+const Me = ExtensionUtils.getCurrentExtension();
+const Utils = Me.imports.utils;
+const Intellihide = Me.imports.intellihide;
+const Theming = Me.imports.theming;
+const MyDash = Me.imports.dash;
+const LauncherAPI = Me.imports.launcherAPI;
+
+const DOCK_DWELL_CHECK_INTERVAL = 100;
+
+var 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.
+*/
+var DashSlideContainer = GObject.registerClass(
+class DashToDock_DashSlideContainer extends St.Widget {
+
+ _init(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];
+ }
+ }
+
+ super._init(params);
+ 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
+ }
+
+ vfunc_allocate(box, flags) {
+ this.set_allocation(box, flags);
+
+ if (this._child == null)
+ return;
+
+ let availWidth = box.x2 - box.x1;
+ let availHeight = box.y2 - box.y1;
+ let [, , 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
+ */
+ vfunc_get_preferred_width(forHeight) {
+ 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;
+ }
+ return [minWidth, natWidth];
+ }
+
+ /**
+ * Just the child height but taking into account the slided out part
+ */
+ vfunc_get_preferred_height(forWidth) {
+ 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;
+ }
+ return [minHeight, natHeight];
+ }
+
+ /**
+ * I was expecting it to be a virtual function... stil I don't understand
+ * how things work.
+ */
+ add_child(actor) {
+ // I'm supposed to have only on child
+ if (this._child !== null)
+ this.remove_child(actor);
+
+ this._child = actor;
+ super.add_child(actor);
+ }
+
+ set slidex(value) {
+ this._slidex = value;
+ this._child.queue_relayout();
+ }
+
+ get slidex() {
+ return this._slidex;
+ }
+});
+
+var DockedDash = class DashToDock {
+
+ constructor(settings, remoteModel, monitorIndex) {
+ this._rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
+
+ // Load settings
+ this._settings = settings;
+ this._remoteModel = remoteModel;
+ 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._remoteModel, 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', this._hoverChanged.bind(this));
+
+ // 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',
+ this._onDragStart.bind(this)
+ ], [
+ Main.overview,
+ 'item-drag-end',
+ this._onDragEnd.bind(this)
+ ], [
+ Main.overview,
+ 'item-drag-cancelled',
+ this._onDragEnd.bind(this)
+ ], [
+ // update when workarea changes, for instance if other extensions modify the struts
+ //(like moving th panel at the bottom)
+ global.display,
+ 'workareas-changed',
+ this._resetPosition.bind(this)
+ ], [
+ Main.overview,
+ 'showing',
+ this._onOverviewShowing.bind(this)
+ ], [
+ Main.overview,
+ 'hiding',
+ this._onOverviewHiding.bind(this)
+ ], [
+ // Hide on appview
+ Main.overview.viewSelector,
+ 'page-changed',
+ this._pageChanged.bind(this)
+ ], [
+ Main.overview.viewSelector,
+ 'page-empty',
+ this._onPageEmpty.bind(this)
+ ], [
+ // Ensure the ShowAppsButton status is kept in sync
+ Main.overview.viewSelector._showAppsButton,
+ 'notify::checked',
+ this._syncShowAppsButtonToggled.bind(this)
+ ], [
+ global.display,
+ 'in-fullscreen-changed',
+ this._updateBarrier.bind(this)
+ ], [
+ // Monitor windows overlapping
+ this._intellihide,
+ 'status-changed',
+ this._updateDashVisibility.bind(this)
+ ], [
+ // Keep dragged icon consistent in size with this dash
+ this.dash,
+ 'icon-size-changed',
+ () => { 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',
+ () => { Main.overview.dashIconSize = this.dash.iconSize; }
+ ], [
+ // sync hover after a popupmenu is closed
+ this.dash,
+ 'menu-closed',
+ () => { this._box.sync_hover() }
+ ]);
+
+ this._injectionsHandler = new Utils.InjectionsHandler();
+ this._themeManager = new Theming.ThemeManager(this._settings, this);
+
+ // 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',
+ Main.layoutManager._queueUpdateRegions.bind(Main.layoutManager));
+
+ this.dash._container.connect('allocation-changed', this._updateStaticBox.bind(this));
+ this._slider.connect(this._isHorizontal ? 'notify::x' : 'notify::y', this._updateStaticBox.bind(this));
+
+ // 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', this._initialize.bind(this));
+
+ // 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);
+ 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, {affectsStruts: true});
+ }
+ else
+ Main.layoutManager._trackActor(this._slider);
+
+ // Set initial position
+ this._resetDepth();
+ this._resetPosition();
+ }
+
+ _initialize() {
+ if (this._paintId > 0) {
+ this.actor.disconnect(this._paintId);
+ this._paintId=0;
+ }
+
+ // 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() {
+ // 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();
+
+ }
+
+ _bindSettingsChanges() {
+ this._signalsHandler.add([
+ this._settings,
+ 'changed::scroll-action',
+ () => { this._optionalScrollWorkspaceSwitch(); }
+ ], [
+ this._settings,
+ 'changed::dash-max-icon-size',
+ () => { this.dash.setIconSize(this._settings.get_int('dash-max-icon-size')); }
+ ], [
+ this._settings,
+ 'changed::icon-size-fixed',
+ () => { this.dash.setIconSize(this._settings.get_int('dash-max-icon-size')); }
+ ], [
+ this._settings,
+ 'changed::show-favorites',
+ () => { this.dash.resetAppIcons(); }
+ ], [
+ this._settings,
+ 'changed::show-running',
+ () => { this.dash.resetAppIcons(); }
+ ], [
+ this._settings,
+ 'changed::show-apps-at-top',
+ () => { this.dash.resetAppIcons(); }
+ ], [
+ this._settings,
+ 'changed::show-show-apps-button',
+ () => {
+ if (this._settings.get_boolean('show-show-apps-button'))
+ this.dash.showShowAppsButton();
+ else
+ this.dash.hideShowAppsButton();
+ }
+ ], [
+ this._settings,
+ 'changed::dock-fixed',
+ () => {
+ 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);
+ Main.layoutManager._trackActor(this._slider, {affectsStruts: true});
+ } else {
+ Main.layoutManager._untrackActor(this.actor);
+ Main.layoutManager._untrackActor(this._slider);
+ Main.layoutManager._trackActor(this._slider);
+ }
+
+ this._resetPosition();
+
+ // Add or remove barrier depending on if dock-fixed
+ this._updateBarrier();
+
+ this._updateVisibilityMode();
+ }
+ ], [
+ this._settings,
+ 'changed::intellihide',
+ this._updateVisibilityMode.bind(this)
+ ], [
+ this._settings,
+ 'changed::intellihide-mode',
+ () => { this._intellihide.forceUpdate(); }
+ ], [
+ this._settings,
+ 'changed::autohide',
+ () => {
+ this._updateVisibilityMode();
+ this._updateBarrier();
+ }
+ ], [
+ this._settings,
+ 'changed::autohide-in-fullscreen',
+ this._updateBarrier.bind(this)
+ ],
+ [
+ this._settings,
+ 'changed::extend-height',
+ this._resetPosition.bind(this)
+ ], [
+ this._settings,
+ 'changed::height-fraction',
+ this._resetPosition.bind(this)
+ ], [
+ this._settings,
+ 'changed::require-pressure-to-show',
+ () => {
+ // Remove pointer watcher
+ if (this._dockWatch) {
+ PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch);
+ this._dockWatch = null;
+ }
+ this._setupDockDwellIfNeeded();
+ this._updateBarrier();
+ }
+ ], [
+ this._settings,
+ 'changed::pressure-threshold',
+ () => {
+ this._updatePressureBarrier();
+ this._updateBarrier();
+ }
+ ]);
+
+ }
+
+ /**
+ * This is call when visibility settings change
+ */
+ _updateVisibilityMode() {
+ 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() {
+ 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() {
+ this._ignoreHover = true;
+ this._intellihide.disable();
+ this._removeAnimations();
+ this._animateIn(this._settings.get_double('animation-time'), 0);
+ }
+
+ _onOverviewHiding() {
+ this._ignoreHover = false;
+ this._intellihide.enable();
+ this._updateDashVisibility();
+ }
+
+ _hoverChanged() {
+ 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();
+ }
+ }
+ }
+
+ getDockState() {
+ return this._dockState;
+ }
+
+ _show() {
+ 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() {
+ // 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(time, delay) {
+ this._dockState = State.SHOWING;
+
+ Tweener.addTween(this._slider, {
+ slidex: 1,
+ time: time,
+ delay: delay,
+ transition: 'easeOutQuad',
+ onComplete: () => {
+ 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, this._removeBarrier.bind(this));
+ }
+ });
+ }
+
+ _animateOut(time, delay) {
+ this._dockState = State.HIDING;
+ Tweener.addTween(this._slider, {
+ slidex: 0,
+ time: time,
+ delay: delay ,
+ transition: 'easeOutQuad',
+ onComplete: () => {
+ this._dockState = State.HIDDEN;
+ // Remove queued barried removal if any
+ if (this._removeBarrierTimeoutId > 0)
+ Mainloop.source_remove(this._removeBarrierTimeoutId);
+ this._updateBarrier();
+ }
+ });
+ }
+
+ /**
+ * Dwelling system based on the GNOME Shell 3.14 messageTray code.
+ */
+ _setupDockDwellIfNeeded() {
+ // 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, this._checkDockDwell.bind(this));
+ this._dockDwelling = false;
+ this._dockDwellUserTime = 0;
+ }
+ }
+
+ _checkDockDwell(x, y) {
+
+ 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);
+
+ 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,
+ this._dockDwellTimeout.bind(this));
+ GLib.Source.set_name_by_id(this._dockDwellTimeoutId, '[dash-to-dock] this._dockDwellTimeout');
+ }
+ this._dockDwelling = true;
+ }
+ else {
+ this._cancelDockDwell();
+ this._dockDwelling = false;
+ }
+ }
+
+ _cancelDockDwell() {
+ if (this._dockDwellTimeoutId != 0) {
+ Mainloop.source_remove(this._dockDwellTimeoutId);
+ this._dockDwellTimeoutId = 0;
+ }
+ }
+
+ _dockDwellTimeout() {
+ 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;
+
+ // 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() {
+ 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;
+ }
+
+ 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', (barrier) => {
+ if (!this._settings.get_boolean('autohide-in-fullscreen') && this._monitor.inFullscreen)
+ return;
+ this._onPressureSensed();
+ });
+ }
+ }
+
+ /**
+ * handler for mouse pressure sensed
+ */
+ _onPressureSensed() {
+ 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, () => {
+ 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();
+ }
+
+ /**
+ * Remove pressure barrier
+ */
+ _removeBarrier() {
+ 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() {
+ // 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;
+ }
+
+ 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() {
+ return (this._monitorIndex == Main.layoutManager.primaryIndex);
+ }
+
+ _resetPosition() {
+ // 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);
+
+ // 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;
+ }
+
+ 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;
+ }
+
+ // Set the dash at the correct depth in z
+ _resetDepth() {
+ // Keep the dash below the modalDialogGroup
+ Main.layoutManager.uiGroup.set_child_below_sibling(this.actor, Main.layoutManager.modalDialogGroup);
+ }
+
+ _updateStaticBox() {
+ this.staticBox.init_rect(
+ this.actor.x + this._slider.x - (this._position == St.Side.RIGHT ? this._box.width : 0),
+ this.actor.y + this._slider.y - (this._position == St.Side.BOTTOM ? this._box.height : 0),
+ this._box.width,
+ this._box.height
+ );
+
+ this._intellihide.updateTargetBox(this.staticBox);
+ }
+
+ _removeAnimations() {
+ Tweener.removeTweens(this._slider);
+ }
+
+ _onDragStart() {
+ // 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() {
+ // 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() {
+ 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() {
+ /* 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() {
+ 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() {
+ 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() {
+ // Restore dash accessibility
+ Main.ctrlAltTabManager.addGroup(
+ this.dash.actor, _('Dash'), 'user-bookmarks-symbolic',
+ {focusCallback: this._onAccessibilityFocus.bind(this)});
+ }
+
+ /**
+ * Switch workspace by scrolling over the dock
+ */
+ _optionalScrollWorkspaceSwitch() {
+ let label = 'optionalScrollWorkspaceSwitch';
+
+ function isEnabled() {
+ return this._settings.get_enum('scroll-action') === scrollAction.SWITCH_WORKSPACE;
+ }
+
+ this._settings.connect('changed::scroll-action', () => {
+ if (isEnabled.bind(this)())
+ enable.bind(this)();
+ else
+ disable.bind(this)();
+ });
+
+ if (isEnabled.bind(this)())
+ enable.bind(this)();
+
+ function enable() {
+ this._signalsHandler.removeWithLabel(label);
+
+ this._signalsHandler.addWithLabel(label, [
+ this._box,
+ 'scroll-event',
+ onScrollEvent.bind(this)
+ ]);
+
+ 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.workspace_manager.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, () => {
+ 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;
+ }
+ }
+
+ _activateApp(appIndex) {
+ let children = this.dash._box.get_children().filter(function(actor) {
+ return actor.child &&
+ actor.child._delegate &&
+ actor.child._delegate.app;
+ });
+
+ // 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);
+ }
+};
+
+Signals.addSignalMethods(DockedDash.prototype);
+
+/*
+ * Handle keybaord shortcuts
+ */
+const DashToDock_KeyboardShortcuts_NUM_HOTKEYS = 10;
+
+var KeyboardShortcuts = class DashToDock_KeyboardShortcuts {
+
+ constructor(settings, allDocks){
+ 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',
+ () => {
+ if (this._settings.get_boolean('hot-keys'))
+ this._enableHotKeys.bind(this)();
+ else
+ this._disableHotKeys.bind(this)();
+ }
+ ]);
+
+ this._optionalNumberOverlay();
+ }
+
+ destroy() {
+ // Remove keybindings
+ this._disableHotKeys();
+ this._disableExtraShortcut();
+ this._signalsHandler.destroy();
+ }
+
+ _enableHotKeys() {
+ if (this._hotKeysEnabled)
+ return;
+
+ // Setup keyboard bindings for dash elements
+ let keys = ['app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-'];
+ keys.forEach( function(key) {
+ for (let i = 0; i < DashToDock_KeyboardShortcuts_NUM_HOTKEYS; i++) {
+ let appNum = i;
+ Main.wm.addKeybinding(key + (i + 1), this._settings,
+ Meta.KeyBindingFlags.NONE,
+ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
+ () => {
+ this._allDocks[0]._activateApp(appNum);
+ this._showOverlay();
+ });
+ }
+ }, this);
+
+ this._hotKeysEnabled = true;
+ }
+
+ _disableHotKeys() {
+ if (!this._hotKeysEnabled)
+ return;
+
+ let keys = ['app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-'];
+ keys.forEach( function(key) {
+ for (let i = 0; i < DashToDock_KeyboardShortcuts_NUM_HOTKEYS; i++)
+ Main.wm.removeKeybinding(key + (i + 1));
+ }, this);
+
+ this._hotKeysEnabled = false;
+ }
+
+ _optionalNumberOverlay() {
+ 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',
+ this._checkHotkeysOptions.bind(this)
+ ], [
+ this._settings,
+ 'changed::hotkeys-overlay',
+ this._checkHotkeysOptions.bind(this)
+ ], [
+ this._settings,
+ 'changed::hotkeys-show-dock',
+ this._checkHotkeysOptions.bind(this)
+ ]);
+ }
+
+ _checkHotkeysOptions() {
+ 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();
+ }
+
+ _enableExtraShortcut() {
+ if (!this._shortcutIsSet) {
+ Main.wm.addKeybinding('shortcut', this._settings,
+ Meta.KeyBindingFlags.NONE,
+ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
+ this._showOverlay.bind(this));
+ this._shortcutIsSet = true;
+ }
+ }
+
+ _disableExtraShortcut() {
+ if (this._shortcutIsSet) {
+ Main.wm.removeKeybinding('shortcut');
+ this._shortcutIsSet = false;
+ }
+ }
+
+ _showOverlay() {
+ 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);
+
+ // 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, () => {
+ 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();
+ }
+ }
+ }
+};
+
+/**
+ * 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.
+ */
+var WorkspaceIsolation = class DashToDock_WorkspaceIsolation {
+
+ constructor(settings, allDocks) {
+
+ this._settings = settings;
+ this._allDocks = allDocks;
+
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+ this._injectionsHandler = new Utils.InjectionsHandler();
+
+ this._signalsHandler.add([
+ this._settings,
+ 'changed::isolate-workspaces',
+ () => {
+ this._allDocks.forEach(function(dock) {
+ dock.dash.resetAppIcons();
+ });
+ if (this._settings.get_boolean('isolate-workspaces') ||
+ this._settings.get_boolean('isolate-monitors'))
+ this._enable.bind(this)();
+ else
+ this._disable.bind(this)();
+ }
+ ],[
+ this._settings,
+ 'changed::isolate-monitors',
+ () => {
+ this._allDocks.forEach(function(dock) {
+ dock.dash.resetAppIcons();
+ });
+ if (this._settings.get_boolean('isolate-workspaces') ||
+ this._settings.get_boolean('isolate-monitors'))
+ this._enable.bind(this)();
+ else
+ this._disable.bind(this)();
+ }
+ ]);
+
+ if (this._settings.get_boolean('isolate-workspaces') ||
+ this._settings.get_boolean('isolate-monitors'))
+ this._enable();
+
+ }
+
+ _enable() {
+
+ // ensure I never double-register/inject
+ // although it should never happen
+ this._disable();
+
+ this._allDocks.forEach(function(dock) {
+ this._signalsHandler.addWithLabel('isolation', [
+ global.display,
+ 'restacked',
+ dock.dash._queueRedisplay.bind(dock.dash)
+ ], [
+ global.window_manager,
+ 'switch-workspace',
+ dock.dash._queueRedisplay.bind(dock.dash)
+ ]);
+
+ // 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.display,
+ 'window-entered-monitor',
+ dock.dash._queueRedisplay.bind(dock.dash)
+ ]);
+
+ }, 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.workspace_manager.get_active_workspace_index();
+ });
+ if (windows.length == 1)
+ if (windows[0].skip_taskbar)
+ return this.open_new_window(-1);
+
+ if (this.is_on_workspace(global.workspace_manager.get_active_workspace()))
+ return Main.activateWindow(windows[0]);
+ return this.open_new_window(-1);
+ }
+
+ this._injectionsHandler.addWithLabel('isolation', [
+ Shell.App.prototype,
+ 'activate',
+ IsolatedOverview
+ ]);
+ }
+
+ _disable () {
+ this._signalsHandler.removeWithLabel('isolation');
+ this._injectionsHandler.removeWithLabel('isolation');
+ }
+
+ destroy() {
+ this._signalsHandler.destroy();
+ this._injectionsHandler.destroy();
+ }
+};
+
+
+var DockManager = class DashToDock_DockManager {
+
+ constructor() {
+ this._remoteModel = new LauncherAPI.LauncherEntryRemoteModel();
+ this._settings = ExtensionUtils.getSettings('org.gnome.shell.extensions.dash-to-dock');
+ this._oldDash = Main.overview._dash;
+ /* Array of all the docks created */
+ this._allDocks = [];
+ this._createDocks();
+
+ // status variable: true when the overview is shown through the dash
+ // applications button.
+ this._forcedOverview = false;
+
+ // Connect relevant signals to the toggling function
+ this._bindSettingsChanges();
+ }
+
+ _toggle() {
+ this._deleteDocks();
+ this._createDocks();
+ this.emit('toggled');
+ }
+
+ _bindSettingsChanges() {
+ // Connect relevant signals to the toggling function
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+ this._signalsHandler.add([
+ Meta.MonitorManager.get(),
+ 'monitors-changed',
+ this._toggle.bind(this)
+ ], [
+ this._settings,
+ 'changed::multi-monitor',
+ this._toggle.bind(this)
+ ], [
+ this._settings,
+ 'changed::preferred-monitor',
+ this._toggle.bind(this)
+ ], [
+ this._settings,
+ 'changed::dock-position',
+ this._toggle.bind(this)
+ ], [
+ this._settings,
+ 'changed::extend-height',
+ this._adjustPanelCorners.bind(this)
+ ], [
+ this._settings,
+ 'changed::dock-fixed',
+ this._adjustPanelCorners.bind(this)
+ ]);
+ }
+
+ _createDocks() {
+
+ // If there are no monitors (headless configurations, but it can also happen temporary while disconnecting
+ // and reconnecting monitors), just do nothing. When a monitor will be connected we we'll be notified and
+ // and thus create the docks. This prevents pointing trying to access monitors throughout the code, were we
+ // are assuming that at least the primary monitor is present.
+ if (Main.layoutManager.monitors.length <= 0) {
+ return;
+ }
+
+ 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 ;
+ }
+
+ // First we create the main Dock, to get the extra features to bind to this one
+ let dock = new DockedDash(this._settings, this._remoteModel, this._preferredMonitorIndex);
+ this._mainShowAppsButton = dock.dash.showAppsButton;
+ this._allDocks.push(dock);
+
+ // connect app icon into the view selector
+ dock.dash.showAppsButton.connect('notify::checked', this._onShowAppsButtonToggled.bind(this));
+
+ // Make the necessary changes to Main.overview._dash
+ this._prepareMainDash();
+
+ // Adjust corners if necessary
+ this._adjustPanelCorners();
+
+ 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, this._remoteModel, iMon);
+ this._allDocks.push(dock);
+ // connect app icon into the view selector
+ dock.dash.showAppsButton.connect('notify::checked', this._onShowAppsButtonToggled.bind(this));
+ }
+ }
+
+ // 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);
+ }
+
+ _prepareMainDash() {
+ // 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;
+
+ // set stored icon size to the new dash
+ Main.overview.dashIconSize = this._allDocks[0].dash.iconSize;
+
+ // 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);
+ }
+
+ _deleteDocks() {
+ // Remove extra features
+ this._workspaceIsolation.destroy();
+ this._keyboardShortcuts.destroy();
+
+ // Delete all docks
+ let nDocks = this._allDocks.length;
+ for (let i = nDocks-1; i >= 0; i--) {
+ this._allDocks[i].destroy();
+ this._allDocks.pop();
+ }
+ }
+
+ _restoreDash() {
+ 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;
+
+ Main.overview._dash = this._oldDash;
+ }
+
+ _onShowAppsButtonToggled(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;
+
+ 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;
+ });
+
+ 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', () => {
+ Main.overview.disconnect(overviewShownId);
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ 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;
+ }
+
+ }
+
+ // Finally show the overview
+ selector._showAppsButton.checked = true;
+ Main.overview.show();
+ }
+ else {
+ if (this._forcedOverview) {
+ // force exiting overview if needed
+
+ 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, () => {
+ 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;
+ }
+
+ destroy() {
+ this._signalsHandler.destroy();
+ this._deleteDocks();
+ this._revertPanelCorners();
+ this._restoreDash();
+ this._remoteModel.destroy();
+ }
+
+ /**
+ * Adjust Panel corners
+ */
+ _adjustPanelCorners() {
+ 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();
+ }
+
+ _revertPanelCorners() {
+ 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..f025fae
--- /dev/null
+++ b/extensions/dash-to-dock/extension.js
@@ -0,0 +1,23 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const ExtensionUtils = imports.misc.extensionUtils;
+const Me = ExtensionUtils.getCurrentExtension();
+const Docking = Me.imports.docking;
+
+// We declare this with var so it can be accessed by other extensions in
+// GNOME Shell 3.26+ (mozjs52+).
+var dockManager;
+
+function init() {
+ ExtensionUtils.initTranslations('dashtodock');
+}
+
+function enable() {
+ dockManager = new Docking.DockManager();
+}
+
+function disable() {
+ dockManager.destroy();
+
+ dockManager=null;
+}
diff --git a/extensions/dash-to-dock/intellihide.js b/extensions/dash-to-dock/intellihide.js
new file mode 100644
index 0000000..f102ea3
--- /dev/null
+++ b/extensions/dash-to-dock/intellihide.js
@@ -0,0 +1,321 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const GLib = imports.gi.GLib;
+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 Utils = Me.imports.utils;
+
+// A good compromise between reactivity and efficiency; to be tuned.
+const INTELLIHIDE_CHECK_INTERVAL = 100;
+
+const OverlapStatus = {
+ UNDEFINED: -1,
+ FALSE: 0,
+ TRUE: 1
+};
+
+const IntellihideMode = {
+ ALL_WINDOWS: 0,
+ FOCUS_APPLICATION_WINDOWS: 1,
+ MAXIMIZED_WINDOWS : 2
+};
+
+// 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
+];
+
+/**
+ * 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;
+ */
+var Intellihide = class DashToDock_Intellihide {
+
+ constructor(settings, monitorIndex) {
+ // Load settings
+ this._settings = settings;
+ this._monitorIndex = monitorIndex;
+
+ 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',
+ this._windowCreated.bind(this)
+ ], [
+ // triggered for instance when the window list order changes,
+ // included when the workspace is switched
+ global.display,
+ 'restacked',
+ this._checkOverlap.bind(this)
+ ], [
+ // 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',
+ this._checkOverlap.bind(this)
+ ], [
+ // update wne monitor changes, for instance in multimonitor when monitor are attached
+ Meta.MonitorManager.get(),
+ 'monitors-changed',
+ this._checkOverlap.bind(this)
+ ]);
+ }
+
+ destroy() {
+ // Disconnect global signals
+ this._signalsHandler.destroy();
+
+ // Remove residual windows signals
+ this.disable();
+ }
+
+ enable() {
+ this._isEnabled = true;
+ this._status = OverlapStatus.UNDEFINED;
+ global.get_window_actors().forEach(function(wa) {
+ this._addWindowSignals(wa);
+ }, this);
+ this._doCheckOverlap();
+ }
+
+ disable() {
+ this._isEnabled = false;
+
+ for (let wa of this._trackedWindows.keys()) {
+ this._removeWindowSignals(wa);
+ }
+ this._trackedWindows.clear();
+
+ if (this._checkOverlapTimeoutId > 0) {
+ Mainloop.source_remove(this._checkOverlapTimeoutId);
+ this._checkOverlapTimeoutId = 0;
+ }
+ }
+
+ _windowCreated(display, metaWindow) {
+ this._addWindowSignals(metaWindow.get_compositor_private());
+ }
+
+ _addWindowSignals(wa) {
+ if (!this._handledWindow(wa))
+ return;
+ let signalId = wa.connect('allocation-changed', this._checkOverlap.bind(this));
+ this._trackedWindows.set(wa, signalId);
+ wa.connect('destroy', this._removeWindowSignals.bind(this));
+ }
+
+ _removeWindowSignals(wa) {
+ if (this._trackedWindows.get(wa)) {
+ wa.disconnect(this._trackedWindows.get(wa));
+ this._trackedWindows.delete(wa);
+ }
+
+ }
+
+ updateTargetBox(box) {
+ this._targetBox = box;
+ this._checkOverlap();
+ }
+
+ forceUpdate() {
+ this._status = OverlapStatus.UNDEFINED;
+ this._doCheckOverlap();
+ }
+
+ getOverlapStatus() {
+ return (this._status == OverlapStatus.TRUE);
+ }
+
+ _checkOverlap() {
+ if (!this._isEnabled || (this._targetBox == null))
+ return;
+
+ /* Limit the number of calls to the doCheckOverlap function */
+ if (this._checkOverlapTimeoutId) {
+ this._checkOverlapTimeoutContinue = true;
+ return
+ }
+
+ this._doCheckOverlap();
+
+ this._checkOverlapTimeoutId = Mainloop.timeout_add(INTELLIHIDE_CHECK_INTERVAL, () => {
+ this._doCheckOverlap();
+ if (this._checkOverlapTimeoutContinue) {
+ this._checkOverlapTimeoutContinue = false;
+ return GLib.SOURCE_CONTINUE;
+ } else {
+ this._checkOverlapTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ }
+ });
+ }
+
+ _doCheckOverlap() {
+
+ if (!this._isEnabled || (this._targetBox == null))
+ return;
+
+ 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);
+ }
+
+ }
+
+ // Filter interesting windows to be considered for intellihide.
+ // Consider all windows visible on the current workspace.
+ // Optionally skip windows of other applications
+ _intellihideFilterInteresting(wa) {
+ let meta_win = wa.get_meta_window();
+ if (!this._handledWindow(wa))
+ return false;
+
+ let currentWorkspace = global.workspace_manager.get_active_workspace_index();
+ let wksp = meta_win.get_workspace();
+ let wksp_index = wksp.index();
+
+ // Depending on the intellihide mode, exclude non-relevent windows
+ switch (this._settings.get_enum('intellihide-mode')) {
+ case IntellihideMode.ALL_WINDOWS:
+ // Do nothing
+ break;
+
+ 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
+ return false;
+
+ }
+
+ // Filter windows by type
+ // inspired by Opacify@gnome-shell.localdomain.pl
+ _handledWindow(wa) {
+ let metaWindow = wa.get_meta_window();
+
+ 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/launcherAPI.js b/extensions/dash-to-dock/launcherAPI.js
new file mode 100644
index 0000000..f0b6199
--- /dev/null
+++ b/extensions/dash-to-dock/launcherAPI.js
@@ -0,0 +1,239 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Gio = imports.gi.Gio;
+const Signals = imports.signals;
+
+var LauncherEntryRemoteModel = class DashToDock_LauncherEntryRemoteModel {
+
+ constructor() {
+ this._entriesByDBusName = {};
+
+ this._launcher_entry_dbus_signal_id =
+ Gio.DBus.session.signal_subscribe(null, // sender
+ 'com.canonical.Unity.LauncherEntry', // iface
+ null, // member
+ null, // path
+ null, // arg0
+ Gio.DBusSignalFlags.NONE,
+ this._onEntrySignalReceived.bind(this));
+
+ this._dbus_name_owner_changed_signal_id =
+ Gio.DBus.session.signal_subscribe('org.freedesktop.DBus', // sender
+ 'org.freedesktop.DBus', // interface
+ 'NameOwnerChanged', // member
+ '/org/freedesktop/DBus', // path
+ null, // arg0
+ Gio.DBusSignalFlags.NONE,
+ this._onDBusNameOwnerChanged.bind(this));
+
+ this._acquireUnityDBus();
+ }
+
+ destroy() {
+ if (this._launcher_entry_dbus_signal_id) {
+ Gio.DBus.session.signal_unsubscribe(this._launcher_entry_dbus_signal_id);
+ }
+
+ if (this._dbus_name_owner_changed_signal_id) {
+ Gio.DBus.session.signal_unsubscribe(this._dbus_name_owner_changed_signal_id);
+ }
+
+ this._releaseUnityDBus();
+ }
+
+ size() {
+ return Object.keys(this._entriesByDBusName).length;
+ }
+
+ lookupByDBusName(dbusName) {
+ return this._entriesByDBusName.hasOwnProperty(dbusName) ? this._entriesByDBusName[dbusName] : null;
+ }
+
+ lookupById(appId) {
+ let ret = [];
+ for (let dbusName in this._entriesByDBusName) {
+ let entry = this._entriesByDBusName[dbusName];
+ if (entry && entry.appId() == appId) {
+ ret.push(entry);
+ }
+ }
+
+ return ret;
+ }
+
+ addEntry(entry) {
+ let existingEntry = this.lookupByDBusName(entry.dbusName());
+ if (existingEntry) {
+ existingEntry.update(entry);
+ } else {
+ this._entriesByDBusName[entry.dbusName()] = entry;
+ this.emit('entry-added', entry);
+ }
+ }
+
+ removeEntry(entry) {
+ delete this._entriesByDBusName[entry.dbusName()]
+ this.emit('entry-removed', entry);
+ }
+
+ _acquireUnityDBus() {
+ if (!this._unity_bus_id) {
+ Gio.DBus.session.own_name('com.canonical.Unity',
+ Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null);
+ }
+ }
+
+ _releaseUnityDBus() {
+ if (this._unity_bus_id) {
+ Gio.DBus.session.unown_name(this._unity_bus_id);
+ this._unity_bus_id = 0;
+ }
+ }
+
+ _onEntrySignalReceived(connection, sender_name, object_path,
+ interface_name, signal_name, parameters, user_data) {
+ if (!parameters || !signal_name)
+ return;
+
+ if (signal_name == 'Update') {
+ if (!sender_name) {
+ return;
+ }
+
+ this._handleUpdateRequest(sender_name, parameters);
+ }
+ }
+
+ _onDBusNameOwnerChanged(connection, sender_name, object_path,
+ interface_name, signal_name, parameters, user_data) {
+ if (!parameters || !this.size())
+ return;
+
+ let [name, before, after] = parameters.deep_unpack();
+
+ if (!after) {
+ if (this._entriesByDBusName.hasOwnProperty(before)) {
+ this.removeEntry(this._entriesByDBusName[before]);
+ }
+ }
+ }
+
+ _handleUpdateRequest(senderName, parameters) {
+ if (!senderName || !parameters) {
+ return;
+ }
+
+ let [appUri, properties] = parameters.deep_unpack();
+ let appId = appUri.replace(/(^\w+:|^)\/\//, '');
+ let entry = this.lookupByDBusName(senderName);
+
+ if (entry) {
+ entry.setDBusName(senderName);
+ entry.update(properties);
+ } else {
+ let entry = new LauncherEntryRemote(senderName, appId, properties);
+ this.addEntry(entry);
+ }
+ }
+};
+Signals.addSignalMethods(LauncherEntryRemoteModel.prototype);
+
+var LauncherEntryRemote = class DashToDock_LauncherEntryRemote {
+
+ constructor(dbusName, appId, properties) {
+ this._dbusName = dbusName;
+ this._appId = appId;
+ this._count = 0;
+ this._countVisible = false;
+ this._progress = 0.0;
+ this._progressVisible = false;
+ this.update(properties);
+ }
+
+ appId() {
+ return this._appId;
+ }
+
+ dbusName() {
+ return this._dbusName;
+ }
+
+ count() {
+ return this._count;
+ }
+
+ setCount(count) {
+ if (this._count != count) {
+ this._count = count;
+ this.emit('count-changed', this._count);
+ }
+ }
+
+ countVisible() {
+ return this._countVisible;
+ }
+
+ setCountVisible(countVisible) {
+ if (this._countVisible != countVisible) {
+ this._countVisible = countVisible;
+ this.emit('count-visible-changed', this._countVisible);
+ }
+ }
+
+ progress() {
+ return this._progress;
+ }
+
+ setProgress(progress) {
+ if (this._progress != progress) {
+ this._progress = progress;
+ this.emit('progress-changed', this._progress);
+ }
+ }
+
+ progressVisible() {
+ return this._progressVisible;
+ }
+
+ setProgressVisible(progressVisible) {
+ if (this._progressVisible != progressVisible) {
+ this._progressVisible = progressVisible;
+ this.emit('progress-visible-changed', this._progressVisible);
+ }
+ }
+
+ setDBusName(dbusName) {
+ if (this._dbusName != dbusName) {
+ let oldName = this._dbusName;
+ this._dbusName = dbusName;
+ this.emit('dbus-name-changed', oldName);
+ }
+ }
+
+ update(other) {
+ if (other instanceof LauncherEntryRemote) {
+ this.setDBusName(other.dbusName())
+ this.setCount(other.count());
+ this.setCountVisible(other.countVisible());
+ this.setProgress(other.progress());
+ this.setProgressVisible(other.progressVisible())
+ } else {
+ for (let property in other) {
+ if (other.hasOwnProperty(property)) {
+ if (property == 'count') {
+ this.setCount(other[property].get_int64());
+ } else if (property == 'count-visible') {
+ this.setCountVisible(other[property].get_boolean());
+ } if (property == 'progress') {
+ this.setProgress(other[property].get_double());
+ } else if (property == 'progress-visible') {
+ this.setProgressVisible(other[property].get_boolean());
+ } else {
+ // Not implemented yet
+ }
+ }
+ }
+ }
+ }
+};
+Signals.addSignalMethods(LauncherEntryRemote.prototype);
diff --git a/extensions/dash-to-dock/media/glossy.svg b/extensions/dash-to-dock/media/glossy.svg
new file mode 100644
index 0000000..55b71ba
--- /dev/null
+++ b/extensions/dash-to-dock/media/glossy.svg
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="18.343554mm"
+ height="18.343554mm"
+ viewBox="0 0 14.674843 14.674842"
+ version="1.1"
+ id="svg4941"
+ sodipodi:docname="glossy.svg"
+ inkscape:version="0.92.1 r15371">
+ <defs
+ id="defs4935">
+ <linearGradient
+ id="linearGradient6812"
+ inkscape:collect="always">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop6810" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop6808" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient18962"
+ id="linearGradient35463"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.29132751,0,0,0.15428114,-54.210829,160.22776)"
+ x1="214.71877"
+ y1="404.36081"
+ x2="214.71877"
+ y2="443.54596" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient18962">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop18964" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop18966" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient18806">
+ <stop
+ style="stop-color:#ff0101;stop-opacity:1;"
+ offset="0"
+ id="stop18808" />
+ <stop
+ style="stop-color:#800000;stop-opacity:1;"
+ offset="1"
+ id="stop18810" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient6812"
+ id="radialGradient6798"
+ cx="7.3538475"
+ cy="230.28426"
+ fx="7.3538475"
+ fy="230.28426"
+ r="7.2099228"
+ gradientTransform="matrix(5.9484829,-0.0346444,0.01679088,3.0681664,-40.338609,-476.01412)"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="10.68"
+ inkscape:cx="65.485107"
+ inkscape:cy="29.432163"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="2560"
+ inkscape:window-height="1406"
+ inkscape:window-x="1920"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ scale-x="0.8"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0" />
+ <metadata
+ id="metadata4938">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-222.92515)">
+ <rect
+ inkscape:export-ydpi="180"
+ inkscape:export-xdpi="180"
+ inkscape:export-filename="C:\Arbeit\Blog\Tutorials\glossybutton\Glossy_Button_Tutorial.png"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.35100002;fill:url(#linearGradient35463);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.39747861;marker:none;enable-background:accumulate"
+ id="rect19155"
+ width="14.634871"
+ height="3.7392156"
+ x="0.039808333"
+ y="222.98268"
+ rx="1.5496143"
+ ry="0.82064426" />
+ <rect
+ style="opacity:0.427;fill:url(#radialGradient6798);fill-opacity:1;stroke:#ffffff;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6706"
+ width="14.363673"
+ height="14.404656"
+ x="0.090466507"
+ y="223.07919" />
+ </g>
+</svg>
diff --git a/extensions/dash-to-dock/media/highlight_stacked_bg.svg b/extensions/dash-to-dock/media/highlight_stacked_bg.svg
new file mode 100644
index 0000000..19be5a9
--- /dev/null
+++ b/extensions/dash-to-dock/media/highlight_stacked_bg.svg
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ viewBox="-0.7 0 48 48"
+ version="1.1"
+ id="svg10"
+ sodipodi:docname="highlight_stacked_bg.svg"
+ width="48"
+ height="48"
+ inkscape:version="0.92.1 r15371">
+ <metadata
+ id="metadata16">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs14" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="951"
+ id="namedview12"
+ showgrid="false"
+ viewbox-x="-0.7"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="3.8125"
+ inkscape:cx="-63.872219"
+ inkscape:cy="15.195756"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg10" />
+ <g
+ id="g8"
+ transform="matrix(1,0,0,48,-0.7,0)"
+ style="opacity:0.25;fill:#eeeeee;stroke-width:0.14433756">
+ <rect
+ width="45"
+ height="1"
+ id="rect2"
+ x="0"
+ y="0"
+ style="stroke-width:0.14433756" />
+ <rect
+ x="45"
+ width="1"
+ height="1"
+ id="rect4"
+ y="0"
+ style="opacity:0.2;stroke-width:0.02083333" />
+ <rect
+ x="46"
+ width="2"
+ height="1"
+ id="rect6"
+ y="0"
+ style="opacity:0.6;stroke-width:0.02083333" />
+ </g>
+</svg>
diff --git a/extensions/dash-to-dock/media/highlight_stacked_bg_h.svg b/extensions/dash-to-dock/media/highlight_stacked_bg_h.svg
new file mode 100644
index 0000000..eeaa869
--- /dev/null
+++ b/extensions/dash-to-dock/media/highlight_stacked_bg_h.svg
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ viewBox="-0.7 0 48 48"
+ version="1.1"
+ id="svg10"
+ sodipodi:docname="highlight_stacked_bg_h.svg"
+ width="48"
+ height="48"
+ inkscape:version="0.92.1 r15371">
+ <metadata
+ id="metadata16">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs14" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1853"
+ inkscape:window-height="1016"
+ id="namedview12"
+ showgrid="false"
+ viewbox-x="-0.7"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="3.8125"
+ inkscape:cx="-63.872219"
+ inkscape:cy="15.195756"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg10" />
+ <g
+ id="g8"
+ transform="matrix(0,-1,-48,0,47.3,48)"
+ style="opacity:0.25;fill:#eeeeee;stroke-width:0.14433756">
+ <rect
+ width="45"
+ height="1"
+ id="rect2"
+ x="0"
+ y="0"
+ style="stroke-width:0.14433756" />
+ <rect
+ x="45"
+ width="1"
+ height="1"
+ id="rect4"
+ y="0"
+ style="opacity:0.2;stroke-width:0.02083333" />
+ <rect
+ x="46"
+ width="2"
+ height="1"
+ id="rect6"
+ y="0"
+ style="opacity:0.6;stroke-width:0.02083333" />
+ </g>
+</svg>
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 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="33.866665mm"
+ height="33.866684mm"
+ viewBox="0 0 33.866665 33.866683"
+ id="svg5179"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="logo.svg">
+ <defs
+ id="defs5181">
+ <clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath4379-92-4-9-6-8-0">
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.83189655;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4381-17-7-5-2-0-6"
+ width="19.934219"
+ height="33.52573"
+ x="356.02826"
+ y="457.71631" />
+ </clipPath>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:collect="always"
+ id="filter4435-8-5-3-2-13-8"
+ x="-0.22881356"
+ width="1.4576271"
+ y="-0.22881356"
+ height="1.4576271">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.0352993"
+ id="feGaussianBlur4437-6-7-9-8-8-1" />
+ </filter>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:collect="always"
+ id="filter4365-71-5-7-0-6-2"
+ x="-0.21864407"
+ width="1.437288"
+ y="-0.21864407"
+ height="1.437288">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.98928601"
+ id="feGaussianBlur4367-74-5-92-0-6-5" />
+ </filter>
+ <clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath4379-6-7-5-8-6-01-2">
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.83189655;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4381-1-8-5-2-0-2-7"
+ width="19.934219"
+ height="33.52573"
+ x="356.02826"
+ y="457.71631" />
+ </clipPath>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:collect="always"
+ id="filter4435-6-1-2-8-2-2-7"
+ x="-0.22881356"
+ width="1.4576271"
+ y="-0.22881356"
+ height="1.4576271">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.0352993"
+ id="feGaussianBlur4437-1-1-3-60-1-4-4" />
+ </filter>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:collect="always"
+ id="filter4365-4-5-2-24-7-3-3"
+ x="-0.21864407"
+ width="1.437288"
+ y="-0.21864407"
+ height="1.437288">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.98928601"
+ id="feGaussianBlur4367-7-0-7-7-9-0-3" />
+ </filter>
+ <clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath4379-5-6-0-9-8-7-9">
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.83189655;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4381-6-8-5-9-9-2-4"
+ width="19.934219"
+ height="33.52573"
+ x="356.02826"
+ y="457.71631" />
+ </clipPath>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:collect="always"
+ id="filter4435-63-9-2-4-1-2-6"
+ x="-0.22881356"
+ width="1.4576271"
+ y="-0.22881356"
+ height="1.4576271">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.0352993"
+ id="feGaussianBlur4437-0-5-6-8-8-9-9" />
+ </filter>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:collect="always"
+ id="filter4365-2-4-3-6-3-1-7"
+ x="-0.21864407"
+ width="1.437288"
+ y="-0.21864407"
+ height="1.437288">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.98928601"
+ id="feGaussianBlur4367-1-2-5-3-5-8-3" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ style="color-interpolation-filters:sRGB"
+ id="filter4255"
+ x="-0.20374454"
+ width="1.4074891"
+ y="-0.13779147"
+ height="1.2755829">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.25863247"
+ id="feGaussianBlur4257" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="8"
+ inkscape:cx="60.090739"
+ inkscape:cy="60.108985"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1861"
+ inkscape:window-height="1023"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata5184">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(136.97858,-11.552354)">
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#0055d4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4006-4-6-9-2-0-6"
+ width="33.83363"
+ height="33.859909"
+ x="-136.9473"
+ y="11.552354"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.15440008;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ d="m -130.12265,11.559157 c -4.30029,5.691881 -6.67207,12.608761 -6.82289,19.674442 -0.0115,0.54232 -0.0147,1.0766 0,1.62024 0.11433,4.23572 1.04846,8.50668 2.82497,12.565201 l 31.00865,0 0,-33.859883 -27.01073,0 z"
+ id="path6097-2-6-0-89-4"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
+ d="m -136.9473,18.430158 0,0.7896 0,20.641361 0,0.7896 1.23782,0 2.26288,0 1.60528,0 c 0.68577,0 1.23783,-0.3548 1.23783,-0.7896 l 0,-20.641361 c 0,-0.4398 -0.55206,-0.7896 -1.23783,-0.7896 l -1.60528,0 -2.26288,0 z"
+ id="rect4008-7-9-2-0-3-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccssssccc"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.15;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.15440008;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ d="m -119.36792,11.559157 c -10.47023,5.721881 -17.57762,16.847401 -17.57762,29.627402 0,1.43804 0.0897,2.841801 0.26432,4.232481 l 33.5693,0 0,-33.859883 -16.256,0 z"
+ id="path6097-4-5-23-9"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4247-4-4-5-3-8-1"
+ width="33.83363"
+ height="2.1162443"
+ x="-136.9473"
+ y="11.552354"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ d="m -103.11365,13.668597 0,1.05812 c 0,-0.58196 -0.47338,-1.05812 -1.05731,-1.05812 l 1.05731,0 z"
+ id="rect4272-0-7-8-1-1-3-3-1"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4031-9-9-2-4-2-5"
+ width="4.2292037"
+ height="4.2324886"
+ x="-135.89"
+ y="19.488146"
+ rx="1.0583334"
+ ry="1.0583334"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ d="m -136.94728,13.668597 0,1.05812 c 0,-0.58196 0.47337,-1.05812 1.0573,-1.05812 l -1.0573,0 z"
+ id="rect4272-0-2-1-74-41-1-6"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <g
+ id="g4353-9-2-1-5-5-4"
+ transform="matrix(0.10331261,0,0,0.10339285,-173.76079,-27.453246)"
+ clip-path="url(#clipPath4379-92-4-9-6-8-0)"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099">
+ <circle
+ r="5.4295697"
+ cy="477.71164"
+ cx="274.13016"
+ transform="matrix(0.94749688,0,0,0.94749688,96.290796,21.848877)"
+ id="path3153-1-7-3-5-60-3-6"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.42241378;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4435-8-5-3-2-13-8);enable-background:accumulate" />
+ <circle
+ r="5.4295697"
+ cy="477.71164"
+ cx="274.13016"
+ transform="matrix(0.24231546,0,0,0.24231546,289.60229,358.72226)"
+ id="path3153-2-4-1-6-6-9-4-1"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4365-71-5-7-0-6-2);enable-background:accumulate" />
+ </g>
+ <g
+ id="g4589-4-1-1-3-6-2"
+ transform="matrix(0.49926208,0,0,0.49964988,-318.21072,-206.05794)"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099">
+ <g
+ clip-path="url(#clipPath4379-6-7-5-8-6-01-2)"
+ transform="matrix(0.20693061,0,0,0.20693061,289.32686,368.5622)"
+ id="g4353-66-1-4-2-6-94-5">
+ <circle
+ r="5.4295697"
+ cy="477.71164"
+ cx="274.13016"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.42241378;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4435-6-1-2-8-2-2-7);enable-background:accumulate"
+ id="path3153-1-6-4-5-63-7-1-0"
+ transform="matrix(0.94749688,0,0,0.94749688,96.290796,21.848877)" />
+ <circle
+ r="5.4295697"
+ cy="477.71164"
+ cx="274.13016"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4365-4-5-2-24-7-3-3);enable-background:accumulate"
+ id="path3153-2-4-7-6-5-8-5-9-5"
+ transform="matrix(0.24231546,0,0,0.24231546,289.60229,358.72226)" />
+ </g>
+ <g
+ clip-path="url(#clipPath4379-5-6-0-9-8-7-9)"
+ transform="matrix(0.20693061,0,0,0.20693061,289.32686,367.53449)"
+ id="g4353-7-2-2-6-4-5-1">
+ <circle
+ r="5.4295697"
+ cy="477.71164"
+ cx="274.13016"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.42241378;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4435-63-9-2-4-1-2-6);enable-background:accumulate"
+ id="path3153-1-19-3-1-5-5-7-8"
+ transform="matrix(0.94749688,0,0,0.94749688,96.290796,21.848877)" />
+ <circle
+ r="5.4295697"
+ cy="477.71164"
+ cx="274.13016"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4365-2-4-3-6-3-1-7);enable-background:accumulate"
+ id="path3153-2-4-5-7-9-9-9-7-6"
+ transform="matrix(0.24231546,0,0,0.24231546,289.60229,358.72226)" />
+ </g>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:1.28805089px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none"
+ x="-124.44726"
+ y="13.10139"
+ id="text4824-5-2-0-4-8"
+ sodipodi:linespacing="125%"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099"
+ transform="scale(0.99961185,1.0003883)"><tspan
+ sodipodi:role="line"
+ id="tspan4826-16-3-8-8-1"
+ x="-124.44726"
+ y="13.10139">Dash to Dock</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:1.28805089px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none"
+ x="-136.50272"
+ y="13.10139"
+ id="text4824-8-8-6-8-7-4"
+ sodipodi:linespacing="125%"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099"
+ transform="scale(0.99961185,1.0003883)"><tspan
+ sodipodi:role="line"
+ id="tspan4826-1-7-7-5-07-5"
+ x="-136.50272"
+ y="13.10139">Michele</tspan></text>
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4031-9-0-8-5-4-0-7-6"
+ width="4.2292037"
+ height="4.2324886"
+ x="-135.89"
+ y="24.778917"
+ rx="1.0583334"
+ ry="1.0583334"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4031-9-0-7-3-3-6-0-1"
+ width="4.2292037"
+ height="4.2324886"
+ x="-135.89"
+ y="30.069445"
+ rx="1.0583334"
+ ry="1.0583334"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4031-9-0-6-5-1-3-9-0"
+ width="4.2292037"
+ height="4.2324886"
+ x="-135.89"
+ y="35.359974"
+ rx="1.0583334"
+ ry="1.0583334"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.5;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
+ d="m -136.9473,17.901078 0,0.52908 2.42849,0 2.21372,0 c 0.94338,0 1.7016,0.3372 1.7016,0.77704 l 0,20.649921 c 0,0.43476 -0.75822,0.7936 -1.7016,0.7936 l -2.21372,0 -2.42849,0 0,0.52904 0.90862,0 2.64325,0 1.88332,0 c 0.80005,0 1.43727,-0.3712 1.43727,-0.82664 l 0,-21.625361 c 0,-0.46072 -0.63722,-0.82668 -1.43727,-0.82668 l -1.88332,0 -2.64325,0 z"
+ id="rect4008-7-0-0-3-3-3-7-9"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccsssscccccssssccc"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
+ d="m -136.9473,17.901078 0,0.52908 2.42849,0 2.21372,0 c 0.94338,0 1.7016,0.3372 1.7016,0.77704 l 0,20.649921 c 0,0.43476 -0.75822,0.7936 -1.7016,0.7936 l -2.21372,0 -2.42849,0 0,0.52904 0.90862,0 2.64325,0 1.88332,0 c 0.80005,0 1.43727,-0.3712 1.43727,-0.82664 l 0,-21.625361 c 0,-0.46072 -0.63722,-0.82668 -1.43727,-0.82668 l -1.88332,0 -2.64325,0 z"
+ id="rect4008-7-0-0-3-1-5-0-5-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccsssscccccssssccc"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#f2f2f2;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ id="rect6777-7-9-6-9-8"
+ width="20.108335"
+ height="18.256252"
+ x="-125.24149"
+ y="19.139757"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.13229166;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="rect4923-8-7-8-2"
+ width="3.7041669"
+ height="3.7041669"
+ x="-116.71888"
+ y="30.163927"
+ rx="1.0583334"
+ ry="1.0583334" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.15;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ d="m -111.94623,19.146638 c -5.49508,1.3884 -10.21465,5.00036 -13.29531,9.92188 l 0,8.334361 20.10833,0 0,-18.256241 -6.81302,0 z"
+ id="path6862-84-2-2-6-7"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ d="m -125.02657,18.882038 c -0.11728,0 -0.21496,0.0812 -0.21496,0.1984 l 0,0.44648 0,1.2568 0,0.2148 0.21496,0 19.67838,0 0.215,0 0,-0.2148 0,-1.2568 0,-0.44648 c 0,-0.1172 -0.0977,-0.1984 -0.215,-0.1984 l -19.67838,0 z"
+ id="rect6779-5-8-6-4-6"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#999999;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ id="rect6779-2-3-9-9-0-8"
+ width="20.108335"
+ height="0.5291667"
+ x="-125.24149"
+ y="20.991808"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ id="rect6779-2-4-8-0-7-1-1"
+ width="15.875001"
+ height="0.5291667"
+ x="21.521105"
+ y="105.13315"
+ transform="rotate(90)"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45" />
+ <g
+ id="g6839-1-5-1-33-0"
+ transform="matrix(0.02002288,0.02002284,-0.02002288,0.02002284,-106.62848,-6.0229242)"
+ style="fill:#1a1a1a"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45">
+ <rect
+ y="616.07727"
+ x="653.01312"
+ height="41.542522"
+ width="11.313708"
+ id="rect6819-8-9-2-56-9"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate" />
+ <rect
+ transform="rotate(90)"
+ y="-679.44122"
+ x="631.19165"
+ height="41.542522"
+ width="11.313708"
+ id="rect6819-3-9-4-3-1-5"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate" />
+ </g>
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.13229166;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="rect4923-6-8-9-1"
+ width="3.7041669"
+ height="3.7041669"
+ x="-123.59805"
+ y="30.163927"
+ rx="1.0583334"
+ ry="1.0583334" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.26458335;stroke-miterlimit:4;stroke-dasharray:none;marker:none;enable-background:accumulate;opacity:0.866;filter:url(#filter4255)"
+ d="m -121.46776,32.043964 -5e-4,1.742839 -4.9e-4,1.742839 0.71518,-0.708051 0.99716,1.727136 1.33421,-0.770304 -0.99542,-1.724104 0.96903,-0.268366 -1.50959,-0.870995 z"
+ id="path6155-6-0-01-4-5-6-0-0"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccc" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.13229167;stroke-miterlimit:4;stroke-dasharray:none;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ d="m -121.86464,32.043964 -5e-4,1.742839 -4.9e-4,1.742839 0.71518,-0.708051 1.05563,1.8284 1.3342,-0.770304 -1.05388,-1.825368 0.96903,-0.268366 -1.50959,-0.870995 z"
+ id="path6155-6-0-8-0-7-97-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccc" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.13229166;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="rect4923-4-8-4"
+ width="3.7041669"
+ height="3.7041669"
+ x="-123.59805"
+ y="23.020128"
+ rx="1.0583334"
+ ry="1.0583334" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.13229166;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="rect4923-2-6-7-8"
+ width="3.7041669"
+ height="3.7041669"
+ x="-116.71888"
+ y="23.020128"
+ rx="1.0583334"
+ ry="1.0583334" />
+ </g>
+</svg>
diff --git a/extensions/dash-to-dock/meson.build b/extensions/dash-to-dock/meson.build
new file mode 100644
index 0000000..290374f
--- /dev/null
+++ b/extensions/dash-to-dock/meson.build
@@ -0,0 +1,23 @@
+extension_data += configure_file(
+ input: metadata_name + '.in',
+ output: metadata_name,
+ configuration: metadata_conf
+)
+
+extension_sources += files(
+ 'appIconIndicators.js',
+ 'appIcons.js',
+ 'dash.js',
+ 'docking.js',
+ 'extension.js',
+ 'intellihide.js',
+ 'launcherAPI.js',
+ 'prefs.js',
+ 'Settings.ui',
+ 'theming.js',
+ 'utils.js',
+ 'windowPreview.js'
+)
+extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
+
+install_data(['media/logo.svg', 'media/glossy.svg'], install_dir: join_paths(extensiondir, uuid, 'media'))
diff --git a/extensions/dash-to-dock/metadata.json.in b/extensions/dash-to-dock/metadata.json.in
new file mode 100644
index 0000000..641a935
--- /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": 66,
+"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..9cf371b
--- /dev/null
+++ b/extensions/dash-to-dock/org.gnome.shell.extensions.dash-to-dock.gschema.xml
@@ -0,0 +1,540 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist gettext-domain="gnome-shell-extensions">
+ <enum id='org.gnome.shell.extensions.dash-to-dock.clickAction'>
+ <value value='0' nick='skip'/>
+ <value value='1' nick='minimize'/>
+ <value value='2' nick='launch'/>
+ <value value='3' nick='cycle-windows'/>
+ <value value='4' nick='minimize-or-overview'/>
+ <value value='5' nick='previews'/>
+ <value value='6' nick='minimize-or-previews'/>
+ <value value='7' nick='focus-or-previews'/>
+ <value value='8' nick='quit'/>
+ </enum>
+ <enum id='org.gnome.shell.extensions.dash-to-dock.scrollAction'>
+ <value value='0' nick='do-nothing'/>
+ <value value='1' nick='cycle-windows'/>
+ <value value='2' nick='switch-workspace'/>
+ </enum>
+ <!-- this is mean to Match StSide. LEFT and RIGHT actual position in reversed in
+ rtl languages -->
+ <enum id='org.gnome.shell.extensions.dash-to-dock.position'>
+ <value value='0' nick='TOP'/>
+ <value value='1' nick='RIGHT'/>
+ <value value='2' nick='BOTTOM'/>
+ <value value='3' nick='LEFT'/>
+ </enum>
+ <enum id='org.gnome.shell.extensions.dash-to-dock.intellihide-mode'>
+ <value value='0' nick='ALL_WINDOWS'/>
+ <value value='1' nick='FOCUS_APPLICATION_WINDOWS'/>
+ <value value='2' nick='MAXIMIZED_WINDOWS'/>
+ </enum>
+ <enum id='org.gnome.shell.extensions.dash-to-dock.transparency-mode'>
+ <value value='0' nick='DEFAULT'/>
+ <value value='1' nick='FIXED'/>
+ <value value='3' nick='DYNAMIC'/>
+ </enum>
+ <enum id='org.gnome.shell.extensions.dash-to-dock.running-indicator-style'>
+ <value value='0' nick='DEFAULT'/>
+ <value value='1' nick='DOTS'/>
+ <value value='2' nick='SQUARES'/>
+ <value value='3' nick='DASHES'/>
+ <value value='4' nick='SEGMENTED'/>
+ <value value='5' nick='SOLID'/>
+ <value value='6' nick='CILIORA'/>
+ <value value='7' nick='METRO'/>
+ </enum>
+ <schema path="/org/gnome/shell/extensions/dash-to-dock/" id="org.gnome.shell.extensions.dash-to-dock">
+ <key name="dock-position" enum="org.gnome.shell.extensions.dash-to-dock.position">
+ <default>'LEFT'</default>
+ <summary>Dock position</summary>
+ <description>Dock is shown on the Left, Right, Top or Bottom side of the screen.</description>
+ </key>
+ <key type="d" name="animation-time">
+ <default>0.2</default>
+ <summary>Animation time</summary>
+ <description>Sets the time duration of the autohide effect.</description>
+ </key>
+ <key type="d" name="show-delay">
+ <default>0.25</default>
+ <summary>Show delay</summary>
+ <description>Sets the delay after the mouse reaches the screen border before showing the dock.</description>
+ </key>
+ <key type="d" name="hide-delay">
+ <default>0.20</default>
+ <summary>Show delay</summary>
+ <description>Sets the delay after the mouse left the dock before hiding it.</description>
+ </key>
+ <key type="b" name="custom-background-color">
+ <default>false</default>
+ <summary>Set a custom dash background background color</summary>
+ <description>Sets the color for the dash background.</description>
+ </key>
+ <key type="s" name="background-color">
+ <default>"#ffffff"</default>
+ <summary>Dash background color.</summary>
+ <description>Customize the background color of the dash.</description>
+ </key>
+ <key name="transparency-mode" enum="org.gnome.shell.extensions.dash-to-dock.transparency-mode">
+ <default>'DEFAULT'</default>
+ <summary>Transparency mode for the dock</summary>
+ <description>FIXED: constant transparency. DYNAMIC: dock takes the opaque style only when windows are close to it.</description>
+ </key>
+ <key name="running-indicator-style" enum="org.gnome.shell.extensions.dash-to-dock.running-indicator-style">
+ <default>'DEFAULT'</default>
+ <summary>...</summary>
+ <description>DEFAULT: .... DOTS: ....</description>
+ </key>
+ <key type="b" name="running-indicator-dominant-color">
+ <default>false</default>
+ <summary>Use application icon dominant color for the indicator color</summary>
+ <description></description>
+ </key>
+ <key type="b" name="customize-alphas">
+ <default>false</default>
+ <summary>Manually set the min and max opacity</summary>
+ <description>For the dynamic mode, the min/max opacity values will be given by 'min-alpha' and 'max-alpha'.</description>
+ </key>
+ <key type="d" name="min-alpha">
+ <default>0.2</default>
+ <summary>Opacity of the dash background when free-floating</summary>
+ <description>Sets the opacity of the dash background when no windows are close.</description>
+ </key>
+ <key type="d" name="max-alpha">
+ <default>0.8</default>
+ <summary>Opacity of the dash background when windows are close.</summary>
+ <description>Sets the opacity of the dash background when windows are close.</description>
+ </key>
+ <key type="d" name="background-opacity">
+ <default>0.8</default>
+ <summary>Opacity of the dash background</summary>
+ <description>Sets the opacity of the dash background when in autohide mode.</description>
+ </key>
+ <key type="b" name="intellihide">
+ <default>true</default>
+ <summary>Dock dodges windows</summary>
+ <description>Enable or disable intellihide mode</description>
+ </key>
+ <key name="intellihide-mode" enum="org.gnome.shell.extensions.dash-to-dock.intellihide-mode">
+ <default>'FOCUS_APPLICATION_WINDOWS'</default>
+ <summary>Define which windows are considered for intellihide.</summary>
+ <description></description>
+ </key>
+ <key type="b" name="autohide">
+ <default>true</default>
+ <summary>Dock shown on mouse over</summary>
+ <description>Enable or disable autohide mode</description>
+ </key>
+ <key type="b" name="require-pressure-to-show">
+ <default>true</default>
+ <summary>Require pressure to show dash</summary>
+ <description>Enable or disable requiring pressure to show the dash</description>
+ </key>
+ <key type="d" name="pressure-threshold">
+ <default>100</default>
+ <summary>Pressure threshold</summary>
+ <description>Sets how much pressure is needed to show the dash.</description>
+ </key>
+ <key type="b" name="autohide-in-fullscreen">
+ <default>false</default>
+ <summary>Enable autohide in fullscreen mode.</summary>
+ <description>Enable autohide in fullscreen mode.</description>
+ </key>
+ <key type="b" name="dock-fixed">
+ <default>false</default>
+ <summary>Dock always visible</summary>
+ <description>Dock is always visible</description>
+ </key>
+ <key type="b" name="scroll-switch-workspace">
+ <default>true</default>
+ <summary>Switch workspace by scrolling over the dock</summary>
+ <description>Add the possibility to switch workspace by mouse scrolling over the dock.</description>
+ </key>
+ <key type="i" name="dash-max-icon-size">
+ <default>48</default>
+ <summary>Maximum dash icon size</summary>
+ <description>Set the allowed maximum dash icon size. Allowed range: 16..64.</description>
+ </key>
+ <key type="b" name="icon-size-fixed">
+ <default>false</default>
+ <summary>Fixed icon size</summary>
+ <description>Keep the icon size fived by scrolling the dock.</description>
+ </key>
+ <key type="b" name="apply-custom-theme">
+ <default>false</default>
+ <summary>Apply custom theme</summary>
+ <description>Apply customization to the dash appearance</description>
+ </key>
+ <key type="b" name="custom-theme-shrink">
+ <default>false</default>
+ <summary>TODO</summary>
+ <description>TODO</description>
+ </key>
+ <key type="b" name="custom-theme-customize-running-dots">
+ <default>false</default>
+ <summary>Customize the style of the running application indicators.</summary>
+ <description>Customize the style of the running application indicators.</description>
+ </key>
+ <key type="s" name="custom-theme-running-dots-color">
+ <default>"#ffffff"</default>
+ <summary>Running application indicators color</summary>
+ <description>Customize the color of the running application indicators.</description>
+ </key>
+ <key type="s" name="custom-theme-running-dots-border-color">
+ <default>"#ffffff"</default>
+ <summary>Running application indicators border color.</summary>
+ <description>Customize the border color of the running application indicators.</description>
+ </key>
+ <key type="i" name="custom-theme-running-dots-border-width">
+ <default>0</default>
+ <summary>Running application indicators border width.</summary>
+ <description>Customize the border width of the running application indicators.</description>
+ </key>
+ <key type="b" name="show-running">
+ <default>true</default>
+ <summary>Show running apps</summary>
+ <description>Show or hide running appplications icons in the dash</description>
+ </key>
+ <key type="b" name="isolate-workspaces">
+ <default>false</default>
+ <summary>Provide workspace isolation</summary>
+ <description>Dash shows only windows from the currentworkspace</description>
+ </key>
+ <key type="b" name="isolate-monitors">
+ <default>false</default>
+ <summary>Provide monitor isolation</summary>
+ <description>Dash shows only windows from the monitor</description>
+ </key>
+ <key type="b" name="show-windows-preview">
+ <default>true</default>
+ <summary>Show preview of the open windows</summary>
+ <description>Replace open windows list with windows previews</description>
+ </key>
+ <key type="b" name="show-favorites">
+ <default>true</default>
+ <summary>Show favorites apps</summary>
+ <description>Show or hide favorite appplications icons in the dash</description>
+ </key>
+ <key type="b" name="show-show-apps-button">
+ <default>true</default>
+ <summary>Show applications button</summary>
+ <description>Show appplications button in the dash</description>
+ </key>
+ <key type="b" name="show-apps-at-top">
+ <default>false</default>
+ <summary>Show application button at top</summary>
+ <description>Show appplication button at top of the dash</description>
+ </key>
+ <key type="b" name="animate-show-apps">
+ <default>true</default>
+ <summary>Animate Show Applications from the desktop</summary>
+ <description>Animate Show Applications from the desktop</description>
+ </key>
+ <key type="b" name="bolt-support">
+ <default>true</default>
+ <summary>Basic compatibility with bolt extensions</summary>
+ <description>Make the extension work properly when bolt extensions is enabled</description>
+ </key>
+ <key type="d" name="height-fraction">
+ <default>0.90</default>
+ <summary>Dock max height (fraction of available space)</summary>
+ </key>
+ <key type="b" name="extend-height">
+ <default>false</default>
+ <summary>Extend the dock container to all the available height</summary>
+ </key>
+ <key type="i" name="preferred-monitor">
+ <default>-1</default>
+ <summary>Monitor on which putting the dock</summary>
+ <description>Set on which monitor to put the dock, use -1 for the primary one</description>
+ </key>
+ <key type="b" name="multi-monitor">
+ <default>false</default>
+ <summary>Enable multi-monitor docks</summary>
+ <description>Show a dock on every monitor</description>
+ </key>
+ <key type="b" name="minimize-shift">
+ <default>true</default>
+ <summary>Minimize on shift+click</summary>
+ </key>
+ <key type="b" name="activate-single-window">
+ <default>true</default>
+ <summary>Activate only one window</summary>
+ </key>
+ <key name="click-action" enum="org.gnome.shell.extensions.dash-to-dock.clickAction">
+ <default>'cycle-windows'</default>
+ <summary>Action when clicking on a running app</summary>
+ <description>Set the action that is executed when clicking on the icon of a running application</description>
+ </key>
+ <key name="scroll-action" enum="org.gnome.shell.extensions.dash-to-dock.scrollAction">
+ <default>'do-nothing'</default>
+ <summary>Action when scrolling app</summary>
+ <description>Set the action that is executed when scrolling on the application icon</description>
+ </key>
+ <key name="shift-click-action" enum="org.gnome.shell.extensions.dash-to-dock.clickAction">
+ <default>'minimize'</default>
+ <summary>Action when shift+clicking on a running app</summary>
+ <description>Set the action that is executed when shift+clicking on the icon of a running application</description>
+ </key>
+ <key name="middle-click-action" enum="org.gnome.shell.extensions.dash-to-dock.clickAction">
+ <default>'launch'</default>
+ <summary>Action when clicking on a running app</summary>
+ <description>Set the action that is executed when middle-clicking on the icon of a running application</description>
+ </key>
+ <key name="shift-middle-click-action" enum="org.gnome.shell.extensions.dash-to-dock.clickAction">
+ <default>'launch'</default>
+ <summary>Action when clicking on a running app</summary>
+ <description>Set the action that is executed when shift+middle-clicking on the icon of a running application</description>
+ </key>
+ <key type="b" name="hot-keys">
+ <default>true</default>
+ <summary>Super Hot-Keys</summary>
+ <description>Launch and switch between dash items using Super+(0-9)</description>
+ </key>
+ <key type="b" name="hotkeys-show-dock">
+ <default>true</default>
+ <summary>Show the dock when using the hotkeys</summary>
+ <description>The dock will be quickly shown so that the number-overlay is visible and app activation is easier</description>
+ </key>
+ <key type="s" name="shortcut-text">
+ <default>"<Super>q"</default>
+ <summary>Keybinding to show the dock and the number overlay.</summary>
+ <description>Behavior depends on hotkeys-show-dock and hotkeys-overlay.</description>
+ </key>
+ <key type="as" name="shortcut">
+ <default><![CDATA[['<Super>q']]]></default>
+ <summary>Keybinding to show the dock and the number overlay.</summary>
+ <description>Behavior depends on hotkeys-show-dock and hotkeys-overlay.</description>
+ </key>
+ <key type="d" name="shortcut-timeout">
+ <default>2</default>
+ <summary>Timeout to hide the dock</summary>
+ <description>Sets the time duration before the dock is hidden again.</description>
+ </key>
+ <key type="b" name="hotkeys-overlay">
+ <default>true</default>
+ <summary>Show the dock when using the hotkeys</summary>
+ <description>The dock will be quickly shown so that the number-overlay is visible and app activation is easier</description>
+ </key>
+ <key name="app-ctrl-hotkey-1" type="as">
+ <default><![CDATA[['<Ctrl><Super>1']]]></default>
+ <summary>Keybinding to launch 1st dash app</summary>
+ <description>
+ Keybinding to launch 1st app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-2" type="as">
+ <default><![CDATA[['<Ctrl><Super>2']]]></default>
+ <summary>Keybinding to launch 2nd dash app</summary>
+ <description>
+ Keybinding to launch 2nd app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-3" type="as">
+ <default><![CDATA[['<Ctrl><Super>3']]]></default>
+ <summary>Keybinding to launch 3rd dash app</summary>
+ <description>
+ Keybinding to launch 3rd app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-4" type="as">
+ <default><![CDATA[['<Ctrl><Super>4']]]></default>
+ <summary>Keybinding to launch 4th dash app</summary>
+ <description>
+ Keybinding to launch 4th app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-5" type="as">
+ <default><![CDATA[['<Ctrl><Super>5']]]></default>
+ <summary>Keybinding to launch 5th dash app</summary>
+ <description>
+ Keybinding to launch 5th app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-6" type="as">
+ <default><![CDATA[['<Ctrl><Super>6']]]></default>
+ <summary>Keybinding to launch 6th dash app</summary>
+ <description>
+ Keybinding to launch 6th app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-7" type="as">
+ <default><![CDATA[['<Ctrl><Super>7']]]></default>
+ <summary>Keybinding to launch 7th dash app</summary>
+ <description>
+ Keybinding to launch 7th app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-8" type="as">
+ <default><![CDATA[['<Ctrl><Super>8']]]></default>
+ <summary>Keybinding to launch 8th dash app</summary>
+ <description>
+ Keybinding to launch 8th app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-9" type="as">
+ <default><![CDATA[['<Ctrl><Super>9']]]></default>
+ <summary>Keybinding to launch 9th dash app</summary>
+ <description>
+ Keybinding to launch 9th app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-10" type="as">
+ <default><![CDATA[['<Ctrl><Super>0']]]></default>
+ <summary>Keybinding to launch 10th dash app</summary>
+ <description>
+ Keybinding to launch 10th app.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-1" type="as">
+ <default><![CDATA[['<Shift><Super>1']]]></default>
+ <summary>Keybinding to trigger 1st dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 1st app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-2" type="as">
+ <default><![CDATA[['<Shift><Super>2']]]></default>
+ <summary>Keybinding to trigger 2nd dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 2nd app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-3" type="as">
+ <default><![CDATA[['<Shift><Super>3']]]></default>
+ <summary>Keybinding to trigger 3rd dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 3rd app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-4" type="as">
+ <default><![CDATA[['<Shift><Super>4']]]></default>
+ <summary>Keybinding to trigger 4th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 4th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-5" type="as">
+ <default><![CDATA[['<Shift><Super>5']]]></default>
+ <summary>Keybinding to trigger 5th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 5th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-6" type="as">
+ <default><![CDATA[['<Shift><Super>6']]]></default>
+ <summary>Keybinding to trigger 6th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 6th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-7" type="as">
+ <default><![CDATA[['<Shift><Super>7']]]></default>
+ <summary>Keybinding to trigger 7th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 7th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-8" type="as">
+ <default><![CDATA[['<Shift><Super>8']]]></default>
+ <summary>Keybinding to trigger 8th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 8th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-9" type="as">
+ <default><![CDATA[['<Shift><Super>9']]]></default>
+ <summary>Keybinding to trigger 9th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 9th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-10" type="as">
+ <default><![CDATA[['<Shift><Super>0']]]></default>
+ <summary>Keybinding to trigger 10th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 10th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-hotkey-1" type="as">
+ <default><![CDATA[['<Super>1']]]></default>
+ <summary>Keybinding to trigger 1st dash app</summary>
+ <description>
+ Keybinding to either show or launch the 1st application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-2" type="as">
+ <default><![CDATA[['<Super>2']]]></default>
+ <summary>Keybinding to trigger 2nd dash app</summary>
+ <description>
+ Keybinding to either show or launch the 2nd application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-3" type="as">
+ <default><![CDATA[['<Super>3']]]></default>
+ <summary>Keybinding to trigger 3rd dash app</summary>
+ <description>
+ Keybinding to either show or launch the 3rd application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-4" type="as">
+ <default><![CDATA[['<Super>4']]]></default>
+ <summary>Keybinding to trigger 4th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 4th application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-5" type="as">
+ <default><![CDATA[['<Super>5']]]></default>
+ <summary>Keybinding to trigger 5th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 5th application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-6" type="as">
+ <default><![CDATA[['<Super>6']]]></default>
+ <summary>Keybinding to trigger 6th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 6th application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-7" type="as">
+ <default><![CDATA[['<Super>7']]]></default>
+ <summary>Keybinding to trigger 7th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 7th application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-8" type="as">
+ <default><![CDATA[['<Super>8']]]></default>
+ <summary>Keybinding to trigger 8th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 8th application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-9" type="as">
+ <default><![CDATA[['<Super>9']]]></default>
+ <summary>Keybinding to trigger 9th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 9th application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-10" type="as">
+ <default><![CDATA[['<Super>0']]]></default>
+ <summary>Keybinding to trigger 10th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 10th application in the dash.
+ </description>
+ </key>
+ <key name="force-straight-corner" type="b">
+ <default>false</default>
+ <summary>Force straight corners in dash</summary>
+ <description>Make the borders in the dash non rounded</description>
+ </key>
+ <key name="unity-backlit-items" type="b">
+ <default>false</default>
+ <summary>Enable unity7 like glossy backlit items</summary>
+ <description>Emulate the unity7 backlit glossy items behaviour</description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/extensions/dash-to-dock/prefs.js b/extensions/dash-to-dock/prefs.js
new file mode 100644
index 0000000..a72b139
--- /dev/null
+++ b/extensions/dash-to-dock/prefs.js
@@ -0,0 +1,861 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+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 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 ExtensionUtils = imports.misc.extensionUtils;
+const Me = ExtensionUtils.getCurrentExtension();
+
+const SCALE_UPDATE_TIMEOUT = 500;
+const DEFAULT_ICONS_SIZES = [ 128, 96, 64, 48, 32, 24, 16 ];
+
+const TransparencyMode = {
+ DEFAULT: 0,
+ FIXED: 1,
+ DYNAMIC: 3
+};
+
+const RunningIndicatorStyle = {
+ DEFAULT: 0,
+ DOTS: 1,
+ SQUARES: 2,
+ DASHES: 3,
+ SEGMENTED: 4,
+ SOLID: 5,
+ CILIORA: 6,
+ METRO: 7
+};
+
+/**
+ * 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;
+ }
+ }
+ xx = parseInt(css.slice(start, end)).toString(16);
+ if (xx.length == 1)
+ xx = '0' + xx;
+ rrggbb += xx;
+ css = css.slice(end);
+ }
+ return rrggbb;
+}
+
+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', []);
+ }
+}
+
+var Settings = class DashToDock_Settings {
+
+ constructor() {
+ this._settings = ExtensionUtils.getSettings('org.gnome.shell.extensions.dash-to-dock');
+
+ this._rtl = (Gtk.Widget.get_default_direction() == Gtk.TextDirection.RTL);
+
+ this._builder = new Gtk.Builder();
+ this._builder.set_translation_domain(Me.metadata['gettext-domain']);
+ this._builder.add_from_file(Me.path + '/Settings.ui');
+
+ this.widget = new Gtk.ScrolledWindow({ hscrollbar_policy: Gtk.PolicyType.NEVER });
+ this._notebook = this._builder.get_object('settings_notebook');
+ this.widget.add(this._notebook);
+
+ // Set a reasonable initial window height
+ this.widget.connect('realize', () => {
+ let window = this.widget.get_toplevel();
+ let [default_width, default_height] = window.get_default_size();
+ window.resize(default_width, 650);
+ });
+
+ // Timeout to delay the update of the settings
+ this._dock_size_timeout = 0;
+ this._icon_size_timeout = 0;
+ this._opacity_timeout = 0;
+
+ this._bindSettings();
+
+ this._builder.connect_signals_full(this._connector.bind(this));
+ }
+
+ /**
+ * Connect signals
+ */
+ _connector(builder, object, signal, handler) {
+ /**init
+ * Object containing all signals defined in the glade file
+ */
+ const SignalHandler = {
+ dock_display_combo_changed_cb(combo) {
+ this._settings.set_int('preferred-monitor', this._monitors[combo.get_active()]);
+ },
+
+ position_top_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('dock-position', 0);
+ },
+
+ position_right_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('dock-position', 1);
+ },
+
+ position_bottom_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('dock-position', 2);
+ },
+
+ position_left_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('dock-position', 3);
+ },
+
+ icon_size_combo_changed_cb(combo) {
+ this._settings.set_int('dash-max-icon-size', this._allIconSizes[combo.get_active()]);
+ },
+
+ dock_size_scale_format_value_cb(scale, value) {
+ return Math.round(value * 100) + ' %';
+ },
+
+ dock_size_scale_value_changed_cb(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, () => {
+ this._settings.set_double('height-fraction', scale.get_value());
+ this._dock_size_timeout = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ },
+
+ icon_size_scale_format_value_cb(scale, value) {
+ return value + ' px';
+ },
+
+ icon_size_scale_value_changed_cb(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, () => {
+ this._settings.set_int('dash-max-icon-size', scale.get_value());
+ this._icon_size_timeout = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ },
+
+ custom_opacity_scale_value_changed_cb(scale) {
+ // Avoid settings the opacity consinuosly as it's change is animated
+ if (this._opacity_timeout > 0)
+ Mainloop.source_remove(this._opacity_timeout);
+
+ this._opacity_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, () => {
+ this._settings.set_double('background-opacity', scale.get_value());
+ this._opacity_timeout = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ },
+
+ min_opacity_scale_value_changed_cb(scale) {
+ // Avoid settings the opacity consinuosly as it's change is animated
+ if (this._opacity_timeout > 0)
+ Mainloop.source_remove(this._opacity_timeout);
+
+ this._opacity_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, () => {
+ this._settings.set_double('min-alpha', scale.get_value());
+ this._opacity_timeout = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ },
+
+ max_opacity_scale_value_changed_cb(scale) {
+ // Avoid settings the opacity consinuosly as it's change is animated
+ if (this._opacity_timeout > 0)
+ Mainloop.source_remove(this._opacity_timeout);
+
+ this._opacity_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, () => {
+ this._settings.set_double('max-alpha', scale.get_value());
+ this._opacity_timeout = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ },
+
+ custom_opacity_scale_format_value_cb(scale, value) {
+ return Math.round(value * 100) + ' %';
+ },
+
+ min_opacity_scale_format_value_cb(scale, value) {
+ return Math.round(value * 100) + ' %';
+ },
+
+ max_opacity_scale_format_value_cb(scale, value) {
+ return Math.round(value * 100) + ' %';
+ },
+
+ all_windows_radio_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('intellihide-mode', 0);
+ },
+
+ focus_application_windows_radio_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('intellihide-mode', 1);
+ },
+
+ maximized_windows_radio_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('intellihide-mode', 2);
+ }
+ }
+
+ object.connect(signal, SignalHandler[handler].bind(this));
+ }
+
+ _bindSettings() {
+ // Position and size panel
+
+ // Monitor options
+
+ 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');
+
+ // 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);
+ }
+ }
+
+ // 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._builder.get_object('dock_monitor_combo').set_active(this._monitors.indexOf(monitor));
+
+ // Position option
+ let position = this._settings.get_enum('dock-position');
+
+ 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 (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'));
+ }
+
+ // 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);
+
+ //this._builder.get_object('animation_duration_spinbutton').set_value(this._settings.get_double('animation-time'));
+
+ // Create dialog for intelligent autohide advanced settings
+ this._builder.get_object('intelligent_autohide_button').connect('clicked', () => {
+
+ let dialog = new Gtk.Dialog({ title: __('Intelligent autohide customization'),
+ transient_for: this.widget.get_toplevel(),
+ use_header_bar: true,
+ modal: true });
+
+ // GTK+ leaves positive values for application-defined response ids.
+ // Use +1 for the reset action
+ dialog.add_button(__('Reset to defaults'), 1);
+
+ let box = this._builder.get_object('intelligent_autohide_advanced_settings_box');
+ dialog.get_content_area().add(box);
+
+ this._settings.bind('intellihide',
+ this._builder.get_object('intellihide_mode_box'),
+ 'sensitive',
+ Gio.SettingsBindFlags.GET);
+
+ // intellihide mode
+
+ 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')
+ ];
+
+ intellihideModeRadioButtons[this._settings.get_enum('intellihide-mode')].set_active(true);
+
+ this._settings.bind('autohide',
+ this._builder.get_object('require_pressure_checkbutton'),
+ 'sensitive',
+ Gio.SettingsBindFlags.GET);
+
+ this._settings.bind('autohide',
+ this._builder.get_object('autohide_enable_in_fullscreen_checkbutton'),
+ 'sensitive',
+ Gio.SettingsBindFlags.GET);
+
+ 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);
+
+ dialog.connect('response', (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;
+ });
+
+ dialog.show_all();
+
+ });
+
+ // 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());
+ });
+
+ // 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);
+ }
+
+ 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);
+
+
+ // 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);
+
+
+ // Behavior panel
+
+ 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);
+
+ this._builder.get_object('click_action_combo').set_active(this._settings.get_enum('click-action'));
+ this._builder.get_object('click_action_combo').connect('changed', (widget) => {
+ this._settings.set_enum('click-action', widget.get_active());
+ });
+
+ this._builder.get_object('scroll_action_combo').set_active(this._settings.get_enum('scroll-action'));
+ this._builder.get_object('scroll_action_combo').connect('changed', (widget) => {
+ this._settings.set_enum('scroll-action', widget.get_active());
+ });
+
+ this._builder.get_object('shift_click_action_combo').connect('changed', (widget) => {
+ this._settings.set_enum('shift-click-action', widget.get_active());
+ });
+
+ this._builder.get_object('middle_click_action_combo').connect('changed', (widget) => {
+ this._settings.set_enum('middle-click-action', widget.get_active());
+ });
+ this._builder.get_object('shift_middle_click_action_combo').connect('changed', (widget) => {
+ this._settings.set_enum('shift-middle-click-action', widget.get_active());
+ });
+
+ // Create dialog for number overlay options
+ this._builder.get_object('overlay_button').connect('clicked', () => {
+
+ let dialog = new Gtk.Dialog({ title: __('Show dock and application numbers'),
+ transient_for: this.widget.get_toplevel(),
+ use_header_bar: true,
+ modal: true });
+
+ // GTK+ leaves positive values for application-defined response ids.
+ // Use +1 for the reset action
+ dialog.add_button(__('Reset to defaults'), 1);
+
+ let box = this._builder.get_object('box_overlay_shortcut');
+ dialog.get_content_area().add(box);
+
+ this._builder.get_object('overlay_switch').set_active(this._settings.get_boolean('hotkeys-overlay'));
+ this._builder.get_object('show_dock_switch').set_active(this._settings.get_boolean('hotkeys-show-dock'));
+
+ // We need to update the shortcut 'strv' when the text is modified
+ this._settings.connect('changed::shortcut-text', () => {setShortcut(this._settings);});
+ this._settings.bind('shortcut-text',
+ this._builder.get_object('shortcut_entry'),
+ 'text',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ this._settings.bind('hotkeys-overlay',
+ this._builder.get_object('overlay_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('hotkeys-show-dock',
+ this._builder.get_object('show_dock_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('shortcut-timeout',
+ this._builder.get_object('timeout_spinbutton'),
+ 'value',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ dialog.connect('response', (dialog, id) => {
+ if (id == 1) {
+ // restore default settings for the relevant keys
+ let keys = ['shortcut-text', 'hotkeys-overlay', 'hotkeys-show-dock', 'shortcut-timeout'];
+ 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;
+ });
+
+ dialog.show_all();
+ });
+
+ // Create dialog for middle-click options
+ this._builder.get_object('middle_click_options_button').connect('clicked', () => {
+
+ let dialog = new Gtk.Dialog({ title: __('Customize middle-click behavior'),
+ transient_for: this.widget.get_toplevel(),
+ use_header_bar: true,
+ modal: true });
+
+ // GTK+ leaves positive values for application-defined response ids.
+ // Use +1 for the reset action
+ dialog.add_button(__('Reset to defaults'), 1);
+
+ let box = this._builder.get_object('box_middle_click_options');
+ dialog.get_content_area().add(box);
+
+ this._builder.get_object('shift_click_action_combo').set_active(this._settings.get_enum('shift-click-action'));
+
+ this._builder.get_object('middle_click_action_combo').set_active(this._settings.get_enum('middle-click-action'));
+
+ this._builder.get_object('shift_middle_click_action_combo').set_active(this._settings.get_enum('shift-middle-click-action'));
+
+ this._settings.bind('shift-click-action',
+ this._builder.get_object('shift_click_action_combo'),
+ 'active-id',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('middle-click-action',
+ this._builder.get_object('middle_click_action_combo'),
+ 'active-id',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('shift-middle-click-action',
+ this._builder.get_object('shift_middle_click_action_combo'),
+ 'active-id',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ dialog.connect('response', (dialog, id) => {
+ if (id == 1) {
+ // restore default settings for the relevant keys
+ let keys = ['shift-click-action', 'middle-click-action', 'shift-middle-click-action'];
+ keys.forEach(function(val) {
+ this._settings.set_value(val, this._settings.get_default_value(val));
+ }, this);
+ this._builder.get_object('shift_click_action_combo').set_active(this._settings.get_enum('shift-click-action'));
+ this._builder.get_object('middle_click_action_combo').set_active(this._settings.get_enum('middle-click-action'));
+ this._builder.get_object('shift_middle_click_action_combo').set_active(this._settings.get_enum('shift-middle-click-action'));
+ } else {
+ // remove the settings box so it doesn't get destroyed;
+ dialog.get_content_area().remove(box);
+ dialog.destroy();
+ }
+ return;
+ });
+
+ dialog.show_all();
+
+ });
+
+ // Appearance Panel
+
+ 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);
+
+ // Running indicators
+ this._builder.get_object('running_indicators_combo').set_active(
+ this._settings.get_enum('running-indicator-style')
+ );
+ this._builder.get_object('running_indicators_combo').connect(
+ 'changed',
+ (widget) => {
+ this._settings.set_enum('running-indicator-style', widget.get_active());
+ }
+ );
+
+ if (this._settings.get_enum('running-indicator-style') == RunningIndicatorStyle.DEFAULT)
+ this._builder.get_object('running_indicators_advance_settings_button').set_sensitive(false);
+
+ this._settings.connect('changed::running-indicator-style', () => {
+ if (this._settings.get_enum('running-indicator-style') == RunningIndicatorStyle.DEFAULT)
+ this._builder.get_object('running_indicators_advance_settings_button').set_sensitive(false);
+ else
+ this._builder.get_object('running_indicators_advance_settings_button').set_sensitive(true);
+ });
+
+ // Create dialog for running indicators advanced settings
+ this._builder.get_object('running_indicators_advance_settings_button').connect('clicked', () => {
+
+ let dialog = new Gtk.Dialog({ title: __('Customize running indicators'),
+ transient_for: this.widget.get_toplevel(),
+ use_header_bar: true,
+ modal: true });
+
+ let box = this._builder.get_object('running_dots_advance_settings_box');
+ dialog.get_content_area().add(box);
+
+ this._settings.bind('running-indicator-dominant-color',
+ this._builder.get_object('dominant_color_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ 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', (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);
+ });
+
+ rgba.parse(this._settings.get_string('custom-theme-running-dots-border-color'));
+ this._builder.get_object('dot_border_color_colorbutton').set_rgba(rgba);
+
+ this._builder.get_object('dot_border_color_colorbutton').connect('notify::color', (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);
+ });
+
+ this._settings.bind('custom-theme-running-dots-border-width',
+ this._builder.get_object('dot_border_width_spin_button'),
+ 'value',
+ Gio.SettingsBindFlags.DEFAULT);
+
+
+ dialog.connect('response', (dialog, id) => {
+ // remove the settings box so it doesn't get destroyed;
+ dialog.get_content_area().remove(box);
+ dialog.destroy();
+ return;
+ });
+
+ dialog.show_all();
+
+ });
+
+ 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 rgba = new Gdk.RGBA();
+ rgba.parse(this._settings.get_string('background-color'));
+ this._builder.get_object('custom_background_color').set_rgba(rgba);
+
+ this._builder.get_object('custom_background_color').connect('notify::color', (button) => {
+ let rgba = button.get_rgba();
+ let css = rgba.to_string();
+ let hexString = cssHexString(css);
+ this._settings.set_string('background-color', hexString);
+ });
+
+ // Opacity
+ this._builder.get_object('customize_opacity_combo').set_active(
+ this._settings.get_enum('transparency-mode')
+ );
+ this._builder.get_object('customize_opacity_combo').connect(
+ 'changed',
+ (widget) => {
+ this._settings.set_enum('transparency-mode', widget.get_active());
+ }
+ );
+
+ this._builder.get_object('custom_opacity_scale').set_value(this._settings.get_double('background-opacity'));
+
+ if (this._settings.get_enum('transparency-mode') !== TransparencyMode.FIXED)
+ this._builder.get_object('custom_opacity_scale').set_sensitive(false);
+
+ this._settings.connect('changed::transparency-mode', () => {
+ if (this._settings.get_enum('transparency-mode') !== TransparencyMode.FIXED)
+ this._builder.get_object('custom_opacity_scale').set_sensitive(false);
+ else
+ this._builder.get_object('custom_opacity_scale').set_sensitive(true);
+ });
+
+ if (this._settings.get_enum('transparency-mode') !== TransparencyMode.DYNAMIC) {
+ this._builder.get_object('dynamic_opacity_button').set_sensitive(false);
+ }
+
+ this._settings.connect('changed::transparency-mode', () => {
+ if (this._settings.get_enum('transparency-mode') !== TransparencyMode.DYNAMIC) {
+ this._builder.get_object('dynamic_opacity_button').set_sensitive(false);
+ }
+ else {
+ this._builder.get_object('dynamic_opacity_button').set_sensitive(true);
+ }
+ });
+
+ // Create dialog for transparency advanced settings
+ this._builder.get_object('dynamic_opacity_button').connect('clicked', () => {
+
+ let dialog = new Gtk.Dialog({ title: __('Cutomize opacity'),
+ transient_for: this.widget.get_toplevel(),
+ use_header_bar: true,
+ modal: true });
+
+ let box = this._builder.get_object('advanced_transparency_dialog');
+ dialog.get_content_area().add(box);
+
+ this._settings.bind(
+ 'customize-alphas',
+ this._builder.get_object('customize_alphas_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this._settings.bind(
+ 'customize-alphas',
+ this._builder.get_object('min_alpha_scale'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this._settings.bind(
+ 'customize-alphas',
+ this._builder.get_object('max_alpha_scale'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+
+ this._builder.get_object('min_alpha_scale').set_value(
+ this._settings.get_double('min-alpha')
+ );
+ this._builder.get_object('max_alpha_scale').set_value(
+ this._settings.get_double('max-alpha')
+ );
+
+ dialog.connect('response', (dialog, id) => {
+ // remove the settings box so it doesn't get destroyed;
+ dialog.get_content_area().remove(box);
+ dialog.destroy();
+ return;
+ });
+
+ dialog.show_all();
+ });
+
+
+ this._settings.bind('unity-backlit-items',
+ this._builder.get_object('unity_backlit_items_switch'),
+ 'active', Gio.SettingsBindFlags.DEFAULT
+ );
+
+ this._settings.bind('force-straight-corner',
+ this._builder.get_object('force_straight_corner_switch'),
+ 'active', Gio.SettingsBindFlags.DEFAULT);
+
+ // About Panel
+
+ this._builder.get_object('extension_version').set_label(Me.metadata.version.toString());
+ }
+};
+
+function init() {
+ ExtensionUtils.initTranslations();
+}
+
+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..26cc960
--- /dev/null
+++ b/extensions/dash-to-dock/stylesheet.css
@@ -0,0 +1,175 @@
+/* Shrink the dash by reducing padding and border radius */
+#dashtodockContainer.shrink #dash,
+#dashtodockContainer.dashtodock #dash {
+ border:1px;
+ padding:0px;
+}
+
+#dashtodockContainer.shrink.left #dash,
+#dashtodockContainer.dashtodock.left #dash {
+ border-left: 0px;
+ border-radius: 0px 9px 9px 0px;
+}
+
+
+#dashtodockContainer.shrink.right #dash,
+#dashtodockContainer.dashtodock.right #dash {
+ border-right: 0px;
+ border-radius: 9px 0px 0px 9px;
+}
+
+
+#dashtodockContainer.shrink.top #dash,
+#dashtodockContainer.dashtodock.top #dash {
+ border-top: 0px;
+ border-radius: 0px 0px 9px 9px;
+}
+
+#dashtodockContainer.shrink.bottom #dash,
+#dashtodockContainer.dashtodock.bottom #dash {
+ border-bottom: 0px;
+ border-radius: 9px 9px 0px 0px;
+}
+
+#dashtodockContainer.straight-corner #dash,
+#dashtodockContainer.shrink.straight-corner #dash {
+ border-radius: 0px;
+}
+
+/* Scrollview style */
+.bottom #dashtodockDashScrollview,
+.top #dashtodockDashScrollview {
+ -st-hfade-offset: 24px;
+}
+
+.left #dashtodockDashScrollview,
+.right #dashtodockDashScrollview {
+ -st-vfade-offset: 24px;
+}
+
+#dashtodockContainer.running-dots .dash-item-container > StButton,
+#dashtodockContainer.dashtodock .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain;
+}
+
+#dashtodockContainer.shrink .dash-item-container > StButton,
+#dashtodockContainer.dashtodock .dash-item-container > StButton {
+ padding: 1px 2px;
+}
+
+/* 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;
+}
+
+#dashtodockContainer.extended.right #dash,
+#dashtodockContainer.extended.left #dash {
+ border-top:0px;
+ border-bottom:0px;
+}
+
+/* Running and focused application style */
+
+#dashtodockContainer.running-dots .app-well-app.running > .overview-icon,
+#dashtodockContainer.dashtodock .app-well-app.running > .overview-icon {
+ background-image:none;
+}
+
+
+#dashtodockContainer.running-dots .app-well-app.focused .overview-icon,
+#dashtodockContainer.dashtodock .app-well-app.focused .overview-icon {
+ background-color: rgba(238, 238, 236, 0.2);
+}
+
+#dashtodockContainer.dashtodock #dash {
+ background: #2e3436;
+}
+
+/*
+ * This is applied to a dummy actor. Only the alpha value for the background and border color
+ * and the transition-duration are used
+ */
+#dashtodockContainer.dummy-opaque {
+ background-color: rgba(0, 0, 0, 0.8);
+ border-color: rgba(0, 0, 0, 0.4);
+ transition-duration: 300ms;
+}
+
+/*
+ * This is applied to a dummy actor. Only the alpha value for the background and border color
+ * and the transition-duration are used
+ */
+#dashtodockContainer.dummy-transparent {
+ background-color: rgba(0, 0, 0, 0.2);
+ border-color: rgba(0, 0, 0, 0.1);
+ transition-duration: 500ms;
+}
+
+#dashtodockContainer.opaque {
+}
+
+#dashtodockContainer.transparent {
+}
+
+#dashtodockContainer .number-overlay {
+ color: rgba(255,255,255,1);
+ background-color: rgba(0,0,0,0.8);
+ text-align: center;
+}
+
+#dashtodockContainer .notification-badge {
+ color: rgba(255,255,255,1);
+ background-color: rgba(255,0,0,1.0);
+ padding: 0.2em 0.5em;
+ border-radius: 1em;
+ font-weight: bold;
+ text-align: center;
+ margin: 2px;
+}
+
+#dashtodockPreviewSeparator.popup-separator-menu-item-horizontal {
+ width: 1px;
+ height: auto;
+ border-right-width: 1px;
+ margin: 32px 0px;
+}
+
+.dashtodock-app-well-preview-menu-item {
+ padding: 1em 1em 0.5em 1em;
+}
+
+#dashtodockContainer .metro .overview-icon{
+ border-radius: 0px;
+}
+
+#dashtodockContainer.bottom .metro.running2.focused,
+#dashtodockContainer.bottom .metro.running3.focused,
+#dashtodockContainer.bottom .metro.running4.focused,
+#dashtodockContainer.top .metro.running2.focused,
+#dashtodockContainer.top .metro.running3.focused,
+#dashtodockContainer.top .metro.running4.focused {
+ background-image: url('./media/highlight_stacked_bg.svg');
+ background-position: 0px 0px;
+ background-size: contain;
+}
+
+#dashtodockContainer.left .metro.running2.focused,
+#dashtodockContainer.left .metro.running3.focused,
+#dashtodockContainer.left .metro.running4.focused,
+#dashtodockContainer.right .metro.running2.focused,
+#dashtodockContainer.right .metro.running3.focused,
+#dashtodockContainer.right .metro.running4.focused {
+ background-image: url('./media/highlight_stacked_bg_h.svg');
+ background-position: 0px 0px;
+ background-size: contain;
+}
\ No newline at end of file
diff --git a/extensions/dash-to-dock/theming.js b/extensions/dash-to-dock/theming.js
new file mode 100644
index 0000000..596574d
--- /dev/null
+++ b/extensions/dash-to-dock/theming.js
@@ -0,0 +1,569 @@
+// -*- 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 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 Dock = Me.imports.docking;
+const Utils = Me.imports.utils;
+
+/*
+ * DEFAULT: transparency given by theme
+ * FIXED: constant transparency chosen by user
+ * DYNAMIC: apply 'transparent' style when no windows are close to the dock
+ * */
+const TransparencyMode = {
+ DEFAULT: 0,
+ FIXED: 1,
+ DYNAMIC: 3
+};
+
+/**
+ * Manage theme customization and custom theme support
+ */
+var ThemeManager = class DashToDock_ThemeManager {
+
+ constructor(settings, dock) {
+ this._settings = settings;
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+ this._bindSettingsChanges();
+ this._actor = dock.actor;
+ this._dash = dock.dash;
+
+ // 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};
+ this._transparency = new Transparency(this._settings, dock);
+
+ this._signalsHandler.add([
+ // When theme changes re-obtain default background color
+ St.ThemeContext.get_for_stage (global.stage),
+ 'changed',
+ this.updateCustomTheme.bind(this)
+ ], [
+ // update :overview pseudoclass
+ Main.overview,
+ 'showing',
+ this._onOverviewShowing.bind(this)
+ ], [
+ Main.overview,
+ 'hiding',
+ this._onOverviewHiding.bind(this)
+ ]);
+
+ this._updateCustomStyleClasses();
+
+ // destroy themeManager when the managed actor is destroyed (e.g. extension unload)
+ // in order to disconnect signals
+ this._actor.connect('destroy', this.destroy.bind(this));
+
+ }
+
+ destroy() {
+ this._signalsHandler.destroy();
+ this._transparency.destroy();
+ }
+
+ _onOverviewShowing() {
+ this._actor.add_style_pseudo_class('overview');
+ }
+
+ _onOverviewHiding() {
+ this._actor.remove_style_pseudo_class('overview');
+ }
+
+ _updateDashOpacity() {
+ let newAlpha = this._settings.get_double('background-opacity');
+
+ let [backgroundColor, borderColor] = this._getDefaultColors();
+
+ if (backgroundColor==null)
+ return;
+
+ // 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;
+
+ // 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);
+
+ this._customizedBackground = 'rgba(' +
+ backgroundColor.red + ',' +
+ backgroundColor.green + ',' +
+ backgroundColor.blue + ',' +
+ newAlpha + ')';
+
+ this._customizedBorder = 'rgba(' +
+ borderColor.red + ',' +
+ borderColor.green + ',' +
+ borderColor.blue + ',' +
+ borderAlpha + ')';
+
+ }
+
+ _getDefaultColors() {
+ // 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];
+
+ // 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);
+
+ let backgroundColor = themeNode.get_background_color();
+
+ // 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 borderColor = themeNode.get_border_color(side);
+
+ return [backgroundColor, borderColor];
+ }
+
+ _updateDashColor() {
+ // Retrieve the color. If needed we will adjust it before passing it to
+ // this._transparency.
+ let [backgroundColor, borderColor] = this._getDefaultColors();
+
+ if (backgroundColor==null)
+ return;
+
+ if (this._settings.get_boolean('custom-background-color')) {
+ // When applying a custom color, we need to check the alpha value,
+ // if not the opacity will always be overridden by the color below.
+ // Note that if using 'dynamic' transparency modes,
+ // the opacity will be set by the opaque/transparent styles anyway.
+ let newAlpha = Math.round(backgroundColor.alpha/2.55)/100;
+ if (this._settings.get_enum('transparency-mode') == TransparencyMode.FIXED)
+ newAlpha = this._settings.get_double('background-opacity');
+
+ backgroundColor = Clutter.color_from_string(this._settings.get_string('background-color'))[1];
+ this._customizedBackground = 'rgba(' +
+ backgroundColor.red + ',' +
+ backgroundColor.green + ',' +
+ backgroundColor.blue + ',' +
+ newAlpha + ')';
+
+ this._customizedBorder = this._customizedBackground;
+ }
+ this._transparency.setColor(backgroundColor);
+ }
+
+ _updateCustomStyleClasses() {
+ 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_enum('running-indicator-style') !== 0)
+ this._actor.add_style_class_name('running-dots');
+ else
+ this._actor.remove_style_class_name('running-dots');
+
+ // 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() {
+ this._updateCustomStyleClasses();
+ this._updateDashOpacity();
+ this._updateDashColor();
+ this._adjustTheme();
+ this._dash._redisplay();
+ }
+
+ /**
+ * Reimported back and adapted from atomdock
+ */
+ _adjustTheme() {
+ // 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);
+ this._transparency.disable();
+
+ // If built-in theme is enabled do nothing else
+ if (this._settings.get_boolean('apply-custom-theme'))
+ return;
+
+ let newStyle = '';
+ let position = Utils.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
+ let fixedTransparency = this._settings.get_enum('transparency-mode') == TransparencyMode.FIXED;
+ let defaultTransparency = this._settings.get_enum('transparency-mode') == TransparencyMode.DEFAULT;
+ if (!defaultTransparency && !fixedTransparency) {
+ this._transparency.enable();
+ }
+ else if (!defaultTransparency || 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);
+ }
+ }
+
+ _bindSettingsChanges() {
+ let keys = ['transparency-mode',
+ 'customize-alphas',
+ 'min-alpha',
+ 'max-alpha',
+ '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,
+ this.updateCustomTheme.bind(this)
+ ]);
+ }, this);
+ }
+};
+
+/**
+ * The following class is based on the following upstream commit:
+ * https://git.gnome.org/browse/gnome-shell/commit/?id=447bf55e45b00426ed908b1b1035f472c2466956
+ * Transparency when free-floating
+ */
+var Transparency = class DashToDock_Transparency {
+
+ constructor(settings, dock) {
+ this._settings = settings;
+ this._dash = dock.dash;
+ this._actor = this._dash._container;
+ this._dockActor = dock.actor;
+ this._dock = dock;
+ this._panel = Main.panel;
+ this._position = Utils.getPosition(this._settings);
+
+ // All these properties are replaced with the ones in the .dummy-opaque and .dummy-transparent css classes
+ this._backgroundColor = '0,0,0';
+ this._transparentAlpha = '0.2';
+ this._opaqueAlpha = '1';
+ this._transparentAlphaBorder = '0.1';
+ this._opaqueAlphaBorder = '0.5';
+ this._transparentTransition = '0ms';
+ this._opaqueTransition = '0ms';
+ this._base_actor_style = "";
+
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+ this._injectionsHandler = new Utils.InjectionsHandler();
+ this._trackedWindows = new Map();
+ }
+
+ enable() {
+ // ensure I never double-register/inject
+ // although it should never happen
+ this.disable();
+
+ this._base_actor_style = this._actor.get_style();
+ if (this._base_actor_style == null) {
+ this._base_actor_style = "";
+ }
+
+ this._signalsHandler.addWithLabel('transparency', [
+ global.window_group,
+ 'actor-added',
+ this._onWindowActorAdded.bind(this)
+ ], [
+ global.window_group,
+ 'actor-removed',
+ this._onWindowActorRemoved.bind(this)
+ ], [
+ global.window_manager,
+ 'switch-workspace',
+ this._updateSolidStyle.bind(this)
+ ], [
+ Main.overview,
+ 'hiding',
+ this._updateSolidStyle.bind(this)
+ ], [
+ Main.overview,
+ 'showing',
+ this._updateSolidStyle.bind(this)
+ ]);
+
+ // Window signals
+ global.window_group.get_children().filter(function(child) {
+ // An irrelevant window actor ('Gnome-shell') produces an error when the signals are
+ // disconnected, therefore do not add signals to it.
+ return child instanceof Meta.WindowActor &&
+ child.get_meta_window().get_wm_class() !== 'Gnome-shell';
+ }).forEach(function(win) {
+ this._onWindowActorAdded(null, win);
+ }, this);
+
+ if (this._actor.get_stage())
+ this._updateSolidStyle();
+
+ this._updateStyles();
+ this._updateSolidStyle();
+
+ this.emit('transparency-enabled');
+ }
+
+ disable() {
+ // ensure I never double-register/inject
+ // although it should never happen
+ this._signalsHandler.removeWithLabel('transparency');
+
+ for (let key of this._trackedWindows.keys())
+ this._trackedWindows.get(key).forEach(id => {
+ key.disconnect(id);
+ });
+ this._trackedWindows.clear();
+
+ this.emit('transparency-disabled');
+ }
+
+ destroy() {
+ this.disable();
+ this._signalsHandler.destroy();
+ this._injectionsHandler.destroy();
+ }
+
+ _onWindowActorAdded(container, metaWindowActor) {
+ let signalIds = [];
+ ['allocation-changed', 'notify::visible'].forEach(s => {
+ signalIds.push(metaWindowActor.connect(s, this._updateSolidStyle.bind(this)));
+ });
+ this._trackedWindows.set(metaWindowActor, signalIds);
+ }
+
+ _onWindowActorRemoved(container, metaWindowActor) {
+ if (!this._trackedWindows.get(metaWindowActor))
+ return;
+
+ this._trackedWindows.get(metaWindowActor).forEach(id => {
+ metaWindowActor.disconnect(id);
+ });
+ this._trackedWindows.delete(metaWindowActor);
+ this._updateSolidStyle();
+ }
+
+ _updateSolidStyle() {
+ let isNear = this._dockIsNear();
+ if (isNear) {
+ this._actor.set_style(this._opaque_style);
+ this._dockActor.remove_style_class_name('transparent');
+ this._dockActor.add_style_class_name('opaque');
+ }
+ else {
+ this._actor.set_style(this._transparent_style);
+ this._dockActor.remove_style_class_name('opaque');
+ this._dockActor.add_style_class_name('transparent');
+ }
+
+ this.emit('solid-style-updated', isNear);
+ }
+
+ _dockIsNear() {
+ if (this._dockActor.has_style_pseudo_class('overview'))
+ return false;
+ /* Get all the windows in the active workspace that are in the primary monitor and visible */
+ let activeWorkspace = global.workspace_manager.get_active_workspace();
+ let dash = this._dash;
+ let windows = activeWorkspace.list_windows().filter(function(metaWindow) {
+ return metaWindow.get_monitor() === dash._monitorIndex &&
+ metaWindow.showing_on_its_workspace() &&
+ metaWindow.get_window_type() != Meta.WindowType.DESKTOP;
+ });
+
+ /* Check if at least one window is near enough to the panel.
+ * If the dock is hidden, we need to account for the space it would take
+ * up when it slides out. This is avoid an ugly transition.
+ * */
+ let factor = 0;
+ if (!this._settings.get_boolean('dock-fixed') &&
+ this._dock.getDockState() == Dock.State.HIDDEN)
+ factor = 1;
+ let [leftCoord, topCoord] = this._actor.get_transformed_position();
+ let threshold;
+ if (this._position === St.Side.LEFT)
+ threshold = leftCoord + this._actor.get_width() * (factor + 1);
+ else if (this._position === St.Side.RIGHT)
+ threshold = leftCoord - this._actor.get_width() * factor;
+ else if (this._position === St.Side.TOP)
+ threshold = topCoord + this._actor.get_height() * (factor + 1);
+ else
+ threshold = topCoord - this._actor.get_height() * factor;
+
+ let scale = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ let isNearEnough = windows.some((metaWindow) => {
+ let coord;
+ if (this._position === St.Side.LEFT) {
+ coord = metaWindow.get_frame_rect().x;
+ return coord < threshold + 5 * scale;
+ }
+ else if (this._position === St.Side.RIGHT) {
+ coord = metaWindow.get_frame_rect().x + metaWindow.get_frame_rect().width;
+ return coord > threshold - 5 * scale;
+ }
+ else if (this._position === St.Side.TOP) {
+ coord = metaWindow.get_frame_rect().y;
+ return coord < threshold + 5 * scale;
+ }
+ else {
+ coord = metaWindow.get_frame_rect().y + metaWindow.get_frame_rect().height;
+ return coord > threshold - 5 * scale;
+ }
+ });
+
+ return isNearEnough;
+ }
+
+ _updateStyles() {
+ this._getAlphas();
+
+ this._transparent_style = this._base_actor_style +
+ 'background-color: rgba(' +
+ this._backgroundColor + ', ' + this._transparentAlpha + ');' +
+ 'border-color: rgba(' +
+ this._backgroundColor + ', ' + this._transparentAlphaBorder + ');' +
+ 'transition-duration: ' + this._transparentTransition + 'ms;';
+
+ this._opaque_style = this._base_actor_style +
+ 'background-color: rgba(' +
+ this._backgroundColor + ', ' + this._opaqueAlpha + ');' +
+ 'border-color: rgba(' +
+ this._backgroundColor + ',' + this._opaqueAlphaBorder + ');' +
+ 'transition-duration: ' + this._opaqueTransition + 'ms;';
+
+ this.emit('styles-updated');
+ }
+
+ setColor(color) {
+ this._backgroundColor = color.red + ',' + color.green + ',' + color.blue;
+ this._updateStyles();
+ }
+
+ _getAlphas() {
+ // Create dummy object and add to the uiGroup to get it to the stage
+ let dummyObject = new St.Bin({
+ name: 'dashtodockContainer',
+ });
+ Main.uiGroup.add_child(dummyObject);
+
+ dummyObject.add_style_class_name('dummy-opaque');
+ let themeNode = dummyObject.get_theme_node();
+ this._opaqueAlpha = themeNode.get_background_color().alpha / 255;
+ this._opaqueAlphaBorder = themeNode.get_border_color(0).alpha / 255;
+ this._opaqueTransition = themeNode.get_transition_duration();
+
+ dummyObject.add_style_class_name('dummy-transparent');
+ themeNode = dummyObject.get_theme_node();
+ this._transparentAlpha = themeNode.get_background_color().alpha / 255;
+ this._transparentAlphaBorder = themeNode.get_border_color(0).alpha / 255;
+ this._transparentTransition = themeNode.get_transition_duration();
+
+ Main.uiGroup.remove_child(dummyObject);
+
+ if (this._settings.get_boolean('customize-alphas')) {
+ this._opaqueAlpha = this._settings.get_double('max-alpha');
+ this._opaqueAlphaBorder = this._opaqueAlpha / 2;
+ this._transparentAlpha = this._settings.get_double('min-alpha');
+ this._transparentAlphaBorder = this._transparentAlpha / 2;
+ }
+ }
+};
+Signals.addSignalMethods(Transparency.prototype);
diff --git a/extensions/dash-to-dock/utils.js b/extensions/dash-to-dock/utils.js
new file mode 100644
index 0000000..d315bd9
--- /dev/null
+++ b/extensions/dash-to-dock/utils.js
@@ -0,0 +1,258 @@
+const Clutter = imports.gi.Clutter;
+const Meta = imports.gi.Meta;
+const St = imports.gi.St;
+
+/**
+ * Simplify global signals and function injections handling
+ * abstract class
+ */
+const BasicHandler = class DashToDock_BasicHandler {
+
+ constructor() {
+ this._storage = new Object();
+ }
+
+ add(/* 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() {
+ for( let label in this._storage )
+ this.removeWithLabel(label);
+ }
+
+ addWithLabel(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++) {
+ let item = this._storage[label];
+ item.push(this._create(arguments[i]));
+ }
+ }
+
+ removeWithLabel(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(item) {
+ throw new Error('no implementation of _create in ' + this);
+ }
+
+ /**
+ * Correctly delete single element
+ */
+ _remove(item) {
+ throw new Error('no implementation of _remove in ' + this);
+ }
+};
+
+/**
+ * Manage global signals
+ */
+var GlobalSignalsHandler = class DashToDock_GlobalSignalHandler extends BasicHandler {
+
+ _create(item) {
+ let object = item[0];
+ let event = item[1];
+ let callback = item[2]
+ let id = object.connect(event, callback);
+
+ return [object, id];
+ }
+
+ _remove(item) {
+ item[0].disconnect(item[1]);
+ }
+};
+
+/**
+ * Color manipulation utilities
+ */
+var ColorUtils = class DashToDock_ColorUtils {
+
+ // Darken or brigthen color by a fraction dlum
+ // Each rgb value is modified by the same fraction.
+ // Return "#rrggbb" string
+ static ColorLuminance(r, g, b, dlum) {
+ let rgbString = '#';
+
+ rgbString += ColorUtils._decimalToHex(Math.round(Math.min(Math.max(r*(1+dlum), 0), 255)), 2);
+ rgbString += ColorUtils._decimalToHex(Math.round(Math.min(Math.max(g*(1+dlum), 0), 255)), 2);
+ rgbString += ColorUtils._decimalToHex(Math.round(Math.min(Math.max(b*(1+dlum), 0), 255)), 2);
+
+ return rgbString;
+ }
+
+ // Convert decimal to an hexadecimal string adding the desired padding
+ static _decimalToHex(d, padding) {
+ let hex = d.toString(16);
+ while (hex.length < padding)
+ hex = '0'+ hex;
+ return hex;
+ }
+
+ // Convert hsv ([0-1, 0-1, 0-1]) to rgb ([0-255, 0-255, 0-255]).
+ // Following algorithm in https://en.wikipedia.org/wiki/HSL_and_HSV
+ // here with h = [0,1] instead of [0, 360]
+ // Accept either (h,s,v) independently or {h:h, s:s, v:v} object.
+ // Return {r:r, g:g, b:b} object.
+ static HSVtoRGB(h, s, v) {
+ if (arguments.length === 1) {
+ s = h.s;
+ v = h.v;
+ h = h.h;
+ }
+
+ let r,g,b;
+ let c = v*s;
+ let h1 = h*6;
+ let x = c*(1 - Math.abs(h1 % 2 - 1));
+ let m = v - c;
+
+ if (h1 <=1)
+ r = c + m, g = x + m, b = m;
+ else if (h1 <=2)
+ r = x + m, g = c + m, b = m;
+ else if (h1 <=3)
+ r = m, g = c + m, b = x + m;
+ else if (h1 <=4)
+ r = m, g = x + m, b = c + m;
+ else if (h1 <=5)
+ r = x + m, g = m, b = c + m;
+ else
+ r = c + m, g = m, b = x + m;
+
+ return {
+ r: Math.round(r * 255),
+ g: Math.round(g * 255),
+ b: Math.round(b * 255)
+ };
+ }
+
+ // Convert rgb ([0-255, 0-255, 0-255]) to hsv ([0-1, 0-1, 0-1]).
+ // Following algorithm in https://en.wikipedia.org/wiki/HSL_and_HSV
+ // here with h = [0,1] instead of [0, 360]
+ // Accept either (r,g,b) independently or {r:r, g:g, b:b} object.
+ // Return {h:h, s:s, v:v} object.
+ static RGBtoHSV(r, g, b) {
+ if (arguments.length === 1) {
+ r = r.r;
+ g = r.g;
+ b = r.b;
+ }
+
+ let h,s,v;
+
+ let M = Math.max(r, g, b);
+ let m = Math.min(r, g, b);
+ let c = M - m;
+
+ if (c == 0)
+ h = 0;
+ else if (M == r)
+ h = ((g-b)/c) % 6;
+ else if (M == g)
+ h = (b-r)/c + 2;
+ else
+ h = (r-g)/c + 4;
+
+ h = h/6;
+ v = M/255;
+ if (M !== 0)
+ s = c/M;
+ else
+ s = 0;
+
+ return {
+ h: h,
+ s: s,
+ v: v
+ };
+ }
+};
+
+/**
+ * Manage function injection: both instances and prototype can be overridden
+ * and restored
+ */
+var InjectionsHandler = class DashToDock_InjectionsHandler extends BasicHandler {
+
+ _create(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(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;
+}
+
+function drawRoundedLine(cr, x, y, width, height, isRoundLeft, isRoundRight, stroke, fill) {
+ if (height > width) {
+ y += Math.floor((height - width) / 2.0);
+ height = width;
+ }
+
+ height = 2.0 * Math.floor(height / 2.0);
+
+ var leftRadius = isRoundLeft ? height / 2.0 : 0.0;
+ var rightRadius = isRoundRight ? height / 2.0 : 0.0;
+
+ cr.moveTo(x + width - rightRadius, y);
+ cr.lineTo(x + leftRadius, y);
+ if (isRoundLeft)
+ cr.arcNegative(x + leftRadius, y + leftRadius, leftRadius, -Math.PI/2, Math.PI/2);
+ else
+ cr.lineTo(x, y + height);
+ cr.lineTo(x + width - rightRadius, y + height);
+ if (isRoundRight)
+ cr.arcNegative(x + width - rightRadius, y + rightRadius, rightRadius, Math.PI/2, -Math.PI/2);
+ else
+ cr.lineTo(x + width, y);
+ cr.closePath();
+
+ if (fill != null) {
+ cr.setSource(fill);
+ cr.fillPreserve();
+ }
+ if (stroke != null)
+ cr.setSource(stroke);
+ cr.stroke();
+}
diff --git a/extensions/dash-to-dock/windowPreview.js b/extensions/dash-to-dock/windowPreview.js
new file mode 100644
index 0000000..ea98f27
--- /dev/null
+++ b/extensions/dash-to-dock/windowPreview.js
@@ -0,0 +1,578 @@
+/*
+ * 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 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 Me = imports.misc.extensionUtils.getCurrentExtension();
+const Utils = Me.imports.utils;
+
+const PREVIEW_MAX_WIDTH = 250;
+const PREVIEW_MAX_HEIGHT = 150;
+
+var WindowPreviewMenu = class DashToDock_WindowPreviewMenu extends PopupMenu.PopupMenu {
+
+ constructor(source, settings) {
+ let side = Utils.getPosition(settings);
+ super(source.actor, 0.5, side);
+
+ this._dtdSettings = settings;
+
+ // We want to keep the item hovered while the menu is up
+ this.blockSourceEvents = true;
+
+ this._source = source;
+ this._app = this._source.app;
+ let monitorIndex = this._source.monitorIndex;
+
+ 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();
+
+ // Chain our visibility and lifecycle to that of the source
+ this._mappedId = this._source.actor.connect('notify::mapped', () => {
+ if (!this._source.actor.mapped)
+ this.close();
+ });
+ this._destroyId = this._source.actor.connect('destroy', this.destroy.bind(this));
+
+ Main.uiGroup.add_actor(this.actor);
+
+ // Change the initialized side where required.
+ this._arrowSide = side;
+ this._boxPointer._arrowSide = side;
+ this._boxPointer._userArrowSide = side;
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ _redisplay() {
+ if (this._previewBox)
+ this._previewBox.destroy();
+ this._previewBox = new WindowPreviewList(this._source, this._dtdSettings);
+ this.addMenuItem(this._previewBox);
+ this._previewBox._redisplay();
+ }
+
+ popup() {
+ 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');
+ }
+ }
+
+ _onDestroy() {
+ if (this._mappedId)
+ this._source.actor.disconnect(this._mappedId);
+
+ if (this._destroyId)
+ this._source.actor.disconnect(this._destroyId);
+ }
+};
+
+var WindowPreviewList = class DashToDock_WindowPreviewList extends PopupMenu.PopupMenuSection {
+
+ constructor(source, settings) {
+ super();
+ this._dtdSettings = settings;
+
+ this.actor = new St.ScrollView({ name: 'dashtodockWindowScrollview',
+ hscrollbar_policy: Gtk.PolicyType.NEVER,
+ vscrollbar_policy: Gtk.PolicyType.NEVER,
+ enable_mouse_scrolling: true });
+
+ this.actor.connect('scroll-event', this._onScrollEvent.bind(this));
+
+ 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;
+
+ this._shownInitially = false;
+
+ this._source = source;
+ this.app = source.app;
+
+ this._redisplayId = Main.initializeDeferredWork(this.actor, this._redisplay.bind(this));
+
+ this.actor.connect('destroy', this._onDestroy.bind(this));
+ this._stateChangedId = this.app.connect('windows-changed',
+ this._queueRedisplay.bind(this));
+ }
+
+ _queueRedisplay () {
+ Main.queueDeferredWork(this._redisplayId);
+ }
+
+ _onScrollEvent(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;
+
+ // Skip to avoid double events mouse
+ if (event.is_pointer_emulated())
+ return Clutter.EVENT_STOP;
+
+ let adjustment, delta;
+
+ 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;
+
+ }
+
+ adjustment.set_value(adjustment.get_value() + delta);
+
+ return Clutter.EVENT_STOP;
+ }
+
+ _onDestroy() {
+ this.app.disconnect(this._stateChangedId);
+ this._stateChangedId = 0;
+ }
+
+ _createPreviewItem(window) {
+ let preview = new WindowPreviewMenuItem(window);
+ return preview;
+ }
+
+ _redisplay () {
+ let children = this._getMenuItems().filter(function(actor) {
+ return actor._window;
+ });
+
+ // Windows currently on the menu
+ let oldWin = children.map(function(actor) {
+ return actor._window;
+ });
+
+ // All app windows with a static order
+ let newWin = this._source.getInterestingWindows().sort(function(a, b) {
+ return a.get_stable_sequence() > b.get_stable_sequence();
+ });
+
+ let addedItems = [];
+ let removedActors = [];
+
+ let newIndex = 0;
+ let oldIndex = 0;
+
+ while (newIndex < newWin.length || oldIndex < oldWin.length) {
+ // No change at oldIndex/newIndex
+ if (oldWin[oldIndex] &&
+ oldWin[oldIndex] == newWin[newIndex]) {
+ oldIndex++;
+ newIndex++;
+ continue;
+ }
+
+ // Window removed at oldIndex
+ if (oldWin[oldIndex] &&
+ newWin.indexOf(oldWin[oldIndex]) == -1) {
+ removedActors.push(children[oldIndex]);
+ oldIndex++;
+ continue;
+ }
+
+ // Window added at newIndex
+ if (newWin[newIndex] &&
+ oldWin.indexOf(newWin[newIndex]) == -1) {
+ addedItems.push({ item: this._createPreviewItem(newWin[newIndex]),
+ pos: newIndex });
+ newIndex++;
+ continue;
+ }
+
+ // 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);
+
+ if (insertHere || alreadyRemoved) {
+ addedItems.push({ item: this._createPreviewItem(newWin[newIndex]),
+ pos: newIndex + removedActors.length });
+ newIndex++;
+ } else {
+ removedActors.push(children[oldIndex]);
+ oldIndex++;
+ }
+ }
+
+ for (let i = 0; i < addedItems.length; i++)
+ this.addMenuItem(addedItems[i].item,
+ addedItems[i].pos);
+
+ for (let i = 0; i < removedActors.length; i++) {
+ let item = removedActors[i];
+ if (this._shownInitially)
+ item._animateOutAndDestroy();
+ else
+ item.actor.destroy();
+ }
+
+ // Skip animations on first run when adding the initial set
+ // of items, to avoid all items zooming in at once
+ let animate = this._shownInitially;
+
+ 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();
+
+ if (newWin.length < 1)
+ this._getTopMenu().close(~0);
+
+ // As for upstream:
+ // St.ScrollView always requests space horizontally for a possible vertical
+ // scrollbar if in AUTOMATIC mode. Doing better would require implementation
+ // of width-for-height in St.BoxLayout and St.ScrollView. This looks bad
+ // when we *don't* need it, so turn off the scrollbar when that's true.
+ // Dynamic changes in whether we need it aren't handled properly.
+ let needsScrollbar = this._needsScrollbar();
+ let scrollbar_policy = needsScrollbar ? Gtk.PolicyType.AUTOMATIC : Gtk.PolicyType.NEVER;
+ if (this.isHorizontal)
+ this.actor.hscrollbar_policy = scrollbar_policy;
+ else
+ this.actor.vscrollbar_policy = scrollbar_policy;
+
+ if (needsScrollbar)
+ this.actor.add_style_pseudo_class('scrolled');
+ else
+ this.actor.remove_style_pseudo_class('scrolled');
+ }
+
+ _needsScrollbar() {
+ let topMenu = this._getTopMenu();
+ let topThemeNode = topMenu.actor.get_theme_node();
+ if (this.isHorizontal) {
+ let [topMinWidth, topNaturalWidth] = topMenu.actor.get_preferred_width(-1);
+ let topMaxWidth = topThemeNode.get_max_width();
+ return topMaxWidth >= 0 && topNaturalWidth >= topMaxWidth;
+ } else {
+ let [topMinHeight, topNaturalHeight] = topMenu.actor.get_preferred_height(-1);
+ let topMaxHeight = topThemeNode.get_max_height();
+ return topMaxHeight >= 0 && topNaturalHeight >= topMaxHeight;
+ }
+
+ }
+
+ isAnimatingOut() {
+ return this.actor.get_children().reduce(function(result, actor) {
+ return result || actor.animatingOut;
+ }, false);
+ }
+};
+
+var WindowPreviewMenuItem = class DashToDock_WindowPreviewMenuItem extends PopupMenu.PopupBaseMenuItem {
+
+ constructor(window, params) {
+ super(params);
+
+ this._window = window;
+ this._destroyId = 0;
+ this._windowAddedId = 0;
+
+ // We don't want this: it adds spacing on the left of the item.
+ this.actor.remove_child(this._ornamentLabel);
+ this.actor.add_style_class_name('dashtodock-app-well-preview-menu-item');
+
+ 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-bottom: 0.5em');
+
+ this.closeButton = new St.Button({ style_class: 'window-close',
+ x_expand: true,
+ y_expand: true});
+ this.closeButton.add_actor(new St.Icon({ icon_name: 'window-close-symbolic' }));
+ this.closeButton.set_x_align(Clutter.ActorAlign.END);
+ this.closeButton.set_y_align(Clutter.ActorAlign.START);
+
+
+ this.closeButton.opacity = 0;
+ this.closeButton.connect('clicked', this._closeWindow.bind(this));
+
+ 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', () => {
+ 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',
+ this._onEnter.bind(this));
+ this.actor.connect('leave-event',
+ this._onLeave.bind(this));
+ this.actor.connect('key-focus-in',
+ this._onEnter.bind(this));
+ this.actor.connect('key-focus-out',
+ this._onLeave.bind(this));
+
+ this._cloneTexture(window);
+
+ }
+
+ _cloneTexture(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(() => {
+ // 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;
+ }
+
+ let windowTexture = mutterWindow.get_texture();
+ let [width, height] = windowTexture.get_size();
+
+ let scale = Math.min(1.0, PREVIEW_MAX_WIDTH/width, PREVIEW_MAX_HEIGHT/height);
+
+ let clone = new Clutter.Clone ({ source: windowTexture,
+ reactive: true,
+ width: width * scale,
+ height: height * scale });
+
+ // 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', () => {
+ clone.destroy();
+ this._destroyId = 0; // avoid to try to disconnect this signal from mutterWindow in _onDestroy(),
+ // as the object was just destroyed
+ this._animateOutAndDestroy();
+ });
+
+ this._clone = clone;
+ this._mutterWindow = mutterWindow;
+ this._cloneBin.set_child(this._clone);
+ }
+
+ _windowCanClose() {
+ return this._window.can_close() &&
+ !this._hasAttachedDialogs();
+ }
+
+ _closeWindow(actor) {
+ this._workspace = this._window.get_workspace();
+
+ // 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',
+ this._onWindowAdded.bind(this));
+
+ this.deleteAllWindows();
+ }
+
+ deleteAllWindows() {
+ // 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;
+
+ metaWindow.delete(global.get_current_time());
+ }
+
+ this._window.delete(global.get_current_time());
+ }
+
+ _onWindowAdded(workspace, win) {
+ let metaWindow = this._window;
+
+ if (win.get_transient_for() == metaWindow) {
+ workspace.disconnect(this._windowAddedId);
+ this._windowAddedId = 0;
+
+ // use an idle handler to avoid mapping problems -
+ // see comment in Workspace._windowAdded
+ let id = Mainloop.idle_add(() => {
+ this.emit('activate');
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(id, '[dash-to-dock] this.emit');
+ }
+ }
+
+ _hasAttachedDialogs() {
+ // count trasient windows
+ let n=0;
+ this._window.foreach_transient(function(){n++;});
+ return n>0;
+ }
+
+ _onEnter() {
+ this._showCloseButton();
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _onLeave() {
+ if (!this._cloneBin.has_pointer &&
+ !this.closeButton.has_pointer)
+ this._hideCloseButton();
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _idleToggleCloseButton() {
+ this._idleToggleCloseId = 0;
+
+ if (!this._cloneBin.has_pointer &&
+ !this.closeButton.has_pointer)
+ this._hideCloseButton();
+
+ return GLib.SOURCE_REMOVE;
+ }
+
+ _showCloseButton() {
+
+ if (this._windowCanClose()) {
+ this.closeButton.show();
+ Tweener.addTween(this.closeButton,
+ { opacity: 255,
+ time: Workspace.CLOSE_BUTTON_FADE_TIME,
+ transition: 'easeOutQuad' });
+ }
+ }
+
+ _hideCloseButton() {
+ Tweener.addTween(this.closeButton,
+ { opacity: 0,
+ time: Workspace.CLOSE_BUTTON_FADE_TIME,
+ transition: 'easeInQuad' });
+ }
+
+ show(animate) {
+ let fullWidth = this.actor.get_width();
+
+ this.actor.opacity = 0;
+ this.actor.set_width(0);
+
+ let time = animate ? 0.25 : 0;
+ Tweener.addTween(this.actor,
+ { opacity: 255,
+ width: fullWidth,
+ time: time,
+ transition: 'easeInOutQuad'
+ });
+ }
+
+ _animateOutAndDestroy() {
+ Tweener.addTween(this.actor,
+ { opacity: 0,
+ time: 0.25,
+ });
+
+ Tweener.addTween(this.actor,
+ { height: 0,
+ width: 0,
+ time: 0.25,
+ delay: 0.25,
+ onCompleteScope: this,
+ onComplete() {
+ this.actor.destroy();
+ }
+ });
+ }
+
+ activate() {
+ this._getTopMenu().close();
+ Main.activateWindow(this._window);
+ }
+
+ _onDestroy() {
+ super._onDestroy();
+
+ 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;
+ }
+ }
+};
+
diff --git a/meson.build b/meson.build
index 6050c32..2909135 100644
--- a/meson.build
+++ b/meson.build
@@ -49,6 +49,7 @@ default_extensions += [
all_extensions = default_extensions
all_extensions += [
'auto-move-windows',
+ 'dash-to-dock',
'native-window-placement',
'top-icons',
'user-theme'
--
2.21.0
From 0baadb623f45f5dfa9449e3bd0aa1ee3880852c3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 20 May 2015 18:55:47 +0200
Subject: [PATCH 3/8] Add panel-favorites extension
---
extensions/panel-favorites/extension.js | 267 ++++++++++++++++++++
extensions/panel-favorites/meson.build | 5 +
extensions/panel-favorites/metadata.json.in | 10 +
extensions/panel-favorites/stylesheet.css | 14 +
meson.build | 1 +
5 files changed, 297 insertions(+)
create mode 100644 extensions/panel-favorites/extension.js
create mode 100644 extensions/panel-favorites/meson.build
create mode 100644 extensions/panel-favorites/metadata.json.in
create mode 100644 extensions/panel-favorites/stylesheet.css
diff --git a/extensions/panel-favorites/extension.js b/extensions/panel-favorites/extension.js
new file mode 100644
index 0000000..b817dbb
--- /dev/null
+++ b/extensions/panel-favorites/extension.js
@@ -0,0 +1,267 @@
+// Copyright (C) 2011-2013 R M Yorston
+// Licence: GPLv2+
+
+const Clutter = imports.gi.Clutter;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Lang = imports.lang;
+const Shell = imports.gi.Shell;
+const Signals = imports.signals;
+const St = imports.gi.St;
+const Mainloop = imports.mainloop;
+
+const AppFavorites = imports.ui.appFavorites;
+const Main = imports.ui.main;
+const Panel = imports.ui.panel;
+const Tweener = imports.ui.tweener;
+
+const PANEL_LAUNCHER_LABEL_SHOW_TIME = 0.15;
+const PANEL_LAUNCHER_LABEL_HIDE_TIME = 0.1;
+const PANEL_LAUNCHER_HOVER_TIMEOUT = 300;
+
+const PanelLauncher = new Lang.Class({
+ Name: 'PanelLauncher',
+
+ _init: function(app) {
+ this.actor = new St.Button({ style_class: 'panel-button',
+ reactive: true });
+ this.iconSize = 24;
+ let icon = app.create_icon_texture(this.iconSize);
+ this.actor.set_child(icon);
+ this.actor._delegate = this;
+ let text = app.get_name();
+ if ( app.get_description() ) {
+ text += '\n' + app.get_description();
+ }
+
+ this.label = new St.Label({ style_class: 'panel-launcher-label'});
+ this.label.set_text(text);
+ Main.layoutManager.addChrome(this.label);
+ this.label.hide();
+ this.actor.label_actor = this.label;
+
+ this._app = app;
+ this.actor.connect('clicked', Lang.bind(this, function() {
+ this._app.open_new_window(-1);
+ }));
+ this.actor.connect('notify::hover',
+ Lang.bind(this, this._onHoverChanged));
+ this.actor.opacity = 207;
+
+ this.actor.connect('notify::allocation', Lang.bind(this, this._alloc));
+ },
+
+ _onHoverChanged: function(actor) {
+ actor.opacity = actor.hover ? 255 : 207;
+ },
+
+ _alloc: function() {
+ let size = this.actor.allocation.y2 - this.actor.allocation.y1 - 3;
+ if ( size >= 24 && size != this.iconSize ) {
+ this.actor.get_child().destroy();
+ this.iconSize = size;
+ let icon = this._app.create_icon_texture(this.iconSize);
+ this.actor.set_child(icon);
+ }
+ },
+
+ showLabel: function() {
+ this.label.opacity = 0;
+ this.label.show();
+
+ let [stageX, stageY] = this.actor.get_transformed_position();
+
+ let itemHeight = this.actor.allocation.y2 - this.actor.allocation.y1;
+ let itemWidth = this.actor.allocation.x2 - this.actor.allocation.x1;
+ let labelWidth = this.label.get_width();
+
+ let node = this.label.get_theme_node();
+ let yOffset = node.get_length('-y-offset');
+
+ let y = stageY + itemHeight + yOffset;
+ let x = Math.floor(stageX + itemWidth/2 - labelWidth/2);
+
+ let parent = this.label.get_parent();
+ let parentWidth = parent.allocation.x2 - parent.allocation.x1;
+
+ if ( Clutter.get_default_text_direction() == Clutter.TextDirection.LTR ) {
+ // stop long tooltips falling off the right of the screen
+ x = Math.min(x, parentWidth-labelWidth-6);
+ // but whatever happens don't let them fall of the left
+ x = Math.max(x, 6);
+ }
+ else {
+ x = Math.max(x, 6);
+ x = Math.min(x, parentWidth-labelWidth-6);
+ }
+
+ this.label.set_position(x, y);
+ Tweener.addTween(this.label,
+ { opacity: 255,
+ time: PANEL_LAUNCHER_LABEL_SHOW_TIME,
+ transition: 'easeOutQuad',
+ });
+ },
+
+ hideLabel: function() {
+ this.label.opacity = 255;
+ Tweener.addTween(this.label,
+ { opacity: 0,
+ time: PANEL_LAUNCHER_LABEL_HIDE_TIME,
+ transition: 'easeOutQuad',
+ onComplete: Lang.bind(this, function() {
+ this.label.hide();
+ })
+ });
+ },
+
+ destroy: function() {
+ this.label.destroy();
+ this.actor.destroy();
+ }
+});
+
+const PanelFavorites = new Lang.Class({
+ Name: 'PanelFavorites',
+
+ _init: function() {
+ this._showLabelTimeoutId = 0;
+ this._resetHoverTimeoutId = 0;
+ this._labelShowing = false;
+
+ this.actor = new St.BoxLayout({ name: 'panelFavorites',
+ x_expand: true, y_expand: true,
+ style_class: 'panel-favorites' });
+ this._display();
+
+ this.container = new St.Bin({ y_fill: true,
+ x_fill: true,
+ child: this.actor });
+
+ this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+ this._installChangedId = Shell.AppSystem.get_default().connect('installed-changed', Lang.bind(this, this._redisplay));
+ this._changedId = AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._redisplay));
+ },
+
+ _redisplay: function() {
+ for ( let i=0; i<this._buttons.length; ++i ) {
+ this._buttons[i].destroy();
+ }
+
+ this._display();
+ },
+
+ _display: function() {
+ let launchers = global.settings.get_strv(AppFavorites.getAppFavorites().FAVORITE_APPS_KEY);
+
+ this._buttons = [];
+ let j = 0;
+ for ( let i=0; i<launchers.length; ++i ) {
+ let app = Shell.AppSystem.get_default().lookup_app(launchers[i]);
+
+ if ( app == null ) {
+ continue;
+ }
+
+ let launcher = new PanelLauncher(app);
+ this.actor.add(launcher.actor);
+ launcher.actor.connect('notify::hover',
+ Lang.bind(this, function() {
+ this._onHover(launcher);
+ }));
+ this._buttons[j] = launcher;
+ ++j;
+ }
+ },
+
+ // this routine stolen from dash.js
+ _onHover: function(launcher) {
+ if ( launcher.actor.hover ) {
+ if (this._showLabelTimeoutId == 0) {
+ let timeout = this._labelShowing ?
+ 0 : PANEL_LAUNCHER_HOVER_TIMEOUT;
+ this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
+ Lang.bind(this, function() {
+ this._labelShowing = true;
+ launcher.showLabel();
+ this._showLabelTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ }));
+ 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;
+ }
+ launcher.hideLabel();
+ if (this._labelShowing) {
+ this._resetHoverTimeoutId = Mainloop.timeout_add(
+ PANEL_LAUNCHER_HOVER_TIMEOUT,
+ Lang.bind(this, function() {
+ this._labelShowing = false;
+ this._resetHoverTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ }));
+ }
+ }
+ },
+
+ _onDestroy: function() {
+ if ( this._installChangedId != 0 ) {
+ Shell.AppSystem.get_default().disconnect(this._installChangedId);
+ this._installChangedId = 0;
+ }
+
+ if ( this._changedId != 0 ) {
+ AppFavorites.getAppFavorites().disconnect(this._changedId);
+ this._changedId = 0;
+ }
+ }
+});
+Signals.addSignalMethods(PanelFavorites.prototype);
+
+let myAddToStatusArea;
+let panelFavorites;
+
+function enable() {
+ Panel.Panel.prototype.myAddToStatusArea = myAddToStatusArea;
+
+ // place panel to left of app menu, or failing that at right end of box
+ let siblings = Main.panel._leftBox.get_children();
+ let appMenu = Main.panel.statusArea['appMenu'];
+ let pos = appMenu ? siblings.indexOf(appMenu.container) : siblings.length;
+
+ panelFavorites = new PanelFavorites();
+ Main.panel.myAddToStatusArea('panel-favorites', panelFavorites,
+ pos, 'left');
+}
+
+function disable() {
+ delete Panel.Panel.prototype.myAddToStatusArea;
+
+ panelFavorites.actor.destroy();
+ panelFavorites.emit('destroy');
+ panelFavorites = null;
+}
+
+function init() {
+ myAddToStatusArea = function(role, indicator, position, box) {
+ if (this.statusArea[role])
+ throw new Error('Extension point conflict: there is already a status indicator for role ' + role);
+
+ position = position || 0;
+ let boxes = {
+ left: this._leftBox,
+ center: this._centerBox,
+ right: this._rightBox
+ };
+ let boxContainer = boxes[box] || this._rightBox;
+ this.statusArea[role] = indicator;
+ this._addToPanelBox(role, indicator, position, boxContainer);
+ return indicator;
+ };
+}
diff --git a/extensions/panel-favorites/meson.build b/extensions/panel-favorites/meson.build
new file mode 100644
index 0000000..48504f6
--- /dev/null
+++ b/extensions/panel-favorites/meson.build
@@ -0,0 +1,5 @@
+extension_data += configure_file(
+ input: metadata_name + '.in',
+ output: metadata_name,
+ configuration: metadata_conf
+)
diff --git a/extensions/panel-favorites/metadata.json.in b/extensions/panel-favorites/metadata.json.in
new file mode 100644
index 0000000..037f281
--- /dev/null
+++ b/extensions/panel-favorites/metadata.json.in
@@ -0,0 +1,10 @@
+{
+"extension-id": "@extension_id@",
+"uuid": "@uuid@",
+"settings-schema": "@gschemaname@",
+"gettext-domain": "@gettext_domain@",
+"name": "Frippery Panel Favorites",
+"description": "Add launchers for Favorites to the panel",
+"shell-version": [ "@shell_current@" ],
+"url": "http://intgat.tigress.co.uk/rmy/extensions/index.html"
+}
diff --git a/extensions/panel-favorites/stylesheet.css b/extensions/panel-favorites/stylesheet.css
new file mode 100644
index 0000000..120adac
--- /dev/null
+++ b/extensions/panel-favorites/stylesheet.css
@@ -0,0 +1,14 @@
+.panel-favorites {
+ spacing: 6px;
+}
+
+.panel-launcher-label {
+ border-radius: 7px;
+ padding: 4px 12px;
+ background-color: rgba(0,0,0,0.9);
+ color: white;
+ text-align: center;
+ font-size: 9pt;
+ font-weight: bold;
+ -y-offset: 6px;
+}
diff --git a/meson.build b/meson.build
index 2909135..e8e00dc 100644
--- a/meson.build
+++ b/meson.build
@@ -51,6 +51,7 @@ all_extensions += [
'auto-move-windows',
'dash-to-dock',
'native-window-placement',
+ 'panel-favorites',
'top-icons',
'user-theme'
]
--
2.21.0
From ce4282836905117970b842a5504a92d8b6ea7dfe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Fri, 4 Mar 2016 17:07:21 +0100
Subject: [PATCH 4/8] Add updates-dialog extension
---
extensions/updates-dialog/extension.js | 503 ++++++++++++++++++
extensions/updates-dialog/meson.build | 7 +
extensions/updates-dialog/metadata.json.in | 10 +
...hell.extensions.updates-dialog.gschema.xml | 30 ++
extensions/updates-dialog/stylesheet.css | 1 +
meson.build | 1 +
po/POTFILES.in | 2 +
7 files changed, 554 insertions(+)
create mode 100644 extensions/updates-dialog/extension.js
create mode 100644 extensions/updates-dialog/meson.build
create mode 100644 extensions/updates-dialog/metadata.json.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/extensions/updates-dialog/extension.js b/extensions/updates-dialog/extension.js
new file mode 100644
index 0000000..59f6dcf
--- /dev/null
+++ b/extensions/updates-dialog/extension.js
@@ -0,0 +1,503 @@
+/*
+ * Copyright (c) 2015 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* exported enable disable */
+
+const { Clutter, Gio, GLib, PackageKitGlib: PkgKit, Pango, Polkit, St } = imports.gi;
+const Signals = imports.signals;
+
+const EndSessionDialog = imports.ui.endSessionDialog;
+const ModalDialog = imports.ui.modalDialog;
+
+const ExtensionUtils = imports.misc.extensionUtils;
+const Me = ExtensionUtils.getCurrentExtension();
+
+const PkIface = '<node> \
+<interface name="org.freedesktop.PackageKit"> \
+ <method name="CreateTransaction"> \
+ <arg type="o" name="object_path" direction="out"/> \
+ </method> \
+ <signal name="UpdatesChanged"/> \
+</interface> \
+</node>';
+
+const PkOfflineIface = '<node> \
+<interface name="org.freedesktop.PackageKit.Offline"> \
+ <property name="UpdatePrepared" type="b" access="read"/> \
+ <property name="TriggerAction" type="s" access="read"/> \
+ <method name="Trigger"> \
+ <arg type="s" name="action" direction="in"/> \
+ </method> \
+ <method name="Cancel"/> \
+</interface> \
+</node>';
+
+const PkTransactionIface = '<node> \
+<interface name="org.freedesktop.PackageKit.Transaction"> \
+ <method name="SetHints"> \
+ <arg type="as" name="hints" direction="in"/> \
+ </method> \
+ <method name="GetUpdates"> \
+ <arg type="t" name="filter" direction="in"/> \
+ </method> \
+ <method name="UpdatePackages"> \
+ <arg type="t" name="transaction_flags" direction="in"/> \
+ <arg type="as" name="package_ids" direction="in"/> \
+ </method> \
+ <signal name="Package"> \
+ <arg type="u" name="info" direction="out"/> \
+ <arg type="s" name="package_id" direction="out"/> \
+ <arg type="s" name="summary" direction="out"/> \
+ </signal> \
+ <signal name="Finished"> \
+ <arg type="u" name="exit" direction="out"/> \
+ <arg type="u" name="runtime" direction="out"/> \
+ </signal> \
+</interface> \
+</node>';
+
+const LoginManagerIface = '<node> \
+<interface name="org.freedesktop.login1.Manager"> \
+<method name="Reboot"> \
+ <arg type="b" direction="in"/> \
+</method> \
+<method name="CanReboot"> \
+ <arg type="s" direction="out"/> \
+</method> \
+</interface> \
+</node>';
+
+const PkProxy = Gio.DBusProxy.makeProxyWrapper(PkIface);
+const PkOfflineProxy = Gio.DBusProxy.makeProxyWrapper(PkOfflineIface);
+const PkTransactionProxy = Gio.DBusProxy.makeProxyWrapper(PkTransactionIface);
+const LoginManagerProxy = Gio.DBusProxy.makeProxyWrapper(LoginManagerIface);
+
+let pkProxy = null;
+let pkOfflineProxy = null;
+let loginManagerProxy = null;
+let updatesDialog = null;
+let extensionSettings = null;
+let cancellable = null;
+
+let updatesCheckInProgress = false;
+let updatesCheckRequested = false;
+let securityUpdates = [];
+
+function getDetailText(period) {
+ let text = _('Important security updates need to be installed.\n');
+ if (period < 60) {
+ text += ngettext(
+ 'You can close this dialog and get %d minute to finish your work.',
+ 'You can close this dialog and get %d minutes to finish your work.',
+ period)
+ .format(period);
+ } else {
+ text += ngettext(
+ 'You can close this dialog and get %d hour to finish your work.',
+ 'You can close this dialog and get %d hours to finish your work.',
+ Math.floor(period / 60))
+ .format(Math.floor(period / 60));
+ }
+ return text;
+}
+
+const UpdatesDialog = class extends ModalDialog.ModalDialog {
+ constructor(settings) {
+ super({
+ styleClass: 'end-session-dialog',
+ destroyOnClose: false
+ });
+
+ this._gracePeriod = settings.get_uint('grace-period');
+ this._gracePeriod = Math.min(Math.max(10, this._gracePeriod), 24 * 60);
+ this._lastWarningPeriod = settings.get_uint('last-warning-period');
+ this._lastWarningPeriod = Math.min(
+ Math.max(1, this._lastWarningPeriod),
+ this._gracePeriod - 1);
+ this._lastWarnings = settings.get_uint('last-warnings');
+ this._lastWarnings = Math.min(
+ Math.max(1, this._lastWarnings),
+ Math.floor((this._gracePeriod - 1) / this._lastWarningPeriod));
+
+ let messageLayout = new St.BoxLayout({
+ vertical: true,
+ style_class: 'end-session-dialog-layout'
+ });
+ this.contentLayout.add(messageLayout, {
+ x_fill: true,
+ y_fill: true,
+ y_expand: true
+ });
+
+ let subjectLabel = new St.Label({
+ style_class: 'end-session-dialog-subject',
+ style: 'padding-bottom: 1em;',
+ text: _('Important security updates')
+ });
+ messageLayout.add(subjectLabel, {
+ x_fill: false,
+ y_fill: false,
+ x_align: St.Align.START,
+ y_align: St.Align.START
+ });
+
+ this._detailLabel = new St.Label({
+ style_class: 'end-session-dialog-description',
+ style: 'padding-bottom: 0em;',
+ text: getDetailText(this._gracePeriod)
+ });
+ this._detailLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ this._detailLabel.clutter_text.line_wrap = true;
+
+ messageLayout.add(this._detailLabel, {
+ y_fill: true,
+ y_align: St.Align.START
+ });
+
+ let buttons = [{
+ action: this.close.bind(this),
+ label: _('Close'),
+ key: Clutter.Escape
+ }, {
+ action: this._done.bind(this),
+ label: _('Restart & Install')
+ }];
+
+ this.setButtons(buttons);
+
+ this._openTimeoutId = 0;
+ this.connect('destroy', this._clearOpenTimeout.bind(this));
+
+ this._startTimer();
+ }
+
+ _clearOpenTimeout() {
+ if (this._openTimeoutId > 0) {
+ GLib.source_remove(this._openTimeoutId);
+ this._openTimeoutId = 0;
+ }
+ }
+
+ tryOpen() {
+ if (this._openTimeoutId > 0 || this.open())
+ return;
+
+ this._openTimeoutId = GLib.timeout_add_seconds(
+ GLib.PRIORITY_DEFAULT, 1, () => {
+ if (!this.open())
+ return GLib.SOURCE_CONTINUE;
+
+ this._clearOpenTimeout();
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ _startTimer() {
+ this._secondsLeft = this._gracePeriod * 60;
+
+ this._timerId = GLib.timeout_add_seconds(
+ GLib.PRIORITY_DEFAULT, 1, () => {
+ this._secondsLeft -= 1;
+ let minutesLeft = this._secondsLeft / 60;
+ let periodLeft = Math.floor(minutesLeft);
+
+ if (this._secondsLeft == 60 ||
+ (periodLeft > 0 && periodLeft <= this._lastWarningPeriod * this._lastWarnings &&
+ minutesLeft % this._lastWarningPeriod == 0)) {
+ this.tryOpen();
+ this._detailLabel.text = getDetailText(periodLeft);
+ }
+
+ if (this._secondsLeft > 0) {
+ if (this._secondsLeft < 60) {
+ let seconds = EndSessionDialog._roundSecondsToInterval(
+ this._gracePeriod * 60, this._secondsLeft, 10);
+ this._detailLabel.text =
+ _('Important security updates need to be installed now.\n') +
+ ngettext(
+ 'This computer will restart in %d second.',
+ 'This computer will restart in %d seconds.',
+ seconds).format(seconds);
+ }
+ return GLib.SOURCE_CONTINUE;
+ }
+
+ this._done();
+ return GLib.SOURCE_REMOVE;
+ });
+ this.connect('destroy', () => {
+ if (this._timerId > 0) {
+ GLib.source_remove(this._timerId);
+ this._timerId = 0;
+ }
+ });
+ }
+
+ _done() {
+ this.emit('done');
+ this.destroy();
+ }
+
+ getState() {
+ return [this._gracePeriod, this._lastWarningPeriod, this._lastWarnings, this._secondsLeft];
+ }
+
+ setState(state) {
+ [this._gracePeriod, this._lastWarningPeriod, this._lastWarnings, this._secondsLeft] = state;
+ }
+};
+Signals.addSignalMethods(UpdatesDialog.prototype);
+
+function showDialog() {
+ if (updatesDialog)
+ return;
+
+ updatesDialog = new UpdatesDialog(extensionSettings);
+ updatesDialog.tryOpen();
+ updatesDialog.connect('destroy', () => updatesDialog = null);
+ updatesDialog.connect('done', () => {
+ if (pkOfflineProxy.TriggerAction == 'power-off' ||
+ pkOfflineProxy.TriggerAction == 'reboot') {
+ loginManagerProxy.RebootRemote(false);
+ } else {
+ pkOfflineProxy.TriggerRemote('reboot', (result, error) => {
+ if (!error)
+ loginManagerProxy.RebootRemote(false);
+ else
+ log('Failed to trigger offline update: %s'.format(error.message));
+ });
+ }
+ });
+}
+
+function cancelDialog(save) {
+ if (!updatesDialog)
+ return;
+
+ if (save) {
+ let state = GLib.Variant.new('(uuuu)', updatesDialog.getState());
+ global.set_runtime_state(Me.uuid, state);
+ }
+ updatesDialog.destroy();
+}
+
+function restoreExistingState() {
+ let state = global.get_runtime_state('(uuuu)', Me.uuid);
+ if (state === null)
+ return false;
+
+ global.set_runtime_state(Me.uuid, null);
+ showDialog();
+ updatesDialog.setState(state.deep_unpack());
+ return true;
+}
+
+function syncState() {
+ if (!pkOfflineProxy || !loginManagerProxy)
+ return;
+
+ if (restoreExistingState())
+ return;
+
+ if (!updatesCheckInProgress &&
+ securityUpdates.length > 0 &&
+ pkOfflineProxy.UpdatePrepared)
+ showDialog();
+ else
+ cancelDialog();
+}
+
+function doPkTransaction(callback) {
+ if (!pkProxy)
+ return;
+
+ pkProxy.CreateTransactionRemote((result, error) => {
+ if (error) {
+ log('Error creating PackageKit transaction: %s'.format(error.message));
+ checkUpdatesDone();
+ return;
+ }
+
+ new PkTransactionProxy(Gio.DBus.system,
+ 'org.freedesktop.PackageKit',
+ String(result),
+ (proxy, error) => {
+ if (!error) {
+ proxy.SetHintsRemote(
+ ['background=true', 'interactive=false'],
+ (result, error) => {
+ if (error) {
+ log('Error connecting to PackageKit: %s'.format(error.message));
+ checkUpdatesDone();
+ return;
+ }
+ callback(proxy);
+ });
+ } else {
+ log('Error connecting to PackageKit: %s'.format(error.message));
+ }
+ });
+ });
+}
+
+function pkUpdatePackages(proxy) {
+ proxy.connectSignal('Finished', (p, e, params) => {
+ let [exit, runtime_] = params;
+
+ if (exit == PkgKit.ExitEnum.CANCELLED_PRIORITY) {
+ // try again
+ checkUpdates();
+ } else if (exit != PkgKit.ExitEnum.SUCCESS) {
+ log('UpdatePackages failed: %s'.format(PkgKit.ExitEnum.to_string(exit)));
+ }
+
+ checkUpdatesDone();
+ });
+ proxy.UpdatePackagesRemote(1 << PkgKit.TransactionFlagEnum.ONLY_DOWNLOAD, securityUpdates);
+}
+
+function pkGetUpdates(proxy) {
+ proxy.connectSignal('Package', (p, e, params) => {
+ let [info, packageId, summary_] = params;
+
+ if (info == PkgKit.InfoEnum.SECURITY)
+ securityUpdates.push(packageId);
+ });
+ proxy.connectSignal('Finished', (p, e, params) => {
+ let [exit, runtime_] = params;
+
+ if (exit == PkgKit.ExitEnum.SUCCESS) {
+ if (securityUpdates.length > 0) {
+ doPkTransaction(pkUpdatePackages);
+ return;
+ }
+ } else if (exit == PkgKit.ExitEnum.CANCELLED_PRIORITY) {
+ // try again
+ checkUpdates();
+ } else {
+ log('GetUpdates failed: %s'.format(PkgKit.ExitEnum.to_string(exit)));
+ }
+
+ checkUpdatesDone();
+ });
+ proxy.GetUpdatesRemote(0);
+}
+
+function checkUpdatesDone() {
+ updatesCheckInProgress = false;
+ if (updatesCheckRequested) {
+ updatesCheckRequested = false;
+ checkUpdates();
+ } else {
+ syncState();
+ }
+}
+
+function checkUpdates() {
+ if (updatesCheckInProgress) {
+ updatesCheckRequested = true;
+ return;
+ }
+ updatesCheckInProgress = true;
+ securityUpdates = [];
+ doPkTransaction(pkGetUpdates);
+}
+
+function initSystemProxies() {
+ new PkProxy(Gio.DBus.system,
+ 'org.freedesktop.PackageKit',
+ '/org/freedesktop/PackageKit',
+ (proxy, error) => {
+ if (!error) {
+ pkProxy = proxy;
+ let id = pkProxy.connectSignal('UpdatesChanged', checkUpdates);
+ pkProxy._signalId = id;
+ checkUpdates();
+ } else {
+ log('Error connecting to PackageKit: %s'.format(error.message));
+ }
+ },
+ cancellable);
+ new PkOfflineProxy(Gio.DBus.system,
+ 'org.freedesktop.PackageKit',
+ '/org/freedesktop/PackageKit',
+ (proxy, error) => {
+ if (!error) {
+ pkOfflineProxy = proxy;
+ let id = pkOfflineProxy.connect('g-properties-changed', syncState);
+ pkOfflineProxy._signalId = id;
+ syncState();
+ } else {
+ log('Error connecting to PackageKit: %s'.format(error.message));
+ }
+ },
+ cancellable);
+ new LoginManagerProxy(Gio.DBus.system,
+ 'org.freedesktop.login1',
+ '/org/freedesktop/login1',
+ (proxy, error) => {
+ if (!error) {
+ proxy.CanRebootRemote(cancellable, (result, error) => {
+ if (!error && result == 'yes') {
+ loginManagerProxy = proxy;
+ syncState();
+ } else {
+ log('Reboot is not available');
+ }
+ });
+ } else {
+ log('Error connecting to Login manager: %s'.format(error.message));
+ }
+ },
+ cancellable);
+}
+
+function enable() {
+ cancellable = new Gio.Cancellable();
+ extensionSettings = ExtensionUtils.getSettings();
+ Polkit.Permission.new('org.freedesktop.packagekit.trigger-offline-update',
+ null,
+ cancellable,
+ (p, result) => {
+ try {
+ let permission = Polkit.Permission.new_finish(result);
+ if (permission && permission.allowed)
+ initSystemProxies();
+ else
+ throw (new Error('not allowed'));
+ } catch (e) {
+ log('No permission to trigger offline updates: %s'.format(e.toString()));
+ }
+ });
+}
+
+function disable() {
+ cancelDialog(true);
+ cancellable.cancel();
+ cancellable = null;
+ extensionSettings = null;
+ updatesDialog = null;
+ loginManagerProxy = null;
+ if (pkOfflineProxy) {
+ pkOfflineProxy.disconnect(pkOfflineProxy._signalId);
+ pkOfflineProxy = null;
+ }
+ if (pkProxy) {
+ pkProxy.disconnectSignal(pkProxy._signalId);
+ pkProxy = null;
+ }
+}
diff --git a/extensions/updates-dialog/meson.build b/extensions/updates-dialog/meson.build
new file mode 100644
index 0000000..585c02d
--- /dev/null
+++ b/extensions/updates-dialog/meson.build
@@ -0,0 +1,7 @@
+extension_data += configure_file(
+ input: metadata_name + '.in',
+ output: metadata_name,
+ configuration: metadata_conf
+)
+
+extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
diff --git a/extensions/updates-dialog/metadata.json.in b/extensions/updates-dialog/metadata.json.in
new file mode 100644
index 0000000..9946abb
--- /dev/null
+++ b/extensions/updates-dialog/metadata.json.in
@@ -0,0 +1,10 @@
+{
+"extension-id": "@extension_id@",
+"uuid": "@uuid@",
+"settings-schema": "@gschemaname@",
+"gettext-domain": "@gettext_domain@",
+"name": "Updates Dialog",
+"description": "Shows a modal dialog when there are software updates.",
+"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 b/extensions/updates-dialog/org.gnome.shell.extensions.updates-dialog.gschema.xml
new file mode 100644
index 0000000..c08d33c
--- /dev/null
+++ b/extensions/updates-dialog/org.gnome.shell.extensions.updates-dialog.gschema.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist>
+ <schema path="/org/gnome/shell/extensions/updates-dialog/"
+ id="org.gnome.shell.extensions.updates-dialog">
+ <key name="grace-period" type="u">
+ <default>300</default>
+ <summary>Grace period in minutes</summary>
+ <description>
+ When the grace period is over, the computer will automatically
+ reboot and install security updates.
+ </description>
+ </key>
+ <key name="last-warning-period" type="u">
+ <default>10</default>
+ <summary>Last warning dialog period</summary>
+ <description>
+ A last warning dialog is displayed this many minutes before
+ the automatic reboot.
+ </description>
+ </key>
+ <key name="last-warnings" type="u">
+ <default>1</default>
+ <summary>Number of last warning dialogs</summary>
+ <description>
+ How many warning dialogs are displayed. Each is displayed at
+ 'last-warning-period' minute intervals.
+ </description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/extensions/updates-dialog/stylesheet.css b/extensions/updates-dialog/stylesheet.css
new file mode 100644
index 0000000..25134b6
--- /dev/null
+++ b/extensions/updates-dialog/stylesheet.css
@@ -0,0 +1 @@
+/* This extensions requires no special styling */
diff --git a/meson.build b/meson.build
index e8e00dc..d129e6c 100644
--- a/meson.build
+++ b/meson.build
@@ -53,6 +53,7 @@ all_extensions += [
'native-window-placement',
'panel-favorites',
'top-icons',
+ 'updates-dialog',
'user-theme'
]
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9c1438a..55f0e9a 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -10,6 +10,8 @@ extensions/native-window-placement/org.gnome.shell.extensions.native-window-plac
extensions/places-menu/extension.js
extensions/places-menu/placeDisplay.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
extensions/user-theme/extension.js
extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml
extensions/window-list/extension.js
--
2.21.0
From 75c1b568c0bc93c1218fdd1d9d1caa4b9c1d723e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Thu, 1 Jun 2017 23:57:14 +0200
Subject: [PATCH 5/8] Add no-hot-corner extension
---
extensions/no-hot-corner/extension.js | 31 +++++++++++++++++++++++
extensions/no-hot-corner/meson.build | 5 ++++
extensions/no-hot-corner/metadata.json.in | 9 +++++++
extensions/no-hot-corner/stylesheet.css | 1 +
meson.build | 1 +
5 files changed, 47 insertions(+)
create mode 100644 extensions/no-hot-corner/extension.js
create mode 100644 extensions/no-hot-corner/meson.build
create mode 100644 extensions/no-hot-corner/metadata.json.in
create mode 100644 extensions/no-hot-corner/stylesheet.css
diff --git a/extensions/no-hot-corner/extension.js b/extensions/no-hot-corner/extension.js
new file mode 100644
index 0000000..e7a0d63
--- /dev/null
+++ b/extensions/no-hot-corner/extension.js
@@ -0,0 +1,31 @@
+const Main = imports.ui.main;
+
+let _id;
+
+function _disableHotCorners() {
+ // Disables all hot corners
+ Main.layoutManager.hotCorners.forEach(function(hotCorner) {
+ if (!hotCorner) {
+ return;
+ }
+
+ hotCorner._toggleOverview = function() {};
+ hotCorner._pressureBarrier._trigger = function() {};
+ });
+}
+
+function init() {
+}
+
+function enable() {
+ _disableHotCorners();
+ // Hot corners may be re-created afterwards (for example, If there's a monitor change).
+ // So we catch all changes.
+ _id = Main.layoutManager.connect('hot-corners-changed', _disableHotCorners);
+}
+
+function disable() {
+ // Disconnects the callback and re-creates the hot corners
+ Main.layoutManager.disconnect(_id);
+ Main.layoutManager._updateHotCorners();
+}
diff --git a/extensions/no-hot-corner/meson.build b/extensions/no-hot-corner/meson.build
new file mode 100644
index 0000000..48504f6
--- /dev/null
+++ b/extensions/no-hot-corner/meson.build
@@ -0,0 +1,5 @@
+extension_data += configure_file(
+ input: metadata_name + '.in',
+ output: metadata_name,
+ configuration: metadata_conf
+)
diff --git a/extensions/no-hot-corner/metadata.json.in b/extensions/no-hot-corner/metadata.json.in
new file mode 100644
index 0000000..406d83b
--- /dev/null
+++ b/extensions/no-hot-corner/metadata.json.in
@@ -0,0 +1,9 @@
+{
+"extension-id": "@extension_id@",
+"uuid": "@uuid@",
+"name": "No Topleft Hot Corner",
+"description": "Disable the hot corner in the top left; you can still reach the overview by clicking the Activities button or pressing the dedicated key.",
+"shell-version": [ "@shell_current@" ],
+"url": "https://github.com/HROMANO/nohotcorner/",
+"version": 15
+}
diff --git a/extensions/no-hot-corner/stylesheet.css b/extensions/no-hot-corner/stylesheet.css
new file mode 100644
index 0000000..25134b6
--- /dev/null
+++ b/extensions/no-hot-corner/stylesheet.css
@@ -0,0 +1 @@
+/* This extensions requires no special styling */
diff --git a/meson.build b/meson.build
index d129e6c..6f27f46 100644
--- a/meson.build
+++ b/meson.build
@@ -51,6 +51,7 @@ all_extensions += [
'auto-move-windows',
'dash-to-dock',
'native-window-placement',
+ 'no-hot-corner',
'panel-favorites',
'top-icons',
'updates-dialog',
--
2.21.0
From c165b79227f0702ecd5cb9f6b25ba5cede47a334 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Tue, 26 Mar 2019 19:44:43 +0100
Subject: [PATCH 6/8] Add window-grouper extension
---
extensions/window-grouper/extension.js | 109 ++++++++++
extensions/window-grouper/meson.build | 8 +
extensions/window-grouper/metadata.json.in | 11 +
...hell.extensions.window-grouper.gschema.xml | 9 +
extensions/window-grouper/prefs.js | 191 ++++++++++++++++++
extensions/window-grouper/stylesheet.css | 1 +
meson.build | 3 +-
7 files changed, 331 insertions(+), 1 deletion(-)
create mode 100644 extensions/window-grouper/extension.js
create mode 100644 extensions/window-grouper/meson.build
create mode 100644 extensions/window-grouper/metadata.json.in
create mode 100644 extensions/window-grouper/org.gnome.shell.extensions.window-grouper.gschema.xml
create mode 100644 extensions/window-grouper/prefs.js
create mode 100644 extensions/window-grouper/stylesheet.css
diff --git a/extensions/window-grouper/extension.js b/extensions/window-grouper/extension.js
new file mode 100644
index 0000000..f66a764
--- /dev/null
+++ b/extensions/window-grouper/extension.js
@@ -0,0 +1,109 @@
+// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
+/* exported init */
+
+const { Shell } = imports.gi;
+
+const ExtensionUtils = imports.misc.extensionUtils;
+
+class WindowMover {
+ constructor() {
+ this._settings = ExtensionUtils.getSettings();
+ this._appSystem = Shell.AppSystem.get_default();
+ this._appConfigs = new Set();
+ this._appData = new Map();
+
+ this._appsChangedId = this._appSystem.connect(
+ 'installed-changed', this._updateAppData.bind(this));
+
+ this._settings.connect('changed', this._updateAppConfigs.bind(this));
+ this._updateAppConfigs();
+ }
+
+ _updateAppConfigs() {
+ this._appConfigs.clear();
+
+ this._settings.get_strv('application-list').forEach(appId => {
+ this._appConfigs.add(appId);
+ });
+
+ this._updateAppData();
+ }
+
+ _updateAppData() {
+ let ids = [...this._appConfigs.values()];
+ let removedApps = [...this._appData.keys()].filter(
+ a => !ids.includes(a.id)
+ );
+ removedApps.forEach(app => {
+ app.disconnect(this._appData.get(app).windowsChangedId);
+ this._appData.delete(app);
+ });
+
+ let addedApps = ids.map(id => this._appSystem.lookup_app(id)).filter(
+ app => app != null && !this._appData.has(app)
+ );
+ addedApps.forEach(app => {
+ let data = {
+ windows: app.get_windows(),
+ windowsChangedId: app.connect(
+ 'windows-changed', this._appWindowsChanged.bind(this))
+ };
+ this._appData.set(app, data);
+ });
+ }
+
+ destroy() {
+ if (this._appsChangedId) {
+ this._appSystem.disconnect(this._appsChangedId);
+ this._appsChangedId = 0;
+ }
+
+ if (this._settings) {
+ this._settings.run_dispose();
+ this._settings = null;
+ }
+
+ this._appConfigs.clear();
+ this._updateAppData();
+ }
+
+ _appWindowsChanged(app) {
+ let data = this._appData.get(app);
+ let windows = app.get_windows();
+
+ // If get_compositor_private() returns non-NULL on a removed windows,
+ // the window still exists and is just moved to a different workspace
+ // or something; assume it'll be added back immediately, so keep it
+ // to avoid moving it again
+ windows.push(...data.windows.filter(
+ w => !windows.includes(w) && w.get_compositor_private() != null
+ ));
+
+ windows.filter(w => !data.windows.includes(w)).forEach(window => {
+ let leader = data.windows.find(w => w.get_pid() == window.get_pid());
+ if (leader)
+ window.change_workspace(leader.get_workspace());
+ });
+ data.windows = windows;
+ }
+}
+
+class Extension {
+ constructor() {
+ this._winMover = null;
+ }
+
+ enable() {
+ this._winMover = new WindowMover();
+ }
+
+ disable() {
+ this._winMover.destroy();
+ this._winMover = null;
+ }
+}
+
+function init() {
+ ExtensionUtils.initTranslations();
+ return new Extension();
+}
diff --git a/extensions/window-grouper/meson.build b/extensions/window-grouper/meson.build
new file mode 100644
index 0000000..c55a783
--- /dev/null
+++ b/extensions/window-grouper/meson.build
@@ -0,0 +1,8 @@
+extension_data += configure_file(
+ input: metadata_name + '.in',
+ output: metadata_name,
+ configuration: metadata_conf
+)
+
+extension_sources += files('prefs.js')
+extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
diff --git a/extensions/window-grouper/metadata.json.in b/extensions/window-grouper/metadata.json.in
new file mode 100644
index 0000000..aa202c8
--- /dev/null
+++ b/extensions/window-grouper/metadata.json.in
@@ -0,0 +1,11 @@
+{
+ "extension-id": "@extension_id@",
+ "uuid": "@uuid@",
+ "settings-schema": "@gschemaname@",
+ "gettext-domain": "@gettext_domain@",
+ "name": "Window grouper",
+ "description": "Keep windows that belong to the same process on the same workspace.",
+ "shell-version": [ "@shell_current@" ],
+ "original-authors": [ "fmuellner@redhat.com" ],
+ "url": "@url@"
+}
diff --git a/extensions/window-grouper/org.gnome.shell.extensions.window-grouper.gschema.xml b/extensions/window-grouper/org.gnome.shell.extensions.window-grouper.gschema.xml
new file mode 100644
index 0000000..ee052a6
--- /dev/null
+++ b/extensions/window-grouper/org.gnome.shell.extensions.window-grouper.gschema.xml
@@ -0,0 +1,9 @@
+<schemalist gettext-domain="gnome-shell-extensions">
+ <schema id="org.gnome.shell.extensions.window-grouper" path="/org/gnome/shell/extensions/window-grouper/">
+ <key name="application-list" type="as">
+ <default>[ ]</default>
+ <summary>Application that should be grouped</summary>
+ <description>A list of application ids</description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/extensions/window-grouper/prefs.js b/extensions/window-grouper/prefs.js
new file mode 100644
index 0000000..d7b748e
--- /dev/null
+++ b/extensions/window-grouper/prefs.js
@@ -0,0 +1,191 @@
+// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
+/* exported init buildPrefsWidget */
+
+const { Gio, GObject, Gtk } = imports.gi;
+
+const ExtensionUtils = imports.misc.extensionUtils;
+const Gettext = imports.gettext.domain('gnome-shell-extensions');
+const _ = Gettext.gettext;
+const N_ = e => e;
+
+const SETTINGS_KEY = 'application-list';
+
+const Columns = {
+ APPINFO: 0,
+ DISPLAY_NAME: 1,
+ ICON: 2
+};
+
+const Widget = GObject.registerClass({
+ GTypeName: 'WindowGrouperPrefsWidget',
+}, class Widget extends Gtk.Grid {
+ _init(params) {
+ super._init(params);
+ this.set_orientation(Gtk.Orientation.VERTICAL);
+
+ this._settings = ExtensionUtils.getSettings();
+ this._settings.connect('changed', this._refresh.bind(this));
+ this._changedPermitted = false;
+
+ this._store = new Gtk.ListStore();
+ this._store.set_column_types([Gio.AppInfo, GObject.TYPE_STRING, Gio.Icon]);
+
+ let scrolled = new Gtk.ScrolledWindow({ shadow_type: Gtk.ShadowType.IN });
+ scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
+ this.add(scrolled);
+
+
+ this._treeView = new Gtk.TreeView({
+ model: this._store,
+ headers_visible: false,
+ hexpand: true,
+ vexpand: true
+ });
+ this._treeView.get_selection().set_mode(Gtk.SelectionMode.SINGLE);
+
+ let appColumn = new Gtk.TreeViewColumn({
+ sort_column_id: Columns.DISPLAY_NAME,
+ spacing: 12
+ });
+ let iconRenderer = new Gtk.CellRendererPixbuf({
+ stock_size: Gtk.IconSize.DIALOG,
+ xpad: 12,
+ ypad: 12
+ });
+ appColumn.pack_start(iconRenderer, false);
+ appColumn.add_attribute(iconRenderer, 'gicon', Columns.ICON);
+ let nameRenderer = new Gtk.CellRendererText();
+ appColumn.pack_start(nameRenderer, true);
+ appColumn.add_attribute(nameRenderer, 'text', Columns.DISPLAY_NAME);
+ this._treeView.append_column(appColumn);
+
+ scrolled.add(this._treeView);
+
+ let toolbar = new Gtk.Toolbar({ icon_size: Gtk.IconSize.SMALL_TOOLBAR });
+ toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_INLINE_TOOLBAR);
+ this.add(toolbar);
+
+ let newButton = new Gtk.ToolButton({
+ icon_name: 'list-add-symbolic'
+ });
+ newButton.connect('clicked', this._createNew.bind(this));
+ toolbar.add(newButton);
+
+ let delButton = new Gtk.ToolButton({
+ icon_name: 'list-remove-symbolic'
+ });
+ delButton.connect('clicked', this._deleteSelected.bind(this));
+ toolbar.add(delButton);
+
+ let selection = this._treeView.get_selection();
+ selection.connect('changed', () => {
+ delButton.sensitive = selection.count_selected_rows() > 0;
+ });
+ delButton.sensitive = selection.count_selected_rows() > 0;
+
+ this._changedPermitted = true;
+ this._refresh();
+ }
+
+ _createNew() {
+ let dialog = new Gtk.AppChooserDialog({
+ heading: _('Select an application for which grouping should apply'),
+ transient_for: this.get_toplevel(),
+ modal: true
+ });
+
+ dialog.get_widget().show_all = true;
+
+ dialog.connect('response', (dialog, id) => {
+ if (id != Gtk.ResponseType.OK) {
+ dialog.destroy();
+ return;
+ }
+
+ let appInfo = dialog.get_app_info();
+ if (!appInfo) {
+ dialog.destroy();
+ return;
+ }
+
+ this._changedPermitted = false;
+ this._appendItem(appInfo.get_id());
+ this._changedPermitted = true;
+
+ let iter = this._store.append();
+ this._store.set(iter,
+ [Columns.APPINFO, Columns.ICON, Columns.DISPLAY_NAME],
+ [appInfo, appInfo.get_icon(), appInfo.get_display_name()]);
+
+ dialog.destroy();
+ });
+ dialog.show_all();
+ }
+
+ _deleteSelected() {
+ let [any, model_, iter] = this._treeView.get_selection().get_selected();
+
+ if (any) {
+ let appInfo = this._store.get_value(iter, Columns.APPINFO);
+
+ this._changedPermitted = false;
+ this._removeItem(appInfo.get_id());
+ this._changedPermitted = true;
+ this._store.remove(iter);
+ }
+ }
+
+ _refresh() {
+ if (!this._changedPermitted)
+ // Ignore this notification, model is being modified outside
+ return;
+
+ this._store.clear();
+
+ let currentItems = this._settings.get_strv(SETTINGS_KEY);
+ let validItems = [];
+ for (let i = 0; i < currentItems.length; i++) {
+ let id = currentItems[i];
+ let appInfo = Gio.DesktopAppInfo.new(id);
+ if (!appInfo)
+ continue;
+ validItems.push(currentItems[i]);
+
+ let iter = this._store.append();
+ this._store.set(iter,
+ [Columns.APPINFO, Columns.ICON, Columns.DISPLAY_NAME],
+ [appInfo, appInfo.get_icon(), appInfo.get_display_name()]);
+ }
+
+ if (validItems.length != currentItems.length) // some items were filtered out
+ this._settings.set_strv(SETTINGS_KEY, validItems);
+ }
+
+ _appendItem(id) {
+ let currentItems = this._settings.get_strv(SETTINGS_KEY);
+ currentItems.push(id);
+ this._settings.set_strv(SETTINGS_KEY, currentItems);
+ }
+
+ _removeItem(id) {
+ let currentItems = this._settings.get_strv(SETTINGS_KEY);
+ let index = currentItems.indexOf(id);
+
+ if (index < 0)
+ return;
+ currentItems.splice(index, 1);
+ this._settings.set_strv(SETTINGS_KEY, currentItems);
+ }
+});
+
+
+function init() {
+ ExtensionUtils.initTranslations();
+}
+
+function buildPrefsWidget() {
+ let widget = new Widget({ margin: 12 });
+ widget.show_all();
+
+ return widget;
+}
diff --git a/extensions/window-grouper/stylesheet.css b/extensions/window-grouper/stylesheet.css
new file mode 100644
index 0000000..25134b6
--- /dev/null
+++ b/extensions/window-grouper/stylesheet.css
@@ -0,0 +1 @@
+/* This extensions requires no special styling */
diff --git a/meson.build b/meson.build
index 6f27f46..4b9d138 100644
--- a/meson.build
+++ b/meson.build
@@ -55,7 +55,8 @@ all_extensions += [
'panel-favorites',
'top-icons',
'updates-dialog',
- 'user-theme'
+ 'user-theme',
+ 'window-grouper'
]
enabled_extensions = get_option('enable_extensions')
--
2.21.0
From be2ec13b3a876b4c4bcf91ee9e537b89c06e4e3f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Tue, 26 Mar 2019 21:32:09 +0100
Subject: [PATCH 7/8] Add disable-screenshield extension
---
extensions/disable-screenshield/extension.js | 27 +++++++++++++++++++
extensions/disable-screenshield/meson.build | 5 ++++
.../disable-screenshield/metadata.json.in | 9 +++++++
.../disable-screenshield/stylesheet.css | 1 +
meson.build | 1 +
5 files changed, 43 insertions(+)
create mode 100644 extensions/disable-screenshield/extension.js
create mode 100644 extensions/disable-screenshield/meson.build
create mode 100644 extensions/disable-screenshield/metadata.json.in
create mode 100644 extensions/disable-screenshield/stylesheet.css
diff --git a/extensions/disable-screenshield/extension.js b/extensions/disable-screenshield/extension.js
new file mode 100644
index 0000000..91204c0
--- /dev/null
+++ b/extensions/disable-screenshield/extension.js
@@ -0,0 +1,27 @@
+/* exported enable disable */
+
+const ScreenShield = imports.ui.screenShield;
+
+let _onUserBecameActiveOrig;
+
+function _onUserBecameActiveInjected() {
+ this.idleMonitor.remove_watch(this._becameActiveId);
+ this._becameActiveId = 0;
+
+ this._longLightbox.hide();
+ this._shortLightbox.hide();
+
+ this.deactivate(false);
+}
+
+function enable() {
+ _onUserBecameActiveOrig =
+ ScreenShield.ScreenShield.prototype._onUserBecameActive;
+ ScreenShield.ScreenShield.prototype._onUserBecameActive =
+ _onUserBecameActiveInjected;
+}
+
+function disable() {
+ ScreenShield.ScreenShield.prototype._onUserBecameActive =
+ _onUserBecameActiveOrig;
+}
diff --git a/extensions/disable-screenshield/meson.build b/extensions/disable-screenshield/meson.build
new file mode 100644
index 0000000..48504f6
--- /dev/null
+++ b/extensions/disable-screenshield/meson.build
@@ -0,0 +1,5 @@
+extension_data += configure_file(
+ input: metadata_name + '.in',
+ output: metadata_name,
+ configuration: metadata_conf
+)
diff --git a/extensions/disable-screenshield/metadata.json.in b/extensions/disable-screenshield/metadata.json.in
new file mode 100644
index 0000000..074429f
--- /dev/null
+++ b/extensions/disable-screenshield/metadata.json.in
@@ -0,0 +1,9 @@
+{
+ "extension-id": "@extension_id@",
+ "uuid": "@uuid@",
+ "name": "Disable Screen Shield",
+ "description": "Disable screen shield when screen lock is disabled",
+ "shell-version": [ "@shell_current@" ],
+ "original-authors": [ "lgpasquale@gmail.com" ],
+ "url": "@url@"
+}
diff --git a/extensions/disable-screenshield/stylesheet.css b/extensions/disable-screenshield/stylesheet.css
new file mode 100644
index 0000000..25134b6
--- /dev/null
+++ b/extensions/disable-screenshield/stylesheet.css
@@ -0,0 +1 @@
+/* This extensions requires no special styling */
diff --git a/meson.build b/meson.build
index 4b9d138..cf855a0 100644
--- a/meson.build
+++ b/meson.build
@@ -50,6 +50,7 @@ all_extensions = default_extensions
all_extensions += [
'auto-move-windows',
'dash-to-dock',
+ 'disable-screenshield',
'native-window-placement',
'no-hot-corner',
'panel-favorites',
--
2.21.0
From 0b1da0ee4e8669bb3ba5d7fef5cf436c7bbcea88 Mon Sep 17 00:00:00 2001
From: Carlos Soriano <csoriano@gnome.org>
Date: Mon, 13 Aug 2018 17:28:41 +0200
Subject: [PATCH 8/8] Add desktop icons extension
---
.../desktop-icons/createFolderDialog.js | 164 ++++
extensions/desktop-icons/createThumbnail.js | 35 +
extensions/desktop-icons/dbusUtils.js | 103 +++
extensions/desktop-icons/desktopGrid.js | 692 +++++++++++++++
extensions/desktop-icons/desktopIconsUtil.js | 123 +++
extensions/desktop-icons/desktopManager.js | 752 ++++++++++++++++
extensions/desktop-icons/extension.js | 71 ++
extensions/desktop-icons/fileItem.js | 800 ++++++++++++++++++
extensions/desktop-icons/meson.build | 19 +
extensions/desktop-icons/metadata.json.in | 11 +
extensions/desktop-icons/po/LINGUAS | 19 +
extensions/desktop-icons/po/POTFILES.in | 4 +
extensions/desktop-icons/po/cs.po | 195 +++++
extensions/desktop-icons/po/da.po | 159 ++++
extensions/desktop-icons/po/de.po | 192 +++++
extensions/desktop-icons/po/es.po | 218 +++++
extensions/desktop-icons/po/fi.po | 191 +++++
extensions/desktop-icons/po/fr.po | 164 ++++
extensions/desktop-icons/po/fur.po | 187 ++++
extensions/desktop-icons/po/hr.po | 186 ++++
extensions/desktop-icons/po/hu.po | 190 +++++
extensions/desktop-icons/po/id.po | 190 +++++
extensions/desktop-icons/po/it.po | 189 +++++
extensions/desktop-icons/po/ja.po | 187 ++++
extensions/desktop-icons/po/meson.build | 1 +
extensions/desktop-icons/po/nl.po | 188 ++++
extensions/desktop-icons/po/pl.po | 193 +++++
extensions/desktop-icons/po/pt_BR.po | 199 +++++
extensions/desktop-icons/po/ru.po | 153 ++++
extensions/desktop-icons/po/sv.po | 197 +++++
extensions/desktop-icons/po/tr.po | 191 +++++
extensions/desktop-icons/po/zh_TW.po | 135 +++
extensions/desktop-icons/prefs.js | 159 ++++
extensions/desktop-icons/schemas/meson.build | 6 +
...shell.extensions.desktop-icons.gschema.xml | 25 +
extensions/desktop-icons/stylesheet.css | 38 +
meson.build | 1 +
po/cs.po | 161 ++++
po/da.po | 161 ++++
po/de.po | 161 ++++
po/es.po | 222 ++++-
po/fi.po | 167 +++-
po/fr.po | 188 ++++
po/id.po | 159 ++++
po/it.po | 177 ++++
po/pl.po | 183 ++++
po/pt_BR.po | 197 ++++-
po/ru.po | 179 ++++
po/zh_TW.po | 165 +++-
49 files changed, 8617 insertions(+), 30 deletions(-)
create mode 100644 extensions/desktop-icons/createFolderDialog.js
create mode 100755 extensions/desktop-icons/createThumbnail.js
create mode 100644 extensions/desktop-icons/dbusUtils.js
create mode 100644 extensions/desktop-icons/desktopGrid.js
create mode 100644 extensions/desktop-icons/desktopIconsUtil.js
create mode 100644 extensions/desktop-icons/desktopManager.js
create mode 100644 extensions/desktop-icons/extension.js
create mode 100644 extensions/desktop-icons/fileItem.js
create mode 100644 extensions/desktop-icons/meson.build
create mode 100644 extensions/desktop-icons/metadata.json.in
create mode 100644 extensions/desktop-icons/po/LINGUAS
create mode 100644 extensions/desktop-icons/po/POTFILES.in
create mode 100644 extensions/desktop-icons/po/cs.po
create mode 100644 extensions/desktop-icons/po/da.po
create mode 100644 extensions/desktop-icons/po/de.po
create mode 100644 extensions/desktop-icons/po/es.po
create mode 100644 extensions/desktop-icons/po/fi.po
create mode 100644 extensions/desktop-icons/po/fr.po
create mode 100644 extensions/desktop-icons/po/fur.po
create mode 100644 extensions/desktop-icons/po/hr.po
create mode 100644 extensions/desktop-icons/po/hu.po
create mode 100644 extensions/desktop-icons/po/id.po
create mode 100644 extensions/desktop-icons/po/it.po
create mode 100644 extensions/desktop-icons/po/ja.po
create mode 100644 extensions/desktop-icons/po/meson.build
create mode 100644 extensions/desktop-icons/po/nl.po
create mode 100644 extensions/desktop-icons/po/pl.po
create mode 100644 extensions/desktop-icons/po/pt_BR.po
create mode 100644 extensions/desktop-icons/po/ru.po
create mode 100644 extensions/desktop-icons/po/sv.po
create mode 100644 extensions/desktop-icons/po/tr.po
create mode 100644 extensions/desktop-icons/po/zh_TW.po
create mode 100644 extensions/desktop-icons/prefs.js
create mode 100644 extensions/desktop-icons/schemas/meson.build
create mode 100644 extensions/desktop-icons/schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml
create mode 100644 extensions/desktop-icons/stylesheet.css
diff --git a/extensions/desktop-icons/createFolderDialog.js b/extensions/desktop-icons/createFolderDialog.js
new file mode 100644
index 0000000..f3e40e9
--- /dev/null
+++ b/extensions/desktop-icons/createFolderDialog.js
@@ -0,0 +1,164 @@
+/* Desktop Icons GNOME Shell extension
+ *
+ * Copyright (C) 2019 Andrea Azzaronea <andrea.azzarone@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+const { Clutter, GObject, GLib, Gio, St } = imports.gi;
+
+const Signals = imports.signals;
+
+const Dialog = imports.ui.dialog;
+const Gettext = imports.gettext.domain('desktop-icons');
+const ModalDialog = imports.ui.modalDialog;
+const ShellEntry = imports.ui.shellEntry;
+const Tweener = imports.ui.tweener;
+
+const ExtensionUtils = imports.misc.extensionUtils;
+const Me = ExtensionUtils.getCurrentExtension();
+const DesktopIconsUtil = Me.imports.desktopIconsUtil;
+const Extension = Me.imports.extension;
+
+const _ = Gettext.gettext;
+
+const DIALOG_GROW_TIME = 0.1;
+
+var CreateFolderDialog = class extends ModalDialog.ModalDialog {
+
+ constructor() {
+ super({ styleClass: 'create-folder-dialog' });
+
+ this._buildLayout();
+ }
+
+ _buildLayout() {
+ let label = new St.Label({ style_class: 'create-folder-dialog-label',
+ text: _('New folder name') });
+ this.contentLayout.add(label, { x_align: St.Align.START });
+
+ this._entry = new St.Entry({ style_class: 'create-folder-dialog-entry',
+ can_focus: true });
+ this._entry.clutter_text.connect('activate', this._onEntryActivate.bind(this));
+ this._entry.clutter_text.connect('text-changed', this._onTextChanged.bind(this));
+ ShellEntry.addContextMenu(this._entry);
+ this.contentLayout.add(this._entry);
+ this.setInitialKeyFocus(this._entry);
+
+ this._errorBox = new St.BoxLayout({ style_class: 'create-folder-dialog-error-box',
+ visible: false });
+ this.contentLayout.add(this._errorBox, { expand: true });
+
+ this._errorMessage = new St.Label({ style_class: 'create-folder-dialog-error-label' });
+ this._errorMessage.clutter_text.line_wrap = true;
+ this._errorBox.add(this._errorMessage, { expand: true,
+ x_align: St.Align.START,
+ x_fill: false,
+ y_align: St.Align.MIDDLE,
+ y_fill: false });
+
+ this._createButton = this.addButton({ action: this._onCreateButton.bind(this),
+ label: _('Create') });
+ this.addButton({ action: this.close.bind(this),
+ label: _('Cancel'),
+ key: Clutter.Escape });
+ this._onTextChanged();
+ }
+
+ _showError(message) {
+ this._errorMessage.set_text(message);
+
+ if (!this._errorBox.visible) {
+ let [errorBoxMinHeight, errorBoxNaturalHeight] = this._errorBox.get_preferred_height(-1);
+ let parentActor = this._errorBox.get_parent();
+
+ Tweener.addTween(parentActor,
+ { height: parentActor.height + errorBoxNaturalHeight,
+ time: DIALOG_GROW_TIME,
+ transition: 'easeOutQuad',
+ onComplete: () => {
+ parentActor.set_height(-1);
+ this._errorBox.show();
+ }
+ });
+ }
+ }
+
+ _hideError() {
+ if (this._errorBox.visible) {
+ let [errorBoxMinHeight, errorBoxNaturalHeight] = this._errorBox.get_preferred_height(-1);
+ let parentActor = this._errorBox.get_parent();
+
+ Tweener.addTween(parentActor,
+ { height: parentActor.height - errorBoxNaturalHeight,
+ time: DIALOG_GROW_TIME,
+ transition: 'easeOutQuad',
+ onComplete: () => {
+ parentActor.set_height(-1);
+ this._errorBox.hide();
+ this._errorMessage.set_text('');
+ }
+ });
+ }
+ }
+
+ _onCreateButton() {
+ this._onEntryActivate();
+ }
+
+ _onEntryActivate() {
+ if (!this._createButton.reactive)
+ return;
+
+ this.emit('response', this._entry.get_text());
+ this.close();
+ }
+
+ _onTextChanged() {
+ let text = this._entry.get_text();
+ let is_valid = true;
+
+ let found_name = false;
+ for(let name of Extension.desktopManager.getDesktopFileNames()) {
+ if (name === text) {
+ found_name = true;
+ break;
+ }
+ }
+
+ if (text.trim().length == 0) {
+ is_valid = false;
+ this._hideError();
+ } else if (text.includes('/')) {
+ is_valid = false;
+ this._showError(_('Folder names cannot contain “/”.'));
+ } else if (text === '.') {
+ is_valid = false;
+ this._showError(_('A folder cannot be called “.”.'));
+ } else if (text === '..') {
+ is_valid = false;
+ this._showError(_('A folder cannot be called “..”.'));
+ } else if (text.startsWith('.')) {
+ this._showError(_('Folders with “.” at the beginning of their name are hidden.'));
+ } else if (found_name) {
+ this._showError(_('There is already a file or folder with that name.'));
+ is_valid = false;
+ } else {
+ this._hideError();
+ }
+
+ this._createButton.reactive = is_valid;
+ }
+};
+Signals.addSignalMethods(CreateFolderDialog.prototype);
diff --git a/extensions/desktop-icons/createThumbnail.js b/extensions/desktop-icons/createThumbnail.js
new file mode 100755
index 0000000..212f6b7
--- /dev/null
+++ b/extensions/desktop-icons/createThumbnail.js
@@ -0,0 +1,35 @@
+#!/usr/bin/gjs
+
+/* Desktop Icons GNOME Shell extension
+ *
+ * Copyright (C) 2018 Sergio Costas <rastersoft@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+const GnomeDesktop = imports.gi.GnomeDesktop;
+const Gio = imports.gi.Gio;
+
+let thumbnailFactory = GnomeDesktop.DesktopThumbnailFactory.new(GnomeDesktop.DesktopThumbnailSize.LARGE);
+
+let file = Gio.File.new_for_path(ARGV[0]);
+let fileUri = file.get_uri();
+
+let fileInfo = file.query_info('standard::content-type,time::modified', Gio.FileQueryInfoFlags.NONE, null);
+let modifiedTime = fileInfo.get_attribute_uint64('time::modified');
+let thumbnailPixbuf = thumbnailFactory.generate_thumbnail(fileUri, fileInfo.get_content_type());
+if (thumbnailPixbuf == null)
+ thumbnailFactory.create_failed_thumbnail(fileUri, modifiedTime);
+else
+ thumbnailFactory.save_thumbnail(thumbnailPixbuf, fileUri, modifiedTime);
diff --git a/extensions/desktop-icons/dbusUtils.js b/extensions/desktop-icons/dbusUtils.js
new file mode 100644
index 0000000..19fe987
--- /dev/null
+++ b/extensions/desktop-icons/dbusUtils.js
@@ -0,0 +1,103 @@
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+var NautilusFileOperationsProxy;
+var FreeDesktopFileManagerProxy;
+
+const NautilusFileOperationsInterface = `<node>
+<interface name='org.gnome.Nautilus.FileOperations'>
+ <method name='CopyURIs'>
+ <arg name='URIs' type='as' direction='in'/>
+ <arg name='Destination' type='s' direction='in'/>
+ </method>
+ <method name='MoveURIs'>
+ <arg name='URIs' type='as' direction='in'/>
+ <arg name='Destination' type='s' direction='in'/>
+ </method>
+ <method name='EmptyTrash'>
+ </method>
+ <method name='TrashFiles'>
+ <arg name='URIs' type='as' direction='in'/>
+ </method>
+ <method name='CreateFolder'>
+ <arg name='URI' type='s' direction='in'/>
+ </method>
+ <method name='RenameFile'>
+ <arg name='URI' type='s' direction='in'/>
+ <arg name='NewName' type='s' direction='in'/>
+ </method>
+ <method name='Undo'>
+ </method>
+ <method name='Redo'>
+ </method>
+ <property name='UndoStatus' type='i' access='read'/>
+</interface>
+</node>`;
+
+const NautilusFileOperationsProxyInterface = Gio.DBusProxy.makeProxyWrapper(NautilusFileOperationsInterface);
+
+const FreeDesktopFileManagerInterface = `<node>
+<interface name='org.freedesktop.FileManager1'>
+ <method name='ShowItems'>
+ <arg name='URIs' type='as' direction='in'/>
+ <arg name='StartupId' type='s' direction='in'/>
+ </method>
+ <method name='ShowItemProperties'>
+ <arg name='URIs' type='as' direction='in'/>
+ <arg name='StartupId' type='s' direction='in'/>
+ </method>
+</interface>
+</node>`;
+
+const FreeDesktopFileManagerProxyInterface = Gio.DBusProxy.makeProxyWrapper(FreeDesktopFileManagerInterface);
+
+function init() {
+ NautilusFileOperationsProxy = new NautilusFileOperationsProxyInterface(
+ Gio.DBus.session,
+ 'org.gnome.Nautilus',
+ '/org/gnome/Nautilus',
+ (proxy, error) => {
+ if (error) {
+ log('Error connecting to Nautilus');
+ }
+ }
+ );
+
+ FreeDesktopFileManagerProxy = new FreeDesktopFileManagerProxyInterface(
+ Gio.DBus.session,
+ 'org.freedesktop.FileManager1',
+ '/org/freedesktop/FileManager1',
+ (proxy, error) => {
+ if (error) {
+ log('Error connecting to Nautilus');
+ }
+ }
+ );
+}
+
+function openFileWithOtherApplication(filePath) {
+ let fdList = new Gio.UnixFDList();
+ let channel = GLib.IOChannel.new_file(filePath, "r");
+ fdList.append(channel.unix_get_fd());
+ channel.set_close_on_unref(true);
+ let builder = GLib.VariantBuilder.new(GLib.VariantType.new("a{sv}"));
+ let options = builder.end();
+ let parameters = GLib.Variant.new_tuple([GLib.Variant.new_string("0"),
+ GLib.Variant.new_handle(0),
+ options]);
+ Gio.bus_get(Gio.BusType.SESSION, null,
+ (source, result) => {
+ let dbus_connection = Gio.bus_get_finish(result);
+ dbus_connection.call_with_unix_fd_list("org.freedesktop.portal.Desktop",
+ "/org/freedesktop/portal/desktop",
+ "org.freedesktop.portal.OpenURI",
+ "OpenFile",
+ parameters,
+ GLib.VariantType.new("o"),
+ Gio.DBusCallFlags.NONE,
+ -1,
+ fdList,
+ null,
+ null);
+ }
+ );
+}
diff --git a/extensions/desktop-icons/desktopGrid.js b/extensions/desktop-icons/desktopGrid.js
new file mode 100644
index 0000000..a2d1f12
--- /dev/null
+++ b/extensions/desktop-icons/desktopGrid.js
@@ -0,0 +1,692 @@
+/* Desktop Icons GNOME Shell extension
+ *
+ * Copyright (C) 2017 Carlos Soriano <csoriano@redhat.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+const Gtk = imports.gi.Gtk;
+const Clutter = imports.gi.Clutter;
+const St = imports.gi.St;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Shell = imports.gi.Shell;
+
+const Signals = imports.signals;
+
+const Layout = imports.ui.layout;
+const Main = imports.ui.main;
+const BoxPointer = imports.ui.boxpointer;
+const PopupMenu = imports.ui.popupMenu;
+const GrabHelper = imports.ui.grabHelper;
+const Config = imports.misc.config;
+
+const ExtensionUtils = imports.misc.extensionUtils;
+const Me = ExtensionUtils.getCurrentExtension();
+const CreateFolderDialog = Me.imports.createFolderDialog;
+const Extension = Me.imports.extension;
+const FileItem = Me.imports.fileItem;
+const Prefs = Me.imports.prefs;
+const DBusUtils = Me.imports.dbusUtils;
+const DesktopIconsUtil = Me.imports.desktopIconsUtil;
+const Util = imports.misc.util;
+
+const Clipboard = St.Clipboard.get_default();
+const CLIPBOARD_TYPE = St.ClipboardType.CLIPBOARD;
+const Gettext = imports.gettext.domain('desktop-icons');
+
+const _ = Gettext.gettext;
+
+
+/* From NautilusFileUndoManagerState */
+var UndoStatus = {
+ NONE: 0,
+ UNDO: 1,
+ REDO: 2,
+};
+
+var StoredCoordinates = {
+ PRESERVE: 0,
+ OVERWRITE:1,
+ ASSIGN:2,
+};
+
+class Placeholder extends St.Bin {
+ constructor() {
+ super();
+ }
+}
+
+var DesktopGrid = class {
+
+ constructor(bgManager) {
+ this._bgManager = bgManager;
+
+ this._fileItemHandlers = new Map();
+ this._fileItems = [];
+
+ this.layout = new Clutter.GridLayout({
+ orientation: Clutter.Orientation.VERTICAL,
+ column_homogeneous: true,
+ row_homogeneous: true
+ });
+
+ this._actorLayout = new Clutter.BinLayout({
+ x_align: Clutter.BinAlignment.FIXED,
+ y_align: Clutter.BinAlignment.FIXED
+ });
+
+ this.actor = new St.Widget({
+ layout_manager: this._actorLayout
+ });
+ this.actor._delegate = this;
+
+ this._grid = new St.Widget({
+ name: 'DesktopGrid',
+ layout_manager: this.layout,
+ reactive: true,
+ x_expand: true,
+ y_expand: true,
+ can_focus: true,
+ opacity: 255
+ });
+ this.actor.add_child(this._grid);
+
+ this._renamePopup = new RenamePopup(this);
+ this.actor.add_child(this._renamePopup.actor);
+
+ this._bgManager._container.add_child(this.actor);
+
+ this.actor.connect('destroy', () => this._onDestroy());
+
+ let monitorIndex = bgManager._monitorIndex;
+ this._monitorConstraint = new Layout.MonitorConstraint({
+ index: monitorIndex,
+ work_area: true
+ });
+ this.actor.add_constraint(this._monitorConstraint);
+
+ this._addDesktopBackgroundMenu();
+
+ this._bgDestroyedId = bgManager.backgroundActor.connect('destroy',
+ () => this._backgroundDestroyed());
+
+ this._grid.connect('button-press-event', (actor, event) => this._onPressButton(actor, event));
+
+ this._grid.connect('key-press-event', this._onKeyPress.bind(this));
+
+ this._grid.connect('allocation-changed', () => Extension.desktopManager.scheduleReLayoutChildren());
+ }
+
+ _onKeyPress(actor, event) {
+ if (global.stage.get_key_focus() != actor)
+ return Clutter.EVENT_PROPAGATE;
+
+ let symbol = event.get_key_symbol();
+ let isCtrl = (event.get_state() & Clutter.ModifierType.CONTROL_MASK) != 0;
+ let isShift = (event.get_state() & Clutter.ModifierType.SHIFT_MASK) != 0;
+ if (isCtrl && isShift && [Clutter.Z, Clutter.z].indexOf(symbol) > -1) {
+ this._doRedo();
+ return Clutter.EVENT_STOP;
+ }
+ else if (isCtrl && [Clutter.Z, Clutter.z].indexOf(symbol) > -1) {
+ this._doUndo();
+ return Clutter.EVENT_STOP;
+ }
+ else if (isCtrl && [Clutter.C, Clutter.c].indexOf(symbol) > -1) {
+ Extension.desktopManager.doCopy();
+ return Clutter.EVENT_STOP;
+ }
+ else if (isCtrl && [Clutter.X, Clutter.x].indexOf(symbol) > -1) {
+ Extension.desktopManager.doCut();
+ return Clutter.EVENT_STOP;
+ }
+ else if (isCtrl && [Clutter.V, Clutter.v].indexOf(symbol) > -1) {
+ this._doPaste();
+ return Clutter.EVENT_STOP;
+ }
+ else if (symbol == Clutter.Return) {
+ Extension.desktopManager.doOpen();
+ return Clutter.EVENT_STOP;
+ }
+ else if (symbol == Clutter.Delete) {
+ Extension.desktopManager.doTrash();
+ return Clutter.EVENT_STOP;
+ } else if (symbol == Clutter.F2) {
+ // Support renaming other grids file items.
+ Extension.desktopManager.doRename();
+ return Clutter.EVENT_STOP;
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _backgroundDestroyed() {
+ this._bgDestroyedId = 0;
+ if (this._bgManager == null)
+ return;
+
+ if (this._bgManager._backgroundSource) {
+ this._bgDestroyedId = this._bgManager.backgroundActor.connect('destroy',
+ () => this._backgroundDestroyed());
+ } else {
+ this.actor.destroy();
+ }
+ }
+
+ _onDestroy() {
+ if (this._bgDestroyedId && this._bgManager.backgroundActor != null)
+ this._bgManager.backgroundActor.disconnect(this._bgDestroyedId);
+ this._bgDestroyedId = 0;
+ this._bgManager = null;
+ }
+
+ _onNewFolderClicked() {
+
+ let dialog = new CreateFolderDialog.CreateFolderDialog();
+
+ dialog.connect('response', (dialog, name) => {
+ let dir = DesktopIconsUtil.getDesktopDir().get_child(name);
+ DBusUtils.NautilusFileOperationsProxy.CreateFolderRemote(dir.get_uri(),
+ (result, error) => {
+ if (error)
+ throw new Error('Error creating new folder: ' + error.message);
+ }
+ );
+ });
+
+ dialog.open();
+ }
+
+ _parseClipboardText(text) {
+ if (text === null)
+ return [false, false, null];
+
+ let lines = text.split('\n');
+ let [mime, action, ...files] = lines;
+
+ if (mime != 'x-special/nautilus-clipboard')
+ return [false, false, null];
+
+ if (!(['copy', 'cut'].includes(action)))
+ return [false, false, null];
+ let isCut = action == 'cut';
+
+ /* Last line is empty due to the split */
+ if (files.length <= 1)
+ return [false, false, null];
+ /* Remove last line */
+ files.pop();
+
+ return [true, isCut, files];
+ }
+
+ _doPaste() {
+ Clipboard.get_text(CLIPBOARD_TYPE,
+ (clipboard, text) => {
+ let [valid, is_cut, files] = this._parseClipboardText(text);
+ if (!valid)
+ return;
+
+ let desktopDir = `${DesktopIconsUtil.getDesktopDir().get_uri()}`;
+ if (is_cut) {
+ DBusUtils.NautilusFileOperationsProxy.MoveURIsRemote(files, desktopDir,
+ (result, error) => {
+ if (error)
+ throw new Error('Error moving files: ' + error.message);
+ }
+ );
+ } else {
+ DBusUtils.NautilusFileOperationsProxy.CopyURIsRemote(files, desktopDir,
+ (result, error) => {
+ if (error)
+ throw new Error('Error copying files: ' + error.message);
+ }
+ );
+ }
+ }
+ );
+ }
+
+ _onPasteClicked() {
+ this._doPaste();
+ }
+
+ _doUndo() {
+ DBusUtils.NautilusFileOperationsProxy.UndoRemote(
+ (result, error) => {
+ if (error)
+ throw new Error('Error performing undo: ' + error.message);
+ }
+ );
+ }
+
+ _onUndoClicked() {
+ this._doUndo();
+ }
+
+ _doRedo() {
+ DBusUtils.NautilusFileOperationsProxy.RedoRemote(
+ (result, error) => {
+ if (error)
+ throw new Error('Error performing redo: ' + error.message);
+ }
+ );
+ }
+
+ _onRedoClicked() {
+ this._doRedo();
+ }
+
+ _onOpenDesktopInFilesClicked() {
+ Gio.AppInfo.launch_default_for_uri_async(DesktopIconsUtil.getDesktopDir().get_uri(),
+ null, null,
+ (source, result) => {
+ try {
+ Gio.AppInfo.launch_default_for_uri_finish(result);
+ } catch (e) {
+ log('Error opening Desktop in Files: ' + e.message);
+ }
+ }
+ );
+ }
+
+ _onOpenTerminalClicked() {
+ let desktopPath = DesktopIconsUtil.getDesktopDir().get_path();
+ DesktopIconsUtil.launchTerminal(desktopPath);
+ }
+
+ _syncUndoRedo() {
+ this._undoMenuItem.actor.visible = DBusUtils.NautilusFileOperationsProxy.UndoStatus == UndoStatus.UNDO;
+ this._redoMenuItem.actor.visible = DBusUtils.NautilusFileOperationsProxy.UndoStatus == UndoStatus.REDO;
+ }
+
+ _undoStatusChanged(proxy, properties, test) {
+ if ('UndoStatus' in properties.deep_unpack())
+ this._syncUndoRedo();
+ }
+
+ _createDesktopBackgroundMenu() {
+ let menu = new PopupMenu.PopupMenu(Main.layoutManager.dummyCursor,
+ 0, St.Side.TOP);
+ menu.addAction(_("New Folder"), () => this._onNewFolderClicked());
+ menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ this._pasteMenuItem = menu.addAction(_("Paste"), () => this._onPasteClicked());
+ this._undoMenuItem = menu.addAction(_("Undo"), () => this._onUndoClicked());
+ this._redoMenuItem = menu.addAction(_("Redo"), () => this._onRedoClicked());
+ menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ menu.addAction(_("Show Desktop in Files"), () => this._onOpenDesktopInFilesClicked());
+ menu.addAction(_("Open in Terminal"), () => this._onOpenTerminalClicked());
+ menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ menu.addSettingsAction(_("Change Background…"), 'gnome-background-panel.desktop');
+ menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ menu.addSettingsAction(_("Display Settings"), 'gnome-display-panel.desktop');
+ menu.addSettingsAction(_("Settings"), 'gnome-control-center.desktop');
+
+ menu.actor.add_style_class_name('background-menu');
+
+ Main.layoutManager.uiGroup.add_child(menu.actor);
+ menu.actor.hide();
+
+ menu._propertiesChangedId = DBusUtils.NautilusFileOperationsProxy.connect('g-properties-changed',
+ this._undoStatusChanged.bind(this));
+ this._syncUndoRedo();
+
+ menu.connect('destroy',
+ () => DBusUtils.NautilusFileOperationsProxy.disconnect(menu._propertiesChangedId));
+ menu.connect('open-state-changed',
+ (popupm, isOpen) => {
+ if (isOpen) {
+ Clipboard.get_text(CLIPBOARD_TYPE,
+ (clipBoard, text) => {
+ let [valid, is_cut, files] = this._parseClipboardText(text);
+ this._pasteMenuItem.setSensitive(valid);
+ }
+ );
+ }
+ }
+ );
+ this._pasteMenuItem.setSensitive(false);
+
+ return menu;
+ }
+
+ _openMenu(x, y) {
+ Main.layoutManager.setDummyCursorGeometry(x, y, 0, 0);
+ this.actor._desktopBackgroundMenu.open(BoxPointer.PopupAnimation.NONE);
+ /* Since the handler is in the press event it needs to ignore the release event
+ * to not immediately close the menu on release
+ */
+ this.actor._desktopBackgroundManager.ignoreRelease();
+ }
+
+ _addFileItemTo(fileItem, column, row, coordinatesAction) {
+ let placeholder = this.layout.get_child_at(column, row);
+ placeholder.child = fileItem.actor;
+ this._fileItems.push(fileItem);
+ let selectedId = fileItem.connect('selected', this._onFileItemSelected.bind(this));
+ let renameId = fileItem.connect('rename-clicked', this.doRename.bind(this));
+ this._fileItemHandlers.set(fileItem, [selectedId, renameId]);
+
+ /* If this file is new in the Desktop and hasn't yet
+ * fixed coordinates, store the new possition to ensure
+ * that the next time it will be shown in the same possition.
+ * Also store the new possition if it has been moved by the user,
+ * and not triggered by a screen change.
+ */
+ if ((fileItem.savedCoordinates == null) || (coordinatesAction == StoredCoordinates.OVERWRITE)) {
+ let [fileX, fileY] = placeholder.get_transformed_position();
+ fileItem.savedCoordinates = [Math.round(fileX), Math.round(fileY)];
+ }
+ }
+
+ addFileItemCloseTo(fileItem, x, y, coordinatesAction) {
+ let [column, row] = this._getEmptyPlaceClosestTo(x, y, coordinatesAction);
+ this._addFileItemTo(fileItem, column, row, coordinatesAction);
+ }
+
+ _getEmptyPlaceClosestTo(x, y, coordinatesAction) {
+ let maxColumns = this._getMaxColumns();
+ let maxRows = this._getMaxRows();
+
+ let [actorX, actorY] = this._grid.get_transformed_position();
+ let actorWidth = this._grid.allocation.x2 - this._grid.allocation.x1;
+ let actorHeight = this._grid.allocation.y2 - this._grid.allocation.y1;
+ let placeX = Math.round((x - actorX) * maxColumns / actorWidth);
+ let placeY = Math.round((y - actorY) * maxRows / actorHeight);
+
+ placeX = DesktopIconsUtil.clamp(placeX, 0, maxColumns - 1);
+ placeY = DesktopIconsUtil.clamp(placeY, 0, maxRows - 1);
+ if (this.layout.get_child_at(placeX, placeY).child == null)
+ return [placeX, placeY];
+ let found = false;
+ let resColumn = null;
+ let resRow = null;
+ let minDistance = Infinity;
+ for (let column = 0; column < maxColumns; column++) {
+ for (let row = 0; row < maxRows; row++) {
+ let placeholder = this.layout.get_child_at(column, row);
+ if (placeholder.child != null)
+ continue;
+
+ let [proposedX, proposedY] = placeholder.get_transformed_position();
+ if (coordinatesAction == StoredCoordinates.ASSIGN)
+ return [column, row];
+ let distance = DesktopIconsUtil.distanceBetweenPoints(proposedX, proposedY, x, y);
+ if (distance < minDistance) {
+ found = true;
+ minDistance = distance;
+ resColumn = column;
+ resRow = row;
+ }
+ }
+ }
+
+ if (!found)
+ throw new Error(`Not enough place at monitor ${this._bgManager._monitorIndex}`);
+
+ return [resColumn, resRow];
+ }
+
+ removeFileItem(fileItem) {
+ let index = this._fileItems.indexOf(fileItem);
+ if (index > -1)
+ this._fileItems.splice(index, 1);
+ else
+ throw new Error('Error removing children from container');
+
+ let [column, row] = this._getPosOfFileItem(fileItem);
+ let placeholder = this.layout.get_child_at(column, row);
+ placeholder.child = null;
+ let [selectedId, renameId] = this._fileItemHandlers.get(fileItem);
+ fileItem.disconnect(selectedId);
+ fileItem.disconnect(renameId);
+ this._fileItemHandlers.delete(fileItem);
+ }
+
+ _fillPlaceholders() {
+ for (let column = 0; column < this._getMaxColumns(); column++) {
+ for (let row = 0; row < this._getMaxRows(); row++) {
+ this.layout.attach(new Placeholder(), column, row, 1, 1);
+ }
+ }
+ }
+
+ reset() {
+ let tmpFileItemsCopy = this._fileItems.slice();
+ for (let fileItem of tmpFileItemsCopy)
+ this.removeFileItem(fileItem);
+ this._grid.remove_all_children();
+
+ this._fillPlaceholders();
+ }
+
+ _onStageMotion(actor, event) {
+ if (this._drawingRubberBand) {
+ let [x, y] = event.get_coords();
+ this._updateRubberBand(x, y);
+ this._selectFromRubberband(x, y);
+ }
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _onPressButton(actor, event) {
+ let button = event.get_button();
+ let [x, y] = event.get_coords();
+
+ this._grid.grab_key_focus();
+
+ if (button == 1) {
+ let shiftPressed = !!(event.get_state() & Clutter.ModifierType.SHIFT_MASK);
+ let controlPressed = !!(event.get_state() & Clutter.ModifierType.CONTROL_MASK);
+ if (!shiftPressed && !controlPressed)
+ Extension.desktopManager.clearSelection();
+ let [gridX, gridY] = this._grid.get_transformed_position();
+ Extension.desktopManager.startRubberBand(x, y, gridX, gridY);
+ return Clutter.EVENT_STOP;
+ }
+
+ if (button == 3) {
+ this._openMenu(x, y);
+
+ return Clutter.EVENT_STOP;
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _addDesktopBackgroundMenu() {
+ this.actor._desktopBackgroundMenu = this._createDesktopBackgroundMenu();
+ this.actor._desktopBackgroundManager = new PopupMenu.PopupMenuManager({ actor: this.actor });
+ this.actor._desktopBackgroundManager.addMenu(this.actor._desktopBackgroundMenu);
+
+ this.actor.connect('destroy', () => {
+ this.actor._desktopBackgroundMenu.destroy();
+ this.actor._desktopBackgroundMenu = null;
+ this.actor._desktopBackgroundManager = null;
+ });
+ }
+
+ _getMaxColumns() {
+ let gridWidth = this._grid.allocation.x2 - this._grid.allocation.x1;
+ return Math.floor(gridWidth / Prefs.get_desired_width(St.ThemeContext.get_for_stage(global.stage).scale_factor));
+ }
+
+ _getMaxRows() {
+ let gridHeight = this._grid.allocation.y2 - this._grid.allocation.y1;
+ return Math.floor(gridHeight / Prefs.get_desired_height(St.ThemeContext.get_for_stage(global.stage).scale_factor));
+ }
+
+ acceptDrop(source, actor, x, y, time) {
+ /* Coordinates are relative to the grid, we want to transform them to
+ * absolute coordinates to work across monitors */
+ let [gridX, gridY] = this.actor.get_transformed_position();
+ let [absoluteX, absoluteY] = [x + gridX, y + gridY];
+ return Extension.desktopManager.acceptDrop(absoluteX, absoluteY);
+ }
+
+ _getPosOfFileItem(itemToFind) {
+ if (itemToFind == null)
+ throw new Error('Error at _getPosOfFileItem: child cannot be null');
+
+ let found = false;
+ let maxColumns = this._getMaxColumns();
+ let maxRows = this._getMaxRows();
+ let column = 0;
+ let row = 0;
+ for (column = 0; column < maxColumns; column++) {
+ for (row = 0; row < maxRows; row++) {
+ let item = this.layout.get_child_at(column, row);
+ if (item.child && item.child._delegate.file.equal(itemToFind.file)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found)
+ break;
+ }
+
+ if (!found)
+ throw new Error('Position of file item was not found');
+
+ return [column, row];
+ }
+
+ _onFileItemSelected(fileItem, keepCurrentSelection, addToSelection) {
+ this._grid.grab_key_focus();
+ }
+
+ doRename(fileItem) {
+ this._renamePopup.onFileItemRenameClicked(fileItem);
+ }
+};
+
+var RenamePopup = class {
+
+ constructor(grid) {
+ this._source = null;
+ this._isOpen = false;
+
+ this._renameEntry = new St.Entry({ hint_text: _("Enter file name…"),
+ can_focus: true,
+ x_expand: true });
+ this._renameEntry.clutter_text.connect('activate', this._onRenameAccepted.bind(this));
+ this._renameOkButton= new St.Button({ label: _("OK"),
+ style_class: 'app-view-control button',
+ button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
+ reactive: true,
+ can_focus: true,
+ x_expand: true });
+ this._renameCancelButton = new St.Button({ label: _("Cancel"),
+ style_class: 'app-view-control button',
+ button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
+ reactive: true,
+ can_focus: true,
+ x_expand: true });
+ this._renameCancelButton.connect('clicked', () => { this._onRenameCanceled(); });
+ this._renameOkButton.connect('clicked', () => { this._onRenameAccepted(); });
+ let renameButtonsBoxLayout = new Clutter.BoxLayout({ homogeneous: true });
+ let renameButtonsBox = new St.Widget({ layout_manager: renameButtonsBoxLayout,
+ x_expand: true });
+ renameButtonsBox.add_child(this._renameCancelButton);
+ renameButtonsBox.add_child(this._renameOkButton);
+
+ let renameContentLayout = new Clutter.BoxLayout({ spacing: 6,
+ orientation: Clutter.Orientation.VERTICAL });
+ let renameContent = new St.Widget({ style_class: 'rename-popup',
+ layout_manager: renameContentLayout,
+ x_expand: true });
+ renameContent.add_child(this._renameEntry);
+ renameContent.add_child(renameButtonsBox);
+
+ this._boxPointer = new BoxPointer.BoxPointer(St.Side.TOP, { can_focus: false, x_expand: false });
+ this.actor = this._boxPointer.actor;
+ this.actor.style_class = 'popup-menu-boxpointer';
+ this.actor.add_style_class_name('popup-menu');
+ this.actor.visible = false;
+ this._boxPointer.bin.set_child(renameContent);
+
+ this._grabHelper = new GrabHelper.GrabHelper(grid.actor, { actionMode: Shell.ActionMode.POPUP });
+ this._grabHelper.addActor(this.actor);
+ }
+
+ _popup() {
+ if (this._isOpen)
+ return;
+
+ this._isOpen = this._grabHelper.grab({ actor: this.actor,
+ onUngrab: this._popdown.bind(this) });
+
+ if (!this._isOpen) {
+ this._grabHelper.ungrab({ actor: this.actor });
+ return;
+ }
+
+ this._boxPointer.setPosition(this._source.actor, 0.5);
+ if (ExtensionUtils.versionCheck(['3.28', '3.30'], Config.PACKAGE_VERSION))
+ this._boxPointer.show(BoxPointer.PopupAnimation.FADE |
+ BoxPointer.PopupAnimation.SLIDE);
+ else
+ this._boxPointer.open(BoxPointer.PopupAnimation.FADE |
+ BoxPointer.PopupAnimation.SLIDE);
+
+ this.emit('open-state-changed', true);
+ }
+
+ _popdown() {
+ if (!this._isOpen)
+ return;
+
+ this._grabHelper.ungrab({ actor: this.actor });
+
+ if (ExtensionUtils.versionCheck(['3.28', '3.30'], Config.PACKAGE_VERSION))
+ this._boxPointer.hide(BoxPointer.PopupAnimation.FADE |
+ BoxPointer.PopupAnimation.SLIDE);
+ else
+ this._boxPointer.close(BoxPointer.PopupAnimation.FADE |
+ BoxPointer.PopupAnimation.SLIDE);
+
+ this._isOpen = false;
+ this.emit('open-state-changed', false);
+ }
+
+ onFileItemRenameClicked(fileItem) {
+ this._source = fileItem;
+
+ this._renameEntry.text = fileItem.displayName;
+
+ this._popup();
+ this._renameEntry.grab_key_focus();
+ this._renameEntry.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
+ let extensionOffset = DesktopIconsUtil.getFileExtensionOffset(fileItem.displayName, fileItem.isDirectory);
+ this._renameEntry.clutter_text.set_selection(0, extensionOffset);
+ }
+
+ _onRenameAccepted() {
+ this._popdown();
+ DBusUtils.NautilusFileOperationsProxy.RenameFileRemote(this._source.file.get_uri(),
+ this._renameEntry.get_text(),
+ (result, error) => {
+ if (error)
+ throw new Error('Error renaming file: ' + error.message);
+ }
+ );
+ }
+
+ _onRenameCanceled() {
+ this._popdown();
+ }
+};
+Signals.addSignalMethods(RenamePopup.prototype);
diff --git a/extensions/desktop-icons/desktopIconsUtil.js b/extensions/desktop-icons/desktopIconsUtil.js
new file mode 100644
index 0000000..0aea654
--- /dev/null
+++ b/extensions/desktop-icons/desktopIconsUtil.js
@@ -0,0 +1,123 @@
+/* Desktop Icons GNOME Shell extension
+ *
+ * Copyright (C) 2017 Carlos Soriano <csoriano@redhat.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+const Gtk = imports.gi.Gtk;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const ExtensionUtils = imports.misc.extensionUtils;
+const Me = ExtensionUtils.getCurrentExtension();
+const Prefs = Me.imports.prefs;
+
+const TERMINAL_SCHEMA = 'org.gnome.desktop.default-applications.terminal';
+const EXEC_KEY = 'exec';
+
+var DEFAULT_ATTRIBUTES = 'metadata::*,standard::*,access::*,time::modified,unix::mode';
+
+function getDesktopDir() {
+ let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
+ return Gio.File.new_for_commandline_arg(desktopPath);
+}
+
+function clamp(value, min, max) {
+ return Math.max(Math.min(value, max), min);
+};
+
+function launchTerminal(workdir) {
+ let terminalSettings = new Gio.Settings({ schema_id: TERMINAL_SCHEMA });
+ let exec = terminalSettings.get_string(EXEC_KEY);
+ let argv = [exec, `--working-directory=${workdir}`];
+
+ /* The following code has been extracted from GNOME Shell's
+ * source code in Misc.Util.trySpawn function and modified to
+ * set the working directory.
+ *
+ * https://gitlab.gnome.org/GNOME/gnome-shell/blob/gnome-3-30/js/misc/util.js
+ */
+
+ var success, pid;
+ try {
+ [success, pid] = GLib.spawn_async(workdir, argv, null,
+ GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
+ null);
+ } catch (err) {
+ /* Rewrite the error in case of ENOENT */
+ if (err.matches(GLib.SpawnError, GLib.SpawnError.NOENT)) {
+ throw new GLib.SpawnError({ code: GLib.SpawnError.NOENT,
+ message: _("Command not found") });
+ } else if (err instanceof GLib.Error) {
+ // The exception from gjs contains an error string like:
+ // Error invoking GLib.spawn_command_line_async: Failed to
+ // execute child process "foo" (No such file or directory)
+ // We are only interested in the part in the parentheses. (And
+ // we can't pattern match the text, since it gets localized.)
+ let message = err.message.replace(/.*\((.+)\)/, '$1');
+ throw new (err.constructor)({ code: err.code,
+ message: message });
+ } else {
+ throw err;
+ }
+ }
+ // Dummy child watch; we don't want to double-fork internally
+ // because then we lose the parent-child relationship, which
+ // can break polkit. See https://bugzilla.redhat.com//show_bug.cgi?id=819275
+ GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, () => {});
+}
+
+function distanceBetweenPoints(x, y, x2, y2) {
+ return (Math.pow(x - x2, 2) + Math.pow(y - y2, 2));
+}
+
+function getExtraFolders() {
+ let extraFolders = new Array();
+ if (Prefs.settings.get_boolean('show-home')) {
+ extraFolders.push([Gio.File.new_for_commandline_arg(GLib.get_home_dir()), Prefs.FileType.USER_DIRECTORY_HOME]);
+ }
+ if (Prefs.settings.get_boolean('show-trash')) {
+ extraFolders.push([Gio.File.new_for_uri('trash:///'), Prefs.FileType.USER_DIRECTORY_TRASH]);
+ }
+ return extraFolders;
+}
+
+function getFileExtensionOffset(filename, isDirectory) {
+ let offset = filename.length;
+
+ if (!isDirectory) {
+ let doubleExtensions = ['.gz', '.bz2', '.sit', '.Z', '.bz', '.xz'];
+ for (let extension of doubleExtensions) {
+ if (filename.endsWith(extension)) {
+ offset -= extension.length;
+ filename = filename.substring(0, offset);
+ break;
+ }
+ }
+ let lastDot = filename.lastIndexOf('.');
+ if (lastDot > 0)
+ offset = lastDot;
+ }
+ return offset;
+}
+
+function getGtkClassBackgroundColor(classname, state) {
+ let widget = new Gtk.WidgetPath();
+ widget.append_type(Gtk.Widget);
+
+ let context = new Gtk.StyleContext();
+ context.set_path(widget);
+ context.add_class(classname);
+ return context.get_background_color(state);
+}
diff --git a/extensions/desktop-icons/desktopManager.js b/extensions/desktop-icons/desktopManager.js
new file mode 100644
index 0000000..d8f548f
--- /dev/null
+++ b/extensions/desktop-icons/desktopManager.js
@@ -0,0 +1,752 @@
+/* Desktop Icons GNOME Shell extension
+ *
+ * Copyright (C) 2017 Carlos Soriano <csoriano@redhat.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+const Gtk = imports.gi.Gtk;
+const Clutter = imports.gi.Clutter;
+const GObject = imports.gi.GObject;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const St = imports.gi.St;
+const Mainloop = imports.mainloop;
+const Meta = imports.gi.Meta;
+
+const Animation = imports.ui.animation;
+const Background = imports.ui.background;
+const DND = imports.ui.dnd;
+const Main = imports.ui.main;
+const GrabHelper = imports.ui.grabHelper;
+
+const ExtensionUtils = imports.misc.extensionUtils;
+const Me = ExtensionUtils.getCurrentExtension();
+const Extension = Me.imports.extension;
+const DesktopGrid = Me.imports.desktopGrid;
+const FileItem = Me.imports.fileItem;
+const Prefs = Me.imports.prefs;
+const DBusUtils = Me.imports.dbusUtils;
+const DesktopIconsUtil = Me.imports.desktopIconsUtil;
+
+const Clipboard = St.Clipboard.get_default();
+const CLIPBOARD_TYPE = St.ClipboardType.CLIPBOARD;
+
+var S_IWOTH = 0x00002;
+
+function getDpy() {
+ return global.screen || global.display;
+}
+
+function findMonitorIndexForPos(x, y) {
+ return getDpy().get_monitor_index_for_rect(new Meta.Rectangle({x, y}));
+}
+
+
+var DesktopManager = GObject.registerClass({
+ Properties: {
+ 'writable-by-others': GObject.ParamSpec.boolean(
+ 'writable-by-others',
+ 'WritableByOthers',
+ 'Whether the desktop\'s directory can be written by others (o+w unix permission)',
+ GObject.ParamFlags.READABLE,
+ false
+ )
+ }
+}, class DesktopManager extends GObject.Object {
+ _init(params) {
+ super._init(params);
+
+ this._layoutChildrenId = 0;
+ this._deleteChildrenId = 0;
+ this._monitorDesktopDir = null;
+ this._desktopMonitorCancellable = null;
+ this._desktopGrids = {};
+ this._fileItemHandlers = new Map();
+ this._fileItems = new Map();
+ this._dragCancelled = false;
+ this._queryFileInfoCancellable = null;
+ this._unixMode = null;
+ this._writableByOthers = null;
+
+ this._monitorsChangedId = Main.layoutManager.connect('monitors-changed', () => this._recreateDesktopIcons());
+ this._rubberBand = new St.Widget({ style_class: 'rubber-band' });
+ this._rubberBand.hide();
+ Main.layoutManager._backgroundGroup.add_child(this._rubberBand);
+ this._grabHelper = new GrabHelper.GrabHelper(global.stage);
+
+ this._addDesktopIcons();
+ this._monitorDesktopFolder();
+
+ this.settingsId = Prefs.settings.connect('changed', () => this._recreateDesktopIcons());
+ this.gtkSettingsId = Prefs.gtkSettings.connect('changed', (obj, key) => {
+ if (key == 'show-hidden')
+ this._recreateDesktopIcons();
+ });
+
+ this._selection = new Set();
+ this._currentSelection = new Set();
+ this._inDrag = false;
+ this._dragXStart = Number.POSITIVE_INFINITY;
+ this._dragYStart = Number.POSITIVE_INFINITY;
+ }
+
+ startRubberBand(x, y) {
+ this._rubberBandInitialX = x;
+ this._rubberBandInitialY = y;
+ this._initRubberBandColor();
+ this._updateRubberBand(x, y);
+ this._rubberBand.show();
+ this._grabHelper.grab({ actor: global.stage });
+ Extension.lockActivitiesButton = true;
+ this._stageReleaseEventId = global.stage.connect('button-release-event', (actor, event) => {
+ this.endRubberBand();
+ });
+ this._rubberBandId = global.stage.connect('motion-event', (actor, event) => {
+ /* In some cases, when the user starts a rubberband selection and ends it
+ * (by releasing the left button) over a window instead of doing it over
+ * the desktop, the stage doesn't receive the "button-release" event.
+ * This happens currently with, at least, Dash to Dock extension, but
+ * it probably also happens with other applications or extensions.
+ * To fix this, we also end the rubberband selection if we detect mouse
+ * motion in the stage without the left button pressed during a
+ * rubberband selection.
+ * */
+ let button = event.get_state();
+ if (!(button & Clutter.ModifierType.BUTTON1_MASK)) {
+ this.endRubberBand();
+ return;
+ }
+ [x, y] = event.get_coords();
+ this._updateRubberBand(x, y);
+ let x0, y0, x1, y1;
+ if (x >= this._rubberBandInitialX) {
+ x0 = this._rubberBandInitialX;
+ x1 = x;
+ } else {
+ x1 = this._rubberBandInitialX;
+ x0 = x;
+ }
+ if (y >= this._rubberBandInitialY) {
+ y0 = this._rubberBandInitialY;
+ y1 = y;
+ } else {
+ y1 = this._rubberBandInitialY;
+ y0 = y;
+ }
+ for (let [fileUri, fileItem] of this._fileItems) {
+ fileItem.emit('selected', true, true,
+ fileItem.intersectsWith(x0, y0, x1 - x0, y1 - y0));
+ }
+ });
+ }
+
+ endRubberBand() {
+ this._rubberBand.hide();
+ Extension.lockActivitiesButton = false;
+ this._grabHelper.ungrab();
+ global.stage.disconnect(this._rubberBandId);
+ global.stage.disconnect(this._stageReleaseEventId);
+ this._rubberBandId = 0;
+ this._stageReleaseEventId = 0;
+
+ this._selection = new Set([...this._selection, ...this._currentSelection]);
+ this._currentSelection.clear();
+ }
+
+ _updateRubberBand(currentX, currentY) {
+ let x = this._rubberBandInitialX < currentX ? this._rubberBandInitialX
+ : currentX;
+ let y = this._rubberBandInitialY < currentY ? this._rubberBandInitialY
+ : currentY;
+ let width = Math.abs(this._rubberBandInitialX - currentX);
+ let height = Math.abs(this._rubberBandInitialY - currentY);
+ /* TODO: Convert to gobject.set for 3.30 */
+ this._rubberBand.set_position(x, y);
+ this._rubberBand.set_size(width, height);
+ }
+
+ _recreateDesktopIcons() {
+ this._destroyDesktopIcons();
+ this._addDesktopIcons();
+ }
+
+ _addDesktopIcons() {
+ forEachBackgroundManager(bgManager => {
+ let newGrid = new DesktopGrid.DesktopGrid(bgManager);
+ newGrid.actor.connect('destroy', (actor) => {
+ // if a grid loses its actor, remove it from the grid list
+ for (let grid in this._desktopGrids)
+ if (this._desktopGrids[grid].actor == actor) {
+ delete this._desktopGrids[grid];
+ break;
+ }
+ });
+ this._desktopGrids[bgManager._monitorIndex] = newGrid;
+ });
+
+ this._scanFiles();
+ }
+
+ _destroyDesktopIcons() {
+ Object.values(this._desktopGrids).forEach(grid => grid.actor.destroy());
+ this._desktopGrids = {};
+ }
+
+ /**
+ * Initialize rubberband color from the GTK rubberband class
+ * */
+ _initRubberBandColor() {
+ let rgba = DesktopIconsUtil.getGtkClassBackgroundColor('rubberband', Gtk.StateFlags.NORMAL);
+ let background_color =
+ 'rgba(' + rgba.red * 255 + ', ' + rgba.green * 255 + ', ' + rgba.blue * 255 + ', 0.4)';
+ this._rubberBand.set_style('background-color: ' + background_color);
+ }
+
+ async _scanFiles() {
+ for (let [fileItem, id] of this._fileItemHandlers)
+ fileItem.disconnect(id);
+ this._fileItemHandlers = new Map();
+
+ if (!this._unixMode) {
+ let desktopDir = DesktopIconsUtil.getDesktopDir();
+ let fileInfo = desktopDir.query_info(Gio.FILE_ATTRIBUTE_UNIX_MODE,
+ Gio.FileQueryInfoFlags.NONE,
+ null);
+ this._unixMode = fileInfo.get_attribute_uint32(Gio.FILE_ATTRIBUTE_UNIX_MODE);
+ this._setWritableByOthers((this._unixMode & S_IWOTH) != 0);
+ }
+
+ try {
+ let tmpFileItems = new Map();
+ for (let [file, info, extra] of await this._enumerateDesktop()) {
+ let fileItem = new FileItem.FileItem(file, info, extra);
+ tmpFileItems.set(fileItem.file.get_uri(), fileItem);
+ let id = fileItem.connect('selected',
+ this._onFileItemSelected.bind(this));
+
+ this._fileItemHandlers.set(fileItem, id);
+ }
+ this._fileItems = tmpFileItems;
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ log(`Error loading desktop files ${e.message}`);
+ return;
+ }
+
+ this.scheduleReLayoutChildren();
+ }
+
+ getDesktopFileNames () {
+ let fileList = [];
+ for (let [uri, item] of this._fileItems) {
+ fileList.push(item.fileName);
+ }
+ return fileList;
+ }
+
+ _enumerateDesktop() {
+ return new Promise((resolve, reject) => {
+ if (this._desktopEnumerateCancellable)
+ this._desktopEnumerateCancellable.cancel();
+
+ this._desktopEnumerateCancellable = new Gio.Cancellable();
+
+ let desktopDir = DesktopIconsUtil.getDesktopDir();
+ desktopDir.enumerate_children_async(DesktopIconsUtil.DEFAULT_ATTRIBUTES,
+ Gio.FileQueryInfoFlags.NONE,
+ GLib.PRIORITY_DEFAULT,
+ this._desktopEnumerateCancellable,
+ (source, result) => {
+ try {
+ let fileEnum = source.enumerate_children_finish(result);
+ let resultGenerator = function *() {
+ let info;
+ for (let [newFolder, extras] of DesktopIconsUtil.getExtraFolders()) {
+ yield [newFolder, newFolder.query_info(DesktopIconsUtil.DEFAULT_ATTRIBUTES, Gio.FileQueryInfoFlags.NONE, this._desktopEnumerateCancellable), extras];
+ }
+ while ((info = fileEnum.next_file(null)))
+ yield [fileEnum.get_child(info), info, Prefs.FileType.NONE];
+ }.bind(this);
+ resolve(resultGenerator());
+ } catch (e) {
+ reject(e);
+ }
+ });
+ });
+ }
+
+ _monitorDesktopFolder() {
+ if (this._monitorDesktopDir) {
+ this._monitorDesktopDir.cancel();
+ this._monitorDesktopDir = null;
+ }
+
+ let desktopDir = DesktopIconsUtil.getDesktopDir();
+ this._monitorDesktopDir = desktopDir.monitor_directory(Gio.FileMonitorFlags.WATCH_MOVES, null);
+ this._monitorDesktopDir.set_rate_limit(1000);
+ this._monitorDesktopDir.connect('changed', (obj, file, otherFile, eventType) => this._updateDesktopIfChanged(file, otherFile, eventType));
+ }
+
+ checkIfSpecialFilesAreSelected() {
+ for (let fileItem of this._selection) {
+ if (fileItem.isSpecial)
+ return true;
+ }
+ return false;
+ }
+
+ getNumberOfSelectedItems() {
+ return this._selection.size;
+ }
+
+ get writableByOthers() {
+ return this._writableByOthers;
+ }
+
+ _setWritableByOthers(value) {
+ if (value == this._writableByOthers)
+ return;
+
+ this._writableByOthers = value
+ this.notify('writable-by-others');
+ }
+
+ _updateDesktopIfChanged (file, otherFile, eventType) {
+ let {
+ DELETED, MOVED_IN, MOVED_OUT, CREATED, RENAMED, CHANGES_DONE_HINT, ATTRIBUTE_CHANGED
+ } = Gio.FileMonitorEvent;
+
+ let fileUri = file.get_uri();
+ let fileItem = null;
+ if (this._fileItems.has(fileUri))
+ fileItem = this._fileItems.get(fileUri);
+ switch(eventType) {
+ case RENAMED:
+ this._fileItems.delete(fileUri);
+ this._fileItems.set(otherFile.get_uri(), fileItem);
+ fileItem.onFileRenamed(otherFile);
+ return;
+ case CHANGES_DONE_HINT:
+ case ATTRIBUTE_CHANGED:
+ /* a file changed, rather than the desktop itself */
+ let desktopDir = DesktopIconsUtil.getDesktopDir();
+ if (file.get_uri() != desktopDir.get_uri())
+ return;
+
+ if (this._queryFileInfoCancellable)
+ this._queryFileInfoCancellable.cancel();
+
+ file.query_info_async(Gio.FILE_ATTRIBUTE_UNIX_MODE,
+ Gio.FileQueryInfoFlags.NONE,
+ GLib.PRIORITY_DEFAULT,
+ this._queryFileInfoCancellable,
+ (source, result) => {
+ try {
+ let info = source.query_info_finish(result);
+ this._queryFileInfoCancellable = null;
+
+ this._unixMode = info.get_attribute_uint32(Gio.FILE_ATTRIBUTE_UNIX_MODE);
+ this._setWritableByOthers((this._unixMode & S_IWOTH) != 0);
+
+ if (this._writableByOthers)
+ log(`desktop-icons: Desktop is writable by others - will not allow launching any desktop files`);
+ } catch(error) {
+ if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ global.log('Error getting desktop unix mode: ' + error);
+ }
+ });
+
+ return;
+ }
+
+ // Only get a subset of events we are interested in.
+ // Note that CREATED will emit a CHANGES_DONE_HINT
+ if (![DELETED, MOVED_IN, MOVED_OUT, CREATED].includes(eventType))
+ return;
+
+ this._recreateDesktopIcons();
+ }
+
+ _setupDnD() {
+ this._draggableContainer = new St.Widget({
+ visible: true,
+ width: 1,
+ height: 1,
+ x: 0,
+ y: 0,
+ style_class: 'draggable'
+ });
+ this._draggableContainer._delegate = this;
+ this._draggable = DND.makeDraggable(this._draggableContainer,
+ {
+ manualMode: true,
+ dragActorOpacity: 100
+ });
+
+ this._draggable.connect('drag-cancelled', () => this._onDragCancelled());
+ this._draggable.connect('drag-end', () => this._onDragEnd());
+
+ this._draggable._dragActorDropped = event => this._dragActorDropped(event);
+ }
+
+ dragStart() {
+ if (this._inDrag) {
+ return;
+ }
+
+ this._setupDnD();
+ let event = Clutter.get_current_event();
+ let [x, y] = event.get_coords();
+ [this._dragXStart, this._dragYStart] = event.get_coords();
+ this._inDrag = true;
+
+ for (let fileItem of this._selection) {
+ let clone = new Clutter.Clone({
+ source: fileItem.actor,
+ reactive: false
+ });
+ clone.x = fileItem.actor.get_transformed_position()[0];
+ clone.y = fileItem.actor.get_transformed_position()[1];
+ this._draggableContainer.add_child(clone);
+ }
+
+ Main.layoutManager.uiGroup.add_child(this._draggableContainer);
+ this._draggable.startDrag(x, y, global.get_current_time(), event.get_event_sequence());
+ }
+
+ _onDragCancelled() {
+ let event = Clutter.get_current_event();
+ let [x, y] = event.get_coords();
+ this._dragCancelled = true;
+ }
+
+ _onDragEnd() {
+ this._inDrag = false;
+ Main.layoutManager.uiGroup.remove_child(this._draggableContainer);
+ }
+
+ _dragActorDropped(event) {
+ let [dropX, dropY] = event.get_coords();
+ let target = this._draggable._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
+ dropX, dropY);
+
+ // We call observers only once per motion with the innermost
+ // target actor. If necessary, the observer can walk the
+ // parent itself.
+ let dropEvent = {
+ dropActor: this._draggable._dragActor,
+ targetActor: target,
+ clutterEvent: event
+ };
+ for (let dragMonitor of DND.dragMonitors) {
+ let dropFunc = dragMonitor.dragDrop;
+ if (dropFunc)
+ switch (dropFunc(dropEvent)) {
+ case DragDropResult.FAILURE:
+ case DragDropResult.SUCCESS:
+ return true;
+ case DragDropResult.CONTINUE:
+ continue;
+ }
+ }
+
+ // At this point it is too late to cancel a drag by destroying
+ // the actor, the fate of which is decided by acceptDrop and its
+ // side-effects
+ this._draggable._dragCancellable = false;
+
+ let destroyActor = false;
+ while (target) {
+ if (target._delegate && target._delegate.acceptDrop) {
+ let [r, targX, targY] = target.transform_stage_point(dropX, dropY);
+ if (target._delegate.acceptDrop(this._draggable.actor._delegate,
+ this._draggable._dragActor,
+ targX,
+ targY,
+ event.get_time())) {
+ // If it accepted the drop without taking the actor,
+ // handle it ourselves.
+ if (this._draggable._dragActor.get_parent() == Main.uiGroup) {
+ if (this._draggable._restoreOnSuccess) {
+ this._draggable._restoreDragActor(event.get_time());
+ return true;
+ }
+ else {
+ // We need this in order to make sure drag-end is fired
+ destroyActor = true;
+ }
+ }
+
+ this._draggable._dragInProgress = false;
+ getDpy().set_cursor(Meta.Cursor.DEFAULT);
+ this._draggable.emit('drag-end', event.get_time(), true);
+ if (destroyActor) {
+ this._draggable._dragActor.destroy();
+ }
+ this._draggable._dragComplete();
+
+ return true;
+ }
+ }
+ target = target.get_parent();
+ }
+
+ this._draggable._cancelDrag(event.get_time());
+
+ return true;
+ }
+
+ acceptDrop(xEnd, yEnd) {
+ let savedCoordinates = new Map();
+ let [xDiff, yDiff] = [xEnd - this._dragXStart, yEnd - this._dragYStart];
+ /* Remove all items before dropping new ones, so we can freely reposition
+ * them.
+ */
+ for (let item of this._selection) {
+ let [itemX, itemY] = item.actor.get_transformed_position();
+ let monitorIndex = findMonitorIndexForPos(itemX, itemY);
+ savedCoordinates.set(item, [itemX, itemY]);
+ this._desktopGrids[monitorIndex].removeFileItem(item);
+ }
+
+ for (let item of this._selection) {
+ let [itemX, itemY] = savedCoordinates.get(item);
+ /* Set the new ideal position where the item drop should happen */
+ let newFileX = Math.round(xDiff + itemX);
+ let newFileY = Math.round(yDiff + itemY);
+ let monitorIndex = findMonitorIndexForPos(newFileX, newFileY);
+ this._desktopGrids[monitorIndex].addFileItemCloseTo(item, newFileX, newFileY, DesktopGrid.StoredCoordinates.OVERWRITE);
+ }
+
+ return true;
+ }
+
+ selectionDropOnFileItem (fileItemDestination) {
+ if (!fileItemDestination.isDirectory)
+ return false;
+
+ let droppedUris = [];
+ for (let fileItem of this._selection) {
+ if (fileItem.isSpecial)
+ return false;
+ if (fileItemDestination.file.get_uri() == fileItem.file.get_uri())
+ return false;
+ droppedUris.push(fileItem.file.get_uri());
+ }
+
+ if (droppedUris.length == 0)
+ return true;
+
+ DBusUtils.NautilusFileOperationsProxy.MoveURIsRemote(droppedUris,
+ fileItemDestination.file.get_uri(),
+ (result, error) => {
+ if (error)
+ throw new Error('Error moving files: ' + error.message);
+ }
+ );
+ for (let fileItem of this._selection) {
+ fileItem.state = FileItem.State.GONE;
+ }
+
+ this._recreateDesktopIcons();
+
+ return true;
+ }
+
+ _resetGridsAndScheduleLayout() {
+ this._deleteChildrenId = 0;
+
+ Object.values(this._desktopGrids).forEach((grid) => grid.reset());
+
+ this._layoutChildrenId = GLib.idle_add(GLib.PRIORITY_LOW, () => this._layoutChildren());
+
+ return GLib.SOURCE_REMOVE;
+ }
+
+ scheduleReLayoutChildren() {
+ if (this._deleteChildrenId != 0)
+ return;
+
+ if (this._layoutChildrenId != 0) {
+ GLib.source_remove(this._layoutChildrenId);
+ this._layoutChildrenId = 0;
+ }
+
+
+ this._deleteChildrenId = GLib.idle_add(GLib.PRIORITY_LOW, () => this._resetGridsAndScheduleLayout());
+ }
+
+ _addFileItemCloseTo(item) {
+ let coordinates;
+ let x = 0;
+ let y = 0;
+ let coordinatesAction = DesktopGrid.StoredCoordinates.ASSIGN;
+ if (item.savedCoordinates != null) {
+ [x, y] = item.savedCoordinates;
+ coordinatesAction = DesktopGrid.StoredCoordinates.PRESERVE;
+ }
+ let monitorIndex = findMonitorIndexForPos(x, y);
+ let desktopGrid = this._desktopGrids[monitorIndex];
+ try {
+ desktopGrid.addFileItemCloseTo(item, x, y, coordinatesAction);
+ } catch (e) {
+ log(`Error adding children to desktop: ${e.message}`);
+ }
+ }
+
+ _layoutChildren() {
+ let showHidden = Prefs.gtkSettings.get_boolean('show-hidden');
+ /*
+ * Paint the icons in two passes:
+ * * first pass paints those that have their coordinates defined in the metadata
+ * * second pass paints those new files that still don't have their definitive coordinates
+ */
+ for (let [fileUri, fileItem] of this._fileItems) {
+ if (fileItem.savedCoordinates == null)
+ continue;
+ if (fileItem.state != FileItem.State.NORMAL)
+ continue;
+ if (!showHidden && fileItem.isHidden)
+ continue;
+ this._addFileItemCloseTo(fileItem);
+ }
+
+ for (let [fileUri, fileItem] of this._fileItems) {
+ if (fileItem.savedCoordinates !== null)
+ continue;
+ if (fileItem.state != FileItem.State.NORMAL)
+ continue;
+ if (!showHidden && fileItem.isHidden)
+ continue;
+ this._addFileItemCloseTo(fileItem);
+ }
+
+ this._layoutChildrenId = 0;
+ return GLib.SOURCE_REMOVE;
+ }
+
+ doRename() {
+ if (this._selection.size != 1)
+ return;
+
+ let item = [...this._selection][0];
+ if (item.canRename())
+ item.doRename();
+ }
+
+ doOpen() {
+ for (let fileItem of this._selection)
+ fileItem.doOpen();
+ }
+
+ doTrash() {
+ DBusUtils.NautilusFileOperationsProxy.TrashFilesRemote([...this._selection].map((x) => { return x.file.get_uri(); }),
+ (source, error) => {
+ if (error)
+ throw new Error('Error trashing files on the desktop: ' + error.message);
+ }
+ );
+ }
+
+ doEmptyTrash() {
+ DBusUtils.NautilusFileOperationsProxy.EmptyTrashRemote( (source, error) => {
+ if (error)
+ throw new Error('Error trashing files on the desktop: ' + error.message);
+ });
+ }
+
+ _onFileItemSelected(fileItem, keepCurrentSelection, rubberBandSelection, addToSelection) {
+
+ if (!keepCurrentSelection && !this._inDrag)
+ this.clearSelection();
+
+ let selection = keepCurrentSelection && rubberBandSelection ? this._currentSelection : this._selection;
+ if (addToSelection)
+ selection.add(fileItem);
+ else
+ selection.delete(fileItem);
+
+ for (let [fileUri, fileItem] of this._fileItems)
+ fileItem.isSelected = this._currentSelection.has(fileItem) || this._selection.has(fileItem);
+ }
+
+ clearSelection() {
+ for (let [fileUri, fileItem] of this._fileItems)
+ fileItem.isSelected = false;
+ this._selection = new Set();
+ this._currentSelection = new Set();
+ }
+
+ _getClipboardText(isCopy) {
+ let action = isCopy ? 'copy' : 'cut';
+ let text = `x-special/nautilus-clipboard\n${action}\n${
+ [...this._selection].map(s => s.file.get_uri()).join('\n')
+ }\n`;
+
+ return text;
+ }
+
+ doCopy() {
+ Clipboard.set_text(CLIPBOARD_TYPE, this._getClipboardText(true));
+ }
+
+ doCut() {
+ Clipboard.set_text(CLIPBOARD_TYPE, this._getClipboardText(false));
+ }
+
+ destroy() {
+ if (this._monitorDesktopDir)
+ this._monitorDesktopDir.cancel();
+ this._monitorDesktopDir = null;
+
+ if (this.settingsId)
+ Prefs.settings.disconnect(this.settingsId);
+ this.settingsId = 0;
+ if (this.gtkSettingsId)
+ Prefs.gtkSettings.disconnect(this.gtkSettingsId);
+ this.gtkSettingsId = 0;
+
+ if (this._layoutChildrenId)
+ GLib.source_remove(this._layoutChildrenId);
+ this._layoutChildrenId = 0;
+
+ if (this._deleteChildrenId)
+ GLib.source_remove(this._deleteChildrenId);
+ this._deleteChildrenId = 0;
+
+ if (this._monitorsChangedId)
+ Main.layoutManager.disconnect(this._monitorsChangedId);
+ this._monitorsChangedId = 0;
+ if (this._stageReleaseEventId)
+ global.stage.disconnect(this._stageReleaseEventId);
+ this._stageReleaseEventId = 0;
+
+ if (this._rubberBandId)
+ global.stage.disconnect(this._rubberBandId);
+ this._rubberBandId = 0;
+
+ this._rubberBand.destroy();
+
+ if (this._queryFileInfoCancellable)
+ this._queryFileInfoCancellable.cancel();
+
+ Object.values(this._desktopGrids).forEach(grid => grid.actor.destroy());
+ this._desktopGrids = {}
+ }
+});
+
+function forEachBackgroundManager(func) {
+ Main.layoutManager._bgManagers.forEach(func);
+}
diff --git a/extensions/desktop-icons/extension.js b/extensions/desktop-icons/extension.js
new file mode 100644
index 0000000..4b960ab
--- /dev/null
+++ b/extensions/desktop-icons/extension.js
@@ -0,0 +1,71 @@
+/* Desktop Icons GNOME Shell extension
+ *
+ * Copyright (C) 2017 Carlos Soriano <csoriano@redhat.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+const Main = imports.ui.main;
+
+const ExtensionUtils = imports.misc.extensionUtils;
+const Me = ExtensionUtils.getCurrentExtension();
+const Prefs = Me.imports.prefs;
+const { DesktopManager } = Me.imports.desktopManager;
+const DBusUtils = Me.imports.dbusUtils;
+
+var desktopManager = null;
+var addBackgroundMenuOrig = null;
+var _startupPreparedId;
+var lockActivitiesButton = false;
+
+var oldShouldToggleByCornerOrButtonFunction = null;
+
+function init() {
+ addBackgroundMenuOrig = Main.layoutManager._addBackgroundMenu;
+
+ Prefs.initTranslations();
+}
+
+function newShouldToggleByCornerOrButton() {
+ if (lockActivitiesButton)
+ return false;
+ else
+ return oldShouldToggleByCornerOrButtonFunction.bind(Main.overview);
+}
+
+function enable() {
+ // register a new function to allow to lock the Activities button when doing a rubberband selection
+ oldShouldToggleByCornerOrButtonFunction = Main.overview.shouldToggleByCornerOrButton;
+ Main.overview.shouldToggleByCornerOrButton = newShouldToggleByCornerOrButton;
+ // wait until the startup process has ended
+ if (Main.layoutManager._startingUp)
+ _startupPreparedId = Main.layoutManager.connect('startup-complete', () => innerEnable(true));
+ else
+ innerEnable(false);
+}
+
+function innerEnable(disconnectSignal) {
+ if (disconnectSignal)
+ Main.layoutManager.disconnect(_startupPreparedId);
+ DBusUtils.init();
+ Prefs.init();
+ Main.layoutManager._addBackgroundMenu = function() {};
+ desktopManager = new DesktopManager();
+}
+
+function disable() {
+ desktopManager.destroy();
+ Main.layoutManager._addBackgroundMenu = addBackgroundMenuOrig;
+ Main.overview.shouldToggleByCornerOrButton = oldShouldToggleByCornerOrButtonFunction;
+}
diff --git a/extensions/desktop-icons/fileItem.js b/extensions/desktop-icons/fileItem.js
new file mode 100644
index 0000000..e6f4f2c
--- /dev/null
+++ b/extensions/desktop-icons/fileItem.js
@@ -0,0 +1,800 @@
+/* Desktop Icons GNOME Shell extension
+ *
+ * Copyright (C) 2017 Carlos Soriano <csoriano@redhat.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+const Gtk = imports.gi.Gtk;
+const Clutter = imports.gi.Clutter;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const St = imports.gi.St;
+const Pango = imports.gi.Pango;
+const Meta = imports.gi.Meta;
+const GdkPixbuf = imports.gi.GdkPixbuf;
+const Cogl = imports.gi.Cogl;
+const GnomeDesktop = imports.gi.GnomeDesktop;
+
+const Mainloop = imports.mainloop;
+const Signals = imports.signals;
+
+const Background = imports.ui.background;
+const Main = imports.ui.main;
+const PopupMenu = imports.ui.popupMenu;
+const Util = imports.misc.util;
+
+const ExtensionUtils = imports.misc.extensionUtils;
+const Me = ExtensionUtils.getCurrentExtension();
+const Extension = Me.imports.extension;
+const Prefs = Me.imports.prefs;
+const DBusUtils = Me.imports.dbusUtils;
+const DesktopIconsUtil = Me.imports.desktopIconsUtil;
+
+const Gettext = imports.gettext.domain('desktop-icons');
+
+const _ = Gettext.gettext;
+
+const DRAG_TRESHOLD = 8;
+
+var S_IXUSR = 0o00100;
+var S_IWOTH = 0o00002;
+
+var State = {
+ NORMAL: 0,
+ GONE: 1,
+};
+
+var FileItem = class {
+
+ constructor(file, fileInfo, fileExtra) {
+ this._fileExtra = fileExtra;
+ this._loadThumbnailDataCancellable = null;
+ this._thumbnailScriptWatch = 0;
+ this._setMetadataCancellable = null;
+ this._queryFileInfoCancellable = null;
+ this._isSpecial = this._fileExtra != Prefs.FileType.NONE;
+
+ this._file = file;
+
+ this._savedCoordinates = null;
+ let savedCoordinates = fileInfo.get_attribute_as_string('metadata::nautilus-icon-position');
+ if (savedCoordinates != null)
+ this._savedCoordinates = savedCoordinates.split(',').map(x => Number(x));
+
+ this._state = State.NORMAL;
+
+ this.actor = new St.Bin({ visible: true });
+ this.actor.set_fill(true, true);
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ this.actor.set_height(Prefs.get_desired_height(scaleFactor));
+ this.actor.set_width(Prefs.get_desired_width(scaleFactor));
+ this.actor._delegate = this;
+ this.actor.connect('destroy', () => this._onDestroy());
+
+ this._container = new St.BoxLayout({ reactive: true,
+ track_hover: true,
+ can_focus: true,
+ style_class: 'file-item',
+ x_expand: true,
+ y_expand: true,
+ x_align: Clutter.ActorAlign.FILL,
+ vertical: true });
+ this.actor.set_child(this._container);
+ this._icon = new St.Bin();
+ this._icon.set_height(Prefs.get_icon_size() * scaleFactor);
+
+ this._iconContainer = new St.Bin({ visible: true });
+ this._iconContainer.child = this._icon;
+ this._container.add_child(this._iconContainer);
+
+ this._label = new St.Label({
+ style_class: 'name-label'
+ });
+
+ this._container.add_child(this._label);
+ let clutterText = this._label.get_clutter_text();
+ /* TODO: Convert to gobject.set for 3.30 */
+ clutterText.set_line_wrap(true);
+ clutterText.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR);
+ clutterText.set_ellipsize(Pango.EllipsizeMode.END);
+
+ this._container.connect('button-press-event', (actor, event) => this._onPressButton(actor, event));
+ this._container.connect('motion-event', (actor, event) => this._onMotion(actor, event));
+ this._container.connect('leave-event', (actor, event) => this._onLeave(actor, event));
+ this._container.connect('button-release-event', (actor, event) => this._onReleaseButton(actor, event));
+
+ /* Set the metadata and update relevant UI */
+ this._updateMetadataFromFileInfo(fileInfo);
+
+ if (this._isDesktopFile) {
+ /* watch for the executable bit being removed or added */
+ this._monitorDesktopFile = this._file.monitor_file(Gio.FileMonitorFlags.NONE, null);
+ this._monitorDesktopFileId = this._monitorDesktopFile.connect('changed',
+ (obj, file, otherFile, eventType) => {
+ switch(eventType) {
+ case Gio.FileMonitorEvent.ATTRIBUTE_CHANGED:
+ this._refreshMetadataAsync(true);
+ break;
+ }
+ });
+ }
+
+ this._createMenu();
+ this._updateIcon();
+
+ this._isSelected = false;
+ this._primaryButtonPressed = false;
+ if (this._attributeCanExecute && !this._isDesktopFile)
+ this._execLine = this.file.get_path();
+ if (fileExtra == Prefs.FileType.USER_DIRECTORY_TRASH) {
+ // if this icon is the trash, monitor the state of the directory to update the icon
+ this._trashChanged = false;
+ this._trashInitializeCancellable = null;
+ this._scheduleTrashRefreshId = 0;
+ this._monitorTrashDir = this._file.monitor_directory(Gio.FileMonitorFlags.WATCH_MOVES, null);
+ this._monitorTrashId = this._monitorTrashDir.connect('changed', (obj, file, otherFile, eventType) => {
+ switch(eventType) {
+ case Gio.FileMonitorEvent.DELETED:
+ case Gio.FileMonitorEvent.MOVED_OUT:
+ case Gio.FileMonitorEvent.CREATED:
+ case Gio.FileMonitorEvent.MOVED_IN:
+ if (this._queryTrashInfoCancellable || this._scheduleTrashRefreshId) {
+ if (this._scheduleTrashRefreshId)
+ GLib.source_remove(this._scheduleTrashRefreshId);
+ this._scheduleTrashRefreshId = Mainloop.timeout_add(200, () => this._refreshTrashIcon());
+ } else {
+ this._refreshTrashIcon();
+ }
+ break;
+ }
+ });
+ }
+
+ Extension.desktopManager.connect('notify::writable-by-others', () => {
+ if (!this._isDesktopFile)
+ return;
+ this._refreshMetadataAsync(true);
+ });
+ }
+
+ _onDestroy() {
+ /* Regular file data */
+ if (this._setMetadataCancellable)
+ this._setMetadataCancellable.cancel();
+ if (this._queryFileInfoCancellable)
+ this._queryFileInfoCancellable.cancel();
+
+ /* Thumbnailing */
+ if (this._thumbnailScriptWatch)
+ GLib.source_remove(this._thumbnailScriptWatch);
+ if (this._loadThumbnailDataCancellable)
+ this._loadThumbnailDataCancellable.cancel();
+
+ /* Desktop file */
+ if (this._monitorDesktopFileId) {
+ this._monitorDesktopFile.disconnect(this._monitorDesktopFileId);
+ this._monitorDesktopFile.cancel();
+ }
+
+ /* Trash */
+ if (this._monitorTrashDir) {
+ this._monitorTrashDir.disconnect(this._monitorTrashId);
+ this._monitorTrashDir.cancel();
+ }
+ if (this._queryTrashInfoCancellable)
+ this._queryTrashInfoCancellable.cancel();
+ if (this._scheduleTrashRefreshId)
+ GLib.source_remove(this._scheduleTrashRefreshId);
+ }
+
+ _refreshMetadataAsync(rebuild) {
+ if (this._queryFileInfoCancellable)
+ this._queryFileInfoCancellable.cancel();
+ this._queryFileInfoCancellable = new Gio.Cancellable();
+ this._file.query_info_async(DesktopIconsUtil.DEFAULT_ATTRIBUTES,
+ Gio.FileQueryInfoFlags.NONE,
+ GLib.PRIORITY_DEFAULT,
+ this._queryFileInfoCancellable,
+ (source, result) => {
+ try {
+ let newFileInfo = source.query_info_finish(result);
+ this._queryFileInfoCancellable = null;
+ this._updateMetadataFromFileInfo(newFileInfo);
+ if (rebuild) {
+ this._createMenu();
+ this._updateIcon();
+ }
+ } catch(error) {
+ if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ global.log("Error getting the file info: " + error);
+ }
+ });
+ }
+
+ _updateMetadataFromFileInfo(fileInfo) {
+ this._fileInfo = fileInfo;
+
+ let oldLabelText = this._label.text;
+
+ this._displayName = fileInfo.get_attribute_as_string('standard::display-name');
+ this._attributeCanExecute = fileInfo.get_attribute_boolean('access::can-execute');
+ this._unixmode = fileInfo.get_attribute_uint32('unix::mode')
+ this._writableByOthers = (this._unixmode & S_IWOTH) != 0;
+ this._trusted = fileInfo.get_attribute_as_string('metadata::trusted') == 'true';
+ this._attributeContentType = fileInfo.get_content_type();
+ this._isDesktopFile = this._attributeContentType == 'application/x-desktop';
+
+ if (this._isDesktopFile && this._writableByOthers)
+ log(`desktop-icons: File ${this._displayName} is writable by others - will not allow launching`);
+
+ if (this._isDesktopFile) {
+ this._desktopFile = Gio.DesktopAppInfo.new_from_filename(this._file.get_path());
+ if (!this._desktopFile) {
+ log(`Couldn’t parse ${this._displayName} as a desktop file, will treat it as a regular file.`);
+ this._isDesktopFile = false;
+ }
+ }
+
+ if (this.displayName != oldLabelText) {
+ this._label.text = this.displayName;
+ }
+
+ this._fileType = fileInfo.get_file_type();
+ this._isDirectory = this._fileType == Gio.FileType.DIRECTORY;
+ this._isSpecial = this._fileExtra != Prefs.FileType.NONE;
+ this._isHidden = fileInfo.get_is_hidden() | fileInfo.get_is_backup();
+ this._isSymlink = fileInfo.get_is_symlink();
+ this._modifiedTime = this._fileInfo.get_attribute_uint64("time::modified");
+ /*
+ * This is a glib trick to detect broken symlinks. If a file is a symlink, the filetype
+ * points to the final file, unless it is broken; thus if the file type is SYMBOLIC_LINK,
+ * it must be a broken link.
+ * https://developer.gnome.org/gio/stable/GFile.html#g-file-query-info
+ */
+ this._isBrokenSymlink = this._isSymlink && this._fileType == Gio.FileType.SYMBOLIC_LINK
+ }
+
+ onFileRenamed(file) {
+ this._file = file;
+ this._refreshMetadataAsync(false);
+ }
+
+ _updateIcon() {
+ if (this._fileExtra == Prefs.FileType.USER_DIRECTORY_TRASH) {
+ this._icon.child = this._createEmblemedStIcon(this._fileInfo.get_icon(), null);
+ return;
+ }
+
+ let thumbnailFactory = GnomeDesktop.DesktopThumbnailFactory.new(GnomeDesktop.DesktopThumbnailSize.LARGE);
+ if (thumbnailFactory.can_thumbnail(this._file.get_uri(),
+ this._attributeContentType,
+ this._modifiedTime)) {
+ let thumbnail = thumbnailFactory.lookup(this._file.get_uri(), this._modifiedTime);
+ if (thumbnail == null) {
+ if (!thumbnailFactory.has_valid_failed_thumbnail(this._file.get_uri(),
+ this._modifiedTime)) {
+ let argv = [];
+ argv.push(GLib.build_filenamev([ExtensionUtils.getCurrentExtension().path,
+ 'createThumbnail.js']));
+ argv.push(this._file.get_path());
+ let [success, pid] = GLib.spawn_async(null, argv, null,
+ GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD, null);
+ if (this._thumbnailScriptWatch)
+ GLib.source_remove(this._thumbnailScriptWatch);
+ this._thumbnailScriptWatch = GLib.child_watch_add(GLib.PRIORITY_DEFAULT,
+ pid,
+ (pid, exitCode) => {
+ if (exitCode == 0)
+ this._updateIcon();
+ else
+ global.log('Failed to generate thumbnail for ' + this._filePath);
+ GLib.spawn_close_pid(pid);
+ return false;
+ }
+ );
+ }
+ } else {
+ if (this._loadThumbnailDataCancellable)
+ this._loadThumbnailDataCancellable.cancel();
+ this._loadThumbnailDataCancellable = new Gio.Cancellable();
+ let thumbnailFile = Gio.File.new_for_path(thumbnail);
+ thumbnailFile.load_bytes_async(this._loadThumbnailDataCancellable,
+ (source, result) => {
+ try {
+ let [thumbnailData, etag_out] = source.load_bytes_finish(result);
+ let thumbnailStream = Gio.MemoryInputStream.new_from_bytes(thumbnailData);
+ let thumbnailPixbuf = GdkPixbuf.Pixbuf.new_from_stream(thumbnailStream, null);
+
+ if (thumbnailPixbuf != null) {
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ let thumbnailImage = new Clutter.Image();
+ thumbnailImage.set_data(thumbnailPixbuf.get_pixels(),
+ thumbnailPixbuf.has_alpha ? Cogl.PixelFormat.RGBA_8888 : Cogl.PixelFormat.RGB_888,
+ thumbnailPixbuf.width,
+ thumbnailPixbuf.height,
+ thumbnailPixbuf.rowstride
+ );
+ let icon = new Clutter.Actor();
+ icon.set_content(thumbnailImage);
+ let width = Prefs.get_desired_width(scaleFactor);
+ let height = Prefs.get_icon_size() * scaleFactor;
+ let aspectRatio = thumbnailPixbuf.width / thumbnailPixbuf.height;
+ if ((width / height) > aspectRatio)
+ icon.set_size(height * aspectRatio, height);
+ else
+ icon.set_size(width, width / aspectRatio);
+ this._icon.child = icon;
+ }
+ } catch (error) {
+ if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+ global.log('Error while loading thumbnail: ' + error);
+ this._icon.child = this._createEmblemedStIcon(this._fileInfo.get_icon(), null);
+ }
+ }
+ }
+ );
+ }
+ }
+
+ if (this._isBrokenSymlink) {
+ this._icon.child = this._createEmblemedStIcon(null, 'text-x-generic');
+ } else {
+ if (this.trustedDesktopFile && this._desktopFile.has_key('Icon'))
+ this._icon.child = this._createEmblemedStIcon(null, this._desktopFile.get_string('Icon'));
+ else
+ this._icon.child = this._createEmblemedStIcon(this._fileInfo.get_icon(), null);
+ }
+ }
+
+ _refreshTrashIcon() {
+ if (this._queryTrashInfoCancellable)
+ this._queryTrashInfoCancellable.cancel();
+ this._queryTrashInfoCancellable = new Gio.Cancellable();
+
+ this._file.query_info_async(DesktopIconsUtil.DEFAULT_ATTRIBUTES,
+ Gio.FileQueryInfoFlags.NONE,
+ GLib.PRIORITY_DEFAULT,
+ this._queryTrashInfoCancellable,
+ (source, result) => {
+ try {
+ this._fileInfo = source.query_info_finish(result);
+ this._queryTrashInfoCancellable = null;
+ this._updateIcon();
+ } catch(error) {
+ if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ global.log('Error getting the number of files in the trash: ' + error);
+ }
+ });
+
+ this._scheduleTrashRefreshId = 0;
+ return false;
+ }
+
+ get file() {
+ return this._file;
+ }
+
+ get isHidden() {
+ return this._isHidden;
+ }
+
+ _createEmblemedStIcon(icon, iconName) {
+ if (icon == null) {
+ if (GLib.path_is_absolute(iconName)) {
+ let iconFile = Gio.File.new_for_commandline_arg(iconName);
+ icon = new Gio.FileIcon({ file: iconFile });
+ } else {
+ icon = Gio.ThemedIcon.new_with_default_fallbacks(iconName);
+ }
+ }
+ let itemIcon = Gio.EmblemedIcon.new(icon, null);
+
+ if (this._isSymlink) {
+ if (this._isBrokenSymlink)
+ itemIcon.add_emblem(Gio.Emblem.new(Gio.ThemedIcon.new('emblem-unreadable')));
+ else
+ itemIcon.add_emblem(Gio.Emblem.new(Gio.ThemedIcon.new('emblem-symbolic-link')));
+ } else if (this.trustedDesktopFile) {
+ itemIcon.add_emblem(Gio.Emblem.new(Gio.ThemedIcon.new('emblem-symbolic-link')));
+ }
+
+ return new St.Icon({ gicon: itemIcon,
+ icon_size: Prefs.get_icon_size()
+ });
+ }
+
+ doRename() {
+ if (!this.canRename()) {
+ log (`Error: ${this.file.get_uri()} cannot be renamed`);
+ return;
+ }
+
+ this.emit('rename-clicked');
+ }
+
+ doOpen() {
+ if (this._isBrokenSymlink) {
+ log(`Error: Can’t open ${this.file.get_uri()} because it is a broken symlink.`);
+ return;
+ }
+
+ if (this.trustedDesktopFile) {
+ this._desktopFile.launch_uris_as_manager([], null, GLib.SpawnFlags.SEARCH_PATH, null, null);
+ return;
+ }
+
+ if (this._attributeCanExecute && !this._isDirectory && !this._isDesktopFile) {
+ if (this._execLine)
+ Util.spawnCommandLine(this._execLine);
+ return;
+ }
+
+ Gio.AppInfo.launch_default_for_uri_async(this.file.get_uri(),
+ null, null,
+ (source, result) => {
+ try {
+ Gio.AppInfo.launch_default_for_uri_finish(result);
+ } catch (e) {
+ log('Error opening file ' + this.file.get_uri() + ': ' + e.message);
+ }
+ }
+ );
+ }
+
+ _onCopyClicked() {
+ Extension.desktopManager.doCopy();
+ }
+
+ _onCutClicked() {
+ Extension.desktopManager.doCut();
+ }
+
+ _onShowInFilesClicked() {
+
+ DBusUtils.FreeDesktopFileManagerProxy.ShowItemsRemote([this.file.get_uri()], '',
+ (result, error) => {
+ if (error)
+ log('Error showing file on desktop: ' + error.message);
+ }
+ );
+ }
+
+ _onPropertiesClicked() {
+
+ DBusUtils.FreeDesktopFileManagerProxy.ShowItemPropertiesRemote([this.file.get_uri()], '',
+ (result, error) => {
+ if (error)
+ log('Error showing properties: ' + error.message);
+ }
+ );
+ }
+
+ _onMoveToTrashClicked() {
+ Extension.desktopManager.doTrash();
+ }
+
+ _onEmptyTrashClicked() {
+ Extension.desktopManager.doEmptyTrash();
+ }
+
+ get _allowLaunchingText() {
+ if (this.trustedDesktopFile)
+ return _("Don’t Allow Launching");
+
+ return _("Allow Launching");
+ }
+
+ get metadataTrusted() {
+ return this._trusted;
+ }
+
+ set metadataTrusted(value) {
+ this._trusted = value;
+
+ let info = new Gio.FileInfo();
+ info.set_attribute_string('metadata::trusted',
+ value ? 'true' : 'false');
+ this._file.set_attributes_async(info,
+ Gio.FileQueryInfoFlags.NONE,
+ GLib.PRIORITY_LOW,
+ null,
+ (source, result) => {
+ try {
+ source.set_attributes_finish(result);
+ this._refreshMetadataAsync(true);
+ } catch(e) {
+ log(`Failed to set metadata::trusted: ${e.message}`);
+ }
+ });
+ }
+
+ _onAllowDisallowLaunchingClicked() {
+ this.metadataTrusted = !this.trustedDesktopFile;
+
+ /*
+ * we're marking as trusted, make the file executable too. note that we
+ * do not ever remove the executable bit, since we don't know who set
+ * it.
+ */
+ if (this.metadataTrusted && !this._attributeCanExecute) {
+ let info = new Gio.FileInfo();
+ let newUnixMode = this._unixmode | S_IXUSR;
+ info.set_attribute_uint32(Gio.FILE_ATTRIBUTE_UNIX_MODE, newUnixMode);
+ this._file.set_attributes_async(info,
+ Gio.FileQueryInfoFlags.NONE,
+ GLib.PRIORITY_LOW,
+ null,
+ (source, result) => {
+ try {
+ source.set_attributes_finish (result);
+ } catch(e) {
+ log(`Failed to set unix mode: ${e.message}`);
+ }
+ });
+ }
+ }
+
+ canRename() {
+ return !this.trustedDesktopFile && this._fileExtra == Prefs.FileType.NONE;
+ }
+
+ _doOpenWith() {
+ DBusUtils.openFileWithOtherApplication(this.file.get_path());
+ }
+
+ _getSelectionStyle() {
+ let rgba = DesktopIconsUtil.getGtkClassBackgroundColor('view', Gtk.StateFlags.SELECTED);
+ let background_color =
+ 'rgba(' + rgba.red * 255 + ', ' + rgba.green * 255 + ', ' + rgba.blue * 255 + ', 0.6)';
+ let border_color =
+ 'rgba(' + rgba.red * 255 + ', ' + rgba.green * 255 + ', ' + rgba.blue * 255 + ', 0.8)';
+
+ return 'background-color: ' + background_color + ';' +
+ 'border-color: ' + border_color + ';'
+ }
+
+ _createMenu() {
+ this._menuManager = new PopupMenu.PopupMenuManager({ actor: this.actor });
+ let side = St.Side.LEFT;
+ if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
+ side = St.Side.RIGHT;
+ this._menu = new PopupMenu.PopupMenu(this.actor, 0.5, side);
+ this._menu.addAction(_('Open'), () => this.doOpen());
+ switch (this._fileExtra) {
+ case Prefs.FileType.NONE:
+ if (!this._isDirectory)
+ this._actionOpenWith = this._menu.addAction(_('Open With Other Application'), () => this._doOpenWith());
+ else
+ this._actionOpenWith = null;
+ this._menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ this._actionCut = this._menu.addAction(_('Cut'), () => this._onCutClicked());
+ this._actionCopy = this._menu.addAction(_('Copy'), () => this._onCopyClicked());
+ if (this.canRename())
+ this._menu.addAction(_('Rename…'), () => this.doRename());
+ this._actionTrash = this._menu.addAction(_('Move to Trash'), () => this._onMoveToTrashClicked());
+ if (this._isDesktopFile && !Extension.desktopManager.writableByOthers && !this._writableByOthers) {
+ this._menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ this._allowLaunchingMenuItem = this._menu.addAction(this._allowLaunchingText,
+ () => this._onAllowDisallowLaunchingClicked());
+
+ }
+ break;
+ case Prefs.FileType.USER_DIRECTORY_TRASH:
+ this._menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ this._menu.addAction(_('Empty Trash'), () => this._onEmptyTrashClicked());
+ break;
+ default:
+ break;
+ }
+ this._menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ this._menu.addAction(_('Properties'), () => this._onPropertiesClicked());
+ this._menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ this._menu.addAction(_('Show in Files'), () => this._onShowInFilesClicked());
+ if (this._isDirectory && this.file.get_path() != null)
+ this._actionOpenInTerminal = this._menu.addAction(_('Open in Terminal'), () => this._onOpenTerminalClicked());
+
+ this._menuManager.addMenu(this._menu);
+
+ Main.layoutManager.uiGroup.add_child(this._menu.actor);
+ this._menu.actor.hide();
+ }
+
+ _onOpenTerminalClicked () {
+ DesktopIconsUtil.launchTerminal(this.file.get_path());
+ }
+
+ _onPressButton(actor, event) {
+ let button = event.get_button();
+ if (button == 3) {
+ if (!this.isSelected)
+ this.emit('selected', false, false, true);
+ this._menu.toggle();
+ if (this._actionOpenWith) {
+ let allowOpenWith = (Extension.desktopManager.getNumberOfSelectedItems() == 1);
+ this._actionOpenWith.setSensitive(allowOpenWith);
+ }
+ let specialFilesSelected = Extension.desktopManager.checkIfSpecialFilesAreSelected();
+ if (this._actionCut)
+ this._actionCut.setSensitive(!specialFilesSelected);
+ if (this._actionCopy)
+ this._actionCopy.setSensitive(!specialFilesSelected);
+ if (this._actionTrash)
+ this._actionTrash.setSensitive(!specialFilesSelected);
+ return Clutter.EVENT_STOP;
+ } else if (button == 1) {
+ if (event.get_click_count() == 1) {
+ let [x, y] = event.get_coords();
+ this._primaryButtonPressed = true;
+ this._buttonPressInitialX = x;
+ this._buttonPressInitialY = y;
+ let shiftPressed = !!(event.get_state() & Clutter.ModifierType.SHIFT_MASK);
+ let controlPressed = !!(event.get_state() & Clutter.ModifierType.CONTROL_MASK);
+ if (!this.isSelected) {
+ this.emit('selected', shiftPressed || controlPressed, false, true);
+ }
+ }
+ return Clutter.EVENT_STOP;
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _onLeave(actor, event) {
+ this._primaryButtonPressed = false;
+ }
+
+ _onMotion(actor, event) {
+ let [x, y] = event.get_coords();
+ if (this._primaryButtonPressed) {
+ let xDiff = x - this._buttonPressInitialX;
+ let yDiff = y - this._buttonPressInitialY;
+ let distance = Math.sqrt(Math.pow(xDiff, 2) + Math.pow(yDiff, 2));
+ if (distance > DRAG_TRESHOLD) {
+ // Don't need to track anymore this if we start drag, and also
+ // avoids reentrance here
+ this._primaryButtonPressed = false;
+ let event = Clutter.get_current_event();
+ let [x, y] = event.get_coords();
+ Extension.desktopManager.dragStart();
+ }
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _onReleaseButton(actor, event) {
+ let button = event.get_button();
+ if (button == 1) {
+ // primaryButtonPressed is TRUE only if the user has pressed the button
+ // over an icon, and if (s)he has not started a drag&drop operation
+ if (this._primaryButtonPressed) {
+ this._primaryButtonPressed = false;
+ let shiftPressed = !!(event.get_state() & Clutter.ModifierType.SHIFT_MASK);
+ let controlPressed = !!(event.get_state() & Clutter.ModifierType.CONTROL_MASK);
+ if ((event.get_click_count() == 1) && Prefs.CLICK_POLICY_SINGLE && !shiftPressed && !controlPressed)
+ this.doOpen();
+ this.emit('selected', shiftPressed || controlPressed, false, true);
+ return Clutter.EVENT_STOP;
+ }
+ if ((event.get_click_count() == 2) && (!Prefs.CLICK_POLICY_SINGLE))
+ this.doOpen();
+ }
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ get savedCoordinates() {
+ return this._savedCoordinates;
+ }
+
+ _onSetMetadataFileFinished(source, result) {
+ try {
+ let [success, info] = source.set_attributes_finish(result);
+ } catch (error) {
+ if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ log('Error setting metadata to desktop files ', error);
+ }
+ }
+
+ set savedCoordinates(pos) {
+ if (this._setMetadataCancellable)
+ this._setMetadataCancellable.cancel();
+
+ this._setMetadataCancellable = new Gio.Cancellable();
+ this._savedCoordinates = [pos[0], pos[1]];
+ let info = new Gio.FileInfo();
+ info.set_attribute_string('metadata::nautilus-icon-position',
+ `${pos[0]},${pos[1]}`);
+ this.file.set_attributes_async(info,
+ Gio.FileQueryInfoFlags.NONE,
+ GLib.PRIORITY_DEFAULT,
+ this._setMetadataCancellable,
+ (source, result) => this._onSetMetadataFileFinished(source, result)
+ );
+ }
+
+ intersectsWith(argX, argY, argWidth, argHeight) {
+ let rect = new Meta.Rectangle({ x: argX, y: argY, width: argWidth, height: argHeight });
+ let [containerX, containerY] = this._container.get_transformed_position();
+ let boundingBox = new Meta.Rectangle({ x: containerX,
+ y: containerY,
+ width: this._container.allocation.x2 - this._container.allocation.x1,
+ height: this._container.allocation.y2 - this._container.allocation.y1 });
+ let [intersects, _] = rect.intersect(boundingBox);
+
+ return intersects;
+ }
+
+ set isSelected(isSelected) {
+ isSelected = !!isSelected;
+ if (isSelected == this._isSelected)
+ return;
+
+ if (isSelected) {
+ this._container.set_style(this._getSelectionStyle());
+ } else {
+ this._container.set_style('background-color: transparent');
+ this._container.set_style('border-color: transparent');
+ }
+
+ this._isSelected = isSelected;
+ }
+
+ get isSelected() {
+ return this._isSelected;
+ }
+
+ get isSpecial() {
+ return this._isSpecial;
+ }
+
+ get state() {
+ return this._state;
+ }
+
+ set state(state) {
+ if (state == this._state)
+ return;
+
+ this._state = state;
+ }
+
+ get isDirectory() {
+ return this._isDirectory;
+ }
+
+ get trustedDesktopFile() {
+ return this._isDesktopFile &&
+ this._attributeCanExecute &&
+ this.metadataTrusted &&
+ !Extension.desktopManager.writableByOthers &&
+ !this._writableByOthers;
+ }
+
+ get fileName() {
+ return this._fileInfo.get_name();
+ }
+
+ get displayName() {
+ if (this.trustedDesktopFile)
+ return this._desktopFile.get_name();
+
+ return this._displayName || null;
+ }
+
+ acceptDrop() {
+ return Extension.desktopManager.selectionDropOnFileItem(this);
+ }
+};
+Signals.addSignalMethods(FileItem.prototype);
diff --git a/extensions/desktop-icons/meson.build b/extensions/desktop-icons/meson.build
new file mode 100644
index 0000000..8431af6
--- /dev/null
+++ b/extensions/desktop-icons/meson.build
@@ -0,0 +1,19 @@
+extension_data += configure_file(
+ input: metadata_name + '.in',
+ output: metadata_name,
+ configuration: metadata_conf
+)
+
+extension_schemas += files(join_paths('schemas', metadata_conf.get('gschemaname') + '.gschema.xml'))
+
+extension_sources += files(
+ 'createFolderDialog.js',
+ 'createThumbnail.js',
+ 'dbusUtils.js',
+ 'desktopGrid.js',
+ 'desktopIconsUtil.js',
+ 'desktopManager.js',
+ 'extension.js',
+ 'fileItem.js',
+ 'prefs.js'
+)
diff --git a/extensions/desktop-icons/metadata.json.in b/extensions/desktop-icons/metadata.json.in
new file mode 100644
index 0000000..78cabf0
--- /dev/null
+++ b/extensions/desktop-icons/metadata.json.in
@@ -0,0 +1,11 @@
+{
+"extension-id": "@extension_id@",
+"uuid": "@uuid@",
+"settings-schema": "@gschemaname@",
+"gettext-domain": "@gettext_domain@",
+"name": "Desktop Icons",
+"description": "Provide desktop icons support for classic mode",
+"original-authors": [ "csoriano@redhat.com" ],
+"shell-version": [ "@shell_current@" ],
+"url": "@url@"
+}
diff --git a/extensions/desktop-icons/po/LINGUAS b/extensions/desktop-icons/po/LINGUAS
new file mode 100644
index 0000000..65b7521
--- /dev/null
+++ b/extensions/desktop-icons/po/LINGUAS
@@ -0,0 +1,19 @@
+cs
+da
+de
+es
+fi
+fr
+fur
+hr
+hu
+id
+it
+ja
+nl
+pl
+pt_BR
+ru
+sv
+tr
+zh_TW
diff --git a/extensions/desktop-icons/po/POTFILES.in b/extensions/desktop-icons/po/POTFILES.in
new file mode 100644
index 0000000..7c2ebd3
--- /dev/null
+++ b/extensions/desktop-icons/po/POTFILES.in
@@ -0,0 +1,4 @@
+prefs.js
+desktopGrid.js
+fileItem.js
+schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml
\ No newline at end of file
diff --git a/extensions/desktop-icons/po/cs.po b/extensions/desktop-icons/po/cs.po
new file mode 100644
index 0000000..f5f9db4
--- /dev/null
+++ b/extensions/desktop-icons/po/cs.po
@@ -0,0 +1,195 @@
+# Czech translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Marek Černocký <marek@manet.cz>, 2018.
+# Milan Zink <zeten30@gmail.com>, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2019-03-01 12:49+0000\n"
+"PO-Revision-Date: 2019-03-02 18:02+0100\n"
+"Last-Translator: Daniel Rusek <mail@asciiwolf.com>\n"
+"Language-Team: Czech <zeten30@gmail.com>\n"
+"Language: cs\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==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+"X-Generator: Poedit 2.2.1\n"
+
+#: createFolderDialog.js:48
+msgid "New folder name"
+msgstr "Název nové složky"
+
+#: createFolderDialog.js:72
+msgid "Create"
+msgstr "Vytvořit"
+
+#: createFolderDialog.js:74 desktopGrid.js:586
+msgid "Cancel"
+msgstr "Zrušit"
+
+#: createFolderDialog.js:145
+msgid "Folder names cannot contain “/”."
+msgstr "Názvy složek nesmí obsahovat „/“."
+
+#: createFolderDialog.js:148
+msgid "A folder cannot be called “.”."
+msgstr "Složka se nemůže jmenovat „.“."
+
+#: createFolderDialog.js:151
+msgid "A folder cannot be called “..”."
+msgstr "Složka se nemůže jmenovat „..“."
+
+#: createFolderDialog.js:153
+msgid "Folders with “.” at the beginning of their name are hidden."
+msgstr "Složky s „.“ na začátku jejich názvu jsou skryty."
+
+#: createFolderDialog.js:155
+msgid "There is already a file or folder with that name."
+msgstr "Soubor nebo složka s tímto názvem již existuje."
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Velikost ikon na pracovní ploše"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "malé"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "standardní"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "velké"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Zobrazovat osobní složku na pracovní ploše"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Zobrazovat ikonu koše na pracovní ploše"
+
+#: desktopGrid.js:320
+msgid "New Folder"
+msgstr "Nová složka"
+
+#: desktopGrid.js:322
+msgid "Paste"
+msgstr "Vložit"
+
+#: desktopGrid.js:323
+msgid "Undo"
+msgstr "Zpět"
+
+#: desktopGrid.js:324
+msgid "Redo"
+msgstr "Znovu"
+
+#: desktopGrid.js:326
+msgid "Show Desktop in Files"
+msgstr "Zobrazit plochu v Souborech"
+
+#: desktopGrid.js:327 fileItem.js:606
+msgid "Open in Terminal"
+msgstr "Otevřít v terminálu"
+
+#: desktopGrid.js:329
+msgid "Change Background…"
+msgstr "Změnit pozadí…"
+
+#: desktopGrid.js:331
+msgid "Display Settings"
+msgstr "Zobrazit nastavení"
+
+#: desktopGrid.js:332
+msgid "Settings"
+msgstr "Nastavení"
+
+#: desktopGrid.js:576
+msgid "Enter file name…"
+msgstr "Zadejte název souboru…"
+
+#: desktopGrid.js:580
+msgid "OK"
+msgstr "Budiž"
+
+#: fileItem.js:490
+msgid "Don’t Allow Launching"
+msgstr "Nepovolit spouštění"
+
+#: fileItem.js:492
+msgid "Allow Launching"
+msgstr "Povolit spouštění"
+
+#: fileItem.js:574
+msgid "Open"
+msgstr "Otevřít"
+
+#: fileItem.js:578
+msgid "Open With Other Application"
+msgstr "Otevřít pomocí jiné aplikace"
+
+#: fileItem.js:582
+msgid "Cut"
+msgstr "Vyjmout"
+
+#: fileItem.js:583
+msgid "Copy"
+msgstr "Kopírovat"
+
+#: fileItem.js:585
+msgid "Rename…"
+msgstr "Přejmenovat…"
+
+#: fileItem.js:586
+msgid "Move to Trash"
+msgstr "Přesunout do koše"
+
+#: fileItem.js:596
+msgid "Empty Trash"
+msgstr "Vyprázdnit koš"
+
+#: fileItem.js:602
+msgid "Properties"
+msgstr "Vlastnosti"
+
+#: fileItem.js:604
+msgid "Show in Files"
+msgstr "Zobrazit v Souborech"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:11
+msgid "Icon size"
+msgstr "Velikost ikon"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Set the size for the desktop icons."
+msgstr "Nastavit velikost pro ikony na pracovní ploše."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:16
+msgid "Show personal folder"
+msgstr "Zobrazovat osobní složku"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show the personal folder in the desktop."
+msgstr "Zobrazovat osobní složku na pracovní ploše."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:21
+msgid "Show trash icon"
+msgstr "Zobrazovat koš"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show the trash icon in the desktop."
+msgstr "Zobrazovat ikonu koše na pracovní ploše."
+
+#~ msgid "Huge"
+#~ msgstr "obrovské"
+
+#~ msgid "Ok"
+#~ msgstr "Ok"
diff --git a/extensions/desktop-icons/po/da.po b/extensions/desktop-icons/po/da.po
new file mode 100644
index 0000000..d3f51f0
--- /dev/null
+++ b/extensions/desktop-icons/po/da.po
@@ -0,0 +1,159 @@
+# Danish translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Alan Mortensen <alanmortensen.am@gmail.com>, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2019-01-15 10:59+0000\n"
+"PO-Revision-Date: 2019-02-09 11:35+0100\n"
+"Last-Translator: Alan Mortensen <alanmortensen.am@gmail.com>\n"
+"Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
+"Language: da\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.0.6\n"
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Størrelsen på skrivebordsikoner"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Små"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Standard"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Store"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Vis den personlige mappe på skrivebordet"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Vis papirkurvsikonet på skrivebordet"
+
+#: desktopGrid.js:187 desktopGrid.js:306
+msgid "New Folder"
+msgstr "Ny mappe"
+
+#: desktopGrid.js:308
+msgid "Paste"
+msgstr "Indsæt"
+
+#: desktopGrid.js:309
+msgid "Undo"
+msgstr "Fortryd"
+
+#: desktopGrid.js:310
+msgid "Redo"
+msgstr "Omgør"
+
+#: desktopGrid.js:312
+msgid "Show Desktop in Files"
+msgstr "Vis skrivebordet i Filer"
+
+#: desktopGrid.js:313 fileItem.js:586
+msgid "Open in Terminal"
+msgstr "Åbn i terminal"
+
+#: desktopGrid.js:315
+msgid "Change Background…"
+msgstr "Skift baggrund …"
+
+#: desktopGrid.js:317
+msgid "Display Settings"
+msgstr "Skærmindstillinger"
+
+#: desktopGrid.js:318
+msgid "Settings"
+msgstr "Indstillinger"
+
+#: desktopGrid.js:559
+msgid "Enter file name…"
+msgstr "Indtast filnavn …"
+
+#: desktopGrid.js:563
+msgid "OK"
+msgstr "OK"
+
+#: desktopGrid.js:569
+msgid "Cancel"
+msgstr "Annullér"
+
+#: fileItem.js:490
+msgid "Don’t Allow Launching"
+msgstr "Tillad ikke opstart"
+
+#: fileItem.js:492
+msgid "Allow Launching"
+msgstr "Tillad opstart"
+
+#: fileItem.js:559
+msgid "Open"
+msgstr "Åbn"
+
+#: fileItem.js:562
+msgid "Cut"
+msgstr "Klip"
+
+#: fileItem.js:563
+msgid "Copy"
+msgstr "Kopiér"
+
+#: fileItem.js:565
+msgid "Rename…"
+msgstr "Omdøb …"
+
+#: fileItem.js:566
+msgid "Move to Trash"
+msgstr "Flyt til papirkurven"
+
+#: fileItem.js:576
+msgid "Empty Trash"
+msgstr "Tøm papirkurven"
+
+#: fileItem.js:582
+msgid "Properties"
+msgstr "Egenskaber"
+
+#: fileItem.js:584
+msgid "Show in Files"
+msgstr "Vis i Filer"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:11
+msgid "Icon size"
+msgstr "Ikonstørrelse"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Set the size for the desktop icons."
+msgstr "Angiv størrelsen på skrivebordsikoner."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:16
+msgid "Show personal folder"
+msgstr "Vis personlig mappe"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show the personal folder in the desktop."
+msgstr "Vis den personlige mappe på skrivebordet."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:21
+msgid "Show trash icon"
+msgstr "Vis papirkurvsikon"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show the trash icon in the desktop."
+msgstr "Vis papirkurvsikonet på skrivebordet."
+
+#~ msgid "Huge"
+#~ msgstr "Enorme"
diff --git a/extensions/desktop-icons/po/de.po b/extensions/desktop-icons/po/de.po
new file mode 100644
index 0000000..ed5a257
--- /dev/null
+++ b/extensions/desktop-icons/po/de.po
@@ -0,0 +1,192 @@
+# German translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Mario Blättermann <mario.blaettermann@gmail.com>, 2018.
+# rugk <rugk+i18n@posteo.de>, 2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2019-03-07 22:46+0000\n"
+"PO-Revision-Date: 2019-03-09 15:42+0100\n"
+"Last-Translator: Tim Sabsch <tim@sabsch.com>\n"
+"Language-Team: German <gnome-de@gnome.org>\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.2.1\n"
+
+#: createFolderDialog.js:48
+msgid "New folder name"
+msgstr "Neuer Ordner"
+
+#: createFolderDialog.js:72
+msgid "Create"
+msgstr "Erstellen"
+
+#: createFolderDialog.js:74 desktopGrid.js:586
+msgid "Cancel"
+msgstr "Abbrechen"
+
+#: createFolderDialog.js:145
+msgid "Folder names cannot contain “/”."
+msgstr "Ordnernamen dürfen kein »/« enthalten."
+
+#: createFolderDialog.js:148
+msgid "A folder cannot be called “.”."
+msgstr "Ein Ordner darf nicht ».« genannt werden."
+
+#: createFolderDialog.js:151
+msgid "A folder cannot be called “..”."
+msgstr "Ein Ordner darf nicht »..« genannt werden."
+
+#: createFolderDialog.js:153
+msgid "Folders with “.” at the beginning of their name are hidden."
+msgstr "Ordner mit ».« am Anfang sind verborgen."
+
+#: createFolderDialog.js:155
+msgid "There is already a file or folder with that name."
+msgstr "Es gibt bereits eine Datei oder einen Ordner mit diesem Namen."
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Größe der Arbeitsflächensymbole"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Klein"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Standard"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Groß"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Den persönlichen Ordner auf der Arbeitsfläche anzeigen"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Papierkorb-Symbol auf der Arbeitsfläche anzeigen"
+
+#: desktopGrid.js:320
+msgid "New Folder"
+msgstr "Neuer Ordner"
+
+#: desktopGrid.js:322
+msgid "Paste"
+msgstr "Einfügen"
+
+#: desktopGrid.js:323
+msgid "Undo"
+msgstr "Rückgängig"
+
+#: desktopGrid.js:324
+msgid "Redo"
+msgstr "Wiederholen"
+
+#: desktopGrid.js:326
+msgid "Show Desktop in Files"
+msgstr "Schreibtisch in Dateien anzeigen"
+
+#: desktopGrid.js:327 fileItem.js:606
+msgid "Open in Terminal"
+msgstr "Im Terminal öffnen"
+
+#: desktopGrid.js:329
+msgid "Change Background…"
+msgstr "Hintergrund ändern …"
+
+#: desktopGrid.js:331
+msgid "Display Settings"
+msgstr "Anzeigeeinstellungen"
+
+#: desktopGrid.js:332
+msgid "Settings"
+msgstr "Einstellungen"
+
+#: desktopGrid.js:576
+msgid "Enter file name…"
+msgstr "Dateinamen eingeben …"
+
+#: desktopGrid.js:580
+msgid "OK"
+msgstr "OK"
+
+#: fileItem.js:490
+msgid "Don’t Allow Launching"
+msgstr "Start nicht erlauben"
+
+#: fileItem.js:492
+msgid "Allow Launching"
+msgstr "Start erlauben"
+
+#: fileItem.js:574
+msgid "Open"
+msgstr "Öffnen"
+
+#: fileItem.js:578
+msgid "Open With Other Application"
+msgstr "Mit anderer Anwendung öffnen"
+
+#: fileItem.js:582
+msgid "Cut"
+msgstr "Ausschneiden"
+
+#: fileItem.js:583
+msgid "Copy"
+msgstr "Kopieren"
+
+#: fileItem.js:585
+msgid "Rename…"
+msgstr "Umbenennen …"
+
+#: fileItem.js:586
+msgid "Move to Trash"
+msgstr "In den Papierkorb verschieben"
+
+#: fileItem.js:596
+msgid "Empty Trash"
+msgstr "Papierkorb leeren"
+
+#: fileItem.js:602
+msgid "Properties"
+msgstr "Eigenschaften"
+
+#: fileItem.js:604
+msgid "Show in Files"
+msgstr "In Dateiverwaltung anzeigen"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:11
+msgid "Icon size"
+msgstr "Symbolgröße"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Set the size for the desktop icons."
+msgstr "Die Größe der Arbeitsflächensymbole festlegen."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:16
+msgid "Show personal folder"
+msgstr "Persönlichen Ordner anzeigen"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show the personal folder in the desktop."
+msgstr "Den persönlichen Ordner auf der Arbeitsfläche anzeigen."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:21
+msgid "Show trash icon"
+msgstr "Papierkorb-Symbol anzeigen"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show the trash icon in the desktop."
+msgstr "Das Papierkorb-Symbol auf der Arbeitsfläche anzeigen."
+
+#~ msgid "Huge"
+#~ msgstr "Riesig"
diff --git a/extensions/desktop-icons/po/es.po b/extensions/desktop-icons/po/es.po
new file mode 100644
index 0000000..8cc87da
--- /dev/null
+++ b/extensions/desktop-icons/po/es.po
@@ -0,0 +1,218 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# Sergio Costas <rastersoft@gmail.com>, 2018.
+# Daniel Mustieles <daniel.mustieles@gmail.com>, 2018-2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: 1.0\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2019-03-01 12:11+0000\n"
+"PO-Revision-Date: 2019-03-04 10:37+0100\n"
+"Last-Translator: Daniel Mustieles <daniel.mustieles@gmail.com>\n"
+"Language-Team: es <gnome-es-list@gnome.org>\n"
+"Language: es\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Gtranslator 3.31.90\n"
+
+#: createFolderDialog.js:48
+#| msgid "New Folder"
+msgid "New folder name"
+msgstr "Nombre de la nueva carpeta"
+
+#: createFolderDialog.js:72
+msgid "Create"
+msgstr "Crear"
+
+#: createFolderDialog.js:74 desktopGrid.js:586
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: createFolderDialog.js:145
+msgid "Folder names cannot contain “/”."
+msgstr "Los nombres de carpetas no pueden contener «/»."
+
+#: createFolderDialog.js:148
+msgid "A folder cannot be called “.”."
+msgstr "Una carpeta no se puede llamar «.»."
+
+#: createFolderDialog.js:151
+msgid "A folder cannot be called “..”."
+msgstr "Una carpeta no se puede llamar «..»."
+
+#: createFolderDialog.js:153
+msgid "Folders with “.” at the beginning of their name are hidden."
+msgstr "Las carpetas cuyo nombre empieza por «.» están ocultas."
+
+#: createFolderDialog.js:155
+msgid "There is already a file or folder with that name."
+msgstr "Ya hay un archivo o carpeta con ese nombre."
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Tamaño de los iconos del escritorio"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Pequeño"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Estándar"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Grande"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Mostrar la carpeta personal en el escritorio"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Mostrar la papelera en el escritorio"
+
+#: desktopGrid.js:320
+msgid "New Folder"
+msgstr "Nueva carpeta"
+
+#: desktopGrid.js:322
+msgid "Paste"
+msgstr "Pegar"
+
+#: desktopGrid.js:323
+msgid "Undo"
+msgstr "Deshacer"
+
+#: desktopGrid.js:324
+msgid "Redo"
+msgstr "Rehacer"
+
+#: desktopGrid.js:326
+msgid "Show Desktop in Files"
+msgstr "Mostrar el escritorio en Archivos"
+
+#: desktopGrid.js:327 fileItem.js:606
+msgid "Open in Terminal"
+msgstr "Abrir en una terminal"
+
+#: desktopGrid.js:329
+msgid "Change Background…"
+msgstr "Cambiar el fondo..."
+
+#: desktopGrid.js:331
+msgid "Display Settings"
+msgstr "Configuración de pantalla"
+
+#: desktopGrid.js:332
+msgid "Settings"
+msgstr "Configuración"
+
+#: desktopGrid.js:576
+msgid "Enter file name…"
+msgstr "Introduzca el nombre del archivo…"
+
+#: desktopGrid.js:580
+msgid "OK"
+msgstr "Aceptar"
+
+#: fileItem.js:490
+msgid "Don’t Allow Launching"
+msgstr "No permitir lanzar"
+
+#: fileItem.js:492
+msgid "Allow Launching"
+msgstr "Permitir lanzar"
+
+#: fileItem.js:574
+msgid "Open"
+msgstr "Abrir"
+
+#: fileItem.js:578
+msgid "Open With Other Application"
+msgstr "Abrir con otra aplicación"
+
+#: fileItem.js:582
+msgid "Cut"
+msgstr "Cortar"
+
+#: fileItem.js:583
+msgid "Copy"
+msgstr "Copiar"
+
+#: fileItem.js:585
+msgid "Rename…"
+msgstr "Renombrar…"
+
+#: fileItem.js:586
+msgid "Move to Trash"
+msgstr "Mover a la papelera"
+
+#: fileItem.js:596
+msgid "Empty Trash"
+msgstr "Vaciar la papelera"
+
+#: fileItem.js:602
+msgid "Properties"
+msgstr "Propiedades"
+
+#: fileItem.js:604
+msgid "Show in Files"
+msgstr "Mostrar en Files"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:11
+msgid "Icon size"
+msgstr "Tamaño de los iconos"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Set the size for the desktop icons."
+msgstr "Establece el tamaño de los iconos del escritorio."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:16
+msgid "Show personal folder"
+msgstr "Mostrar la carpeta personal"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show the personal folder in the desktop."
+msgstr "Mostrar la carpeta personal en el escritorio."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:21
+msgid "Show trash icon"
+msgstr "Mostrar la papelera"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show the trash icon in the desktop."
+msgstr "Mostrar la papelera en el escritorio."
+
+#~ msgid "Huge"
+#~ msgstr "Inmenso"
+
+#~ msgid "Ok"
+#~ msgstr "Aceptar"
+
+#~ msgid "huge"
+#~ msgstr "inmenso"
+
+#~ msgid "Maximum width for the icons and filename."
+#~ msgstr "Ancho máximo de los iconos y el nombre de fichero."
+
+#~ msgid "Shows the Documents folder in the desktop."
+#~ msgstr "Muestra la carpeta Documentos en el escritorio."
+
+#~ msgid "Shows the Downloads folder in the desktop."
+#~ msgstr "Muestra la carpeta Descargas en el escritorio."
+
+#~ msgid "Shows the Music folder in the desktop."
+#~ msgstr "Muestra la carpeta Música en el escritorio."
+
+#~ msgid "Shows the Pictures folder in the desktop."
+#~ msgstr "Muestra la carpeta Imágenes en el escritorio."
+
+#~ msgid "Shows the Videos folder in the desktop."
+#~ msgstr "Muestra la carpeta Vídeos en el escritorio."
diff --git a/extensions/desktop-icons/po/fi.po b/extensions/desktop-icons/po/fi.po
new file mode 100644
index 0000000..71ac40d
--- /dev/null
+++ b/extensions/desktop-icons/po/fi.po
@@ -0,0 +1,191 @@
+# Finnish translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Jiri Grönroos <jiri.gronroos@iki.fi>, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2019-03-01 12:11+0000\n"
+"PO-Revision-Date: 2019-03-02 11:29+0200\n"
+"Last-Translator: Jiri Grönroos <jiri.gronroos+l10n@iki.fi>\n"
+"Language-Team: Finnish <lokalisointi-lista@googlegroups.com>\n"
+"Language: fi\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.0.6\n"
+
+#: createFolderDialog.js:48
+msgid "New folder name"
+msgstr "Uusi kansion nimi"
+
+#: createFolderDialog.js:72
+msgid "Create"
+msgstr "Luo"
+
+#: createFolderDialog.js:74 desktopGrid.js:586
+msgid "Cancel"
+msgstr "Peru"
+
+#: createFolderDialog.js:145
+msgid "Folder names cannot contain “/”."
+msgstr "Kansion nimi ei voi sisältää merkkiä “/”."
+
+#: createFolderDialog.js:148
+msgid "A folder cannot be called “.”."
+msgstr "Kansion nimi ei voi olla “.”."
+
+#: createFolderDialog.js:151
+msgid "A folder cannot be called “..”."
+msgstr "Kansion nimi ei voi olla “..”."
+
+#: createFolderDialog.js:153
+msgid "Folders with “.” at the beginning of their name are hidden."
+msgstr "Kansiot, joiden nimi alkaa merkillä “.”, ovat piilotettuja."
+
+#: createFolderDialog.js:155
+msgid "There is already a file or folder with that name."
+msgstr "Nimi on jo toisen tiedoston tai kansion käytössä."
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Työpöytäkuvakkeiden koko"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Pieni"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Normaali"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Suuri"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Näytä kotikansio työpöydällä"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Näytä roskakorin kuvake työpöydällä"
+
+#: desktopGrid.js:320
+msgid "New Folder"
+msgstr "Uusi kansio"
+
+#: desktopGrid.js:322
+msgid "Paste"
+msgstr "Liitä"
+
+#: desktopGrid.js:323
+msgid "Undo"
+msgstr "Kumoa"
+
+#: desktopGrid.js:324
+msgid "Redo"
+msgstr "Tee uudeleen"
+
+#: desktopGrid.js:326
+msgid "Show Desktop in Files"
+msgstr "Näytä työpöytä tiedostonhallinnassa"
+
+#: desktopGrid.js:327 fileItem.js:606
+msgid "Open in Terminal"
+msgstr "Avaa päätteessä"
+
+#: desktopGrid.js:329
+msgid "Change Background…"
+msgstr "Vaihda taustakuvaa…"
+
+#: desktopGrid.js:331
+msgid "Display Settings"
+msgstr "Näytön asetukset"
+
+#: desktopGrid.js:332
+msgid "Settings"
+msgstr "Asetukset"
+
+#: desktopGrid.js:576
+msgid "Enter file name…"
+msgstr "Anna tiedostonimi…"
+
+#: desktopGrid.js:580
+msgid "OK"
+msgstr "OK"
+
+#: fileItem.js:490
+msgid "Don’t Allow Launching"
+msgstr "Älä salli käynnistämistä"
+
+#: fileItem.js:492
+msgid "Allow Launching"
+msgstr "Salli käynnistäminen"
+
+#: fileItem.js:574
+msgid "Open"
+msgstr "Avaa"
+
+#: fileItem.js:578
+msgid "Open With Other Application"
+msgstr "Avaa toisella sovelluksella"
+
+#: fileItem.js:582
+msgid "Cut"
+msgstr "Leikkaa"
+
+#: fileItem.js:583
+msgid "Copy"
+msgstr "Kopioi"
+
+#: fileItem.js:585
+msgid "Rename…"
+msgstr "Nimeä uudelleen…"
+
+#: fileItem.js:586
+msgid "Move to Trash"
+msgstr "Siirrä roskakoriin"
+
+#: fileItem.js:596
+msgid "Empty Trash"
+msgstr "Tyhjennä roskakori"
+
+#: fileItem.js:602
+msgid "Properties"
+msgstr "Ominaisuudet"
+
+#: fileItem.js:604
+msgid "Show in Files"
+msgstr "Näytä tiedostonhallinnassa"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:11
+msgid "Icon size"
+msgstr "Kuvakekoko"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Set the size for the desktop icons."
+msgstr "Aseta työpöytäkuvakkeiden koko."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:16
+msgid "Show personal folder"
+msgstr "Näytä kotikansio"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show the personal folder in the desktop."
+msgstr "Näytä kotikansio työpöydällä."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:21
+msgid "Show trash icon"
+msgstr "Näytä roskakorin kuvake"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show the trash icon in the desktop."
+msgstr "Näytä roskakorin kuvake työpöydällä."
+
+#~ msgid "Huge"
+#~ msgstr "Valtava"
diff --git a/extensions/desktop-icons/po/fr.po b/extensions/desktop-icons/po/fr.po
new file mode 100644
index 0000000..13e8f3a
--- /dev/null
+++ b/extensions/desktop-icons/po/fr.po
@@ -0,0 +1,164 @@
+# French translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# ghentdebian <ghent.debian@gmail.com>, 2018.
+# Charles Monzat <charles.monzat@numericable.fr>, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-12-14 09:12+0000\n"
+"PO-Revision-Date: 2018-12-16 17:47+0100\n"
+"Last-Translator: Charles Monzat <charles.monzat@numericable.fr>\n"
+"Language-Team: GNOME French Team <gnomefr@traduc.org>\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+"X-Generator: Gtranslator 3.30.0\n"
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Taille des icônes du bureau"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Petite"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Normale"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Grande"
+
+#: prefs.js:102
+msgid "Huge"
+msgstr "Immense"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Montrer le dossier personnel sur le bureau"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Montrer la corbeille sur le bureau"
+
+#: desktopGrid.js:182 desktopGrid.js:301
+msgid "New Folder"
+msgstr "Nouveau dossier"
+
+#: desktopGrid.js:303
+msgid "Paste"
+msgstr "Coller"
+
+#: desktopGrid.js:304
+msgid "Undo"
+msgstr "Annuler"
+
+#: desktopGrid.js:305
+msgid "Redo"
+msgstr "Refaire"
+
+#: desktopGrid.js:307
+msgid "Open Desktop in Files"
+msgstr "Ouvrir le bureau dans Fichiers"
+
+#: desktopGrid.js:308
+msgid "Open Terminal"
+msgstr "Ouvrir un terminal"
+
+#: desktopGrid.js:310
+msgid "Change Background…"
+msgstr "Changer l’arrière-plan…"
+
+#: desktopGrid.js:311
+msgid "Display Settings"
+msgstr "Configuration d’affichage"
+
+#: desktopGrid.js:312
+msgid "Settings"
+msgstr "Paramètres"
+
+#: desktopGrid.js:568
+msgid "Enter file name…"
+msgstr "Saisir un nom de fichier…"
+
+#: desktopGrid.js:572
+msgid "OK"
+msgstr "Valider"
+
+#: desktopGrid.js:578
+msgid "Cancel"
+msgstr "Annuler"
+
+#: fileItem.js:485
+msgid "Don’t Allow Launching"
+msgstr "Ne pas autoriser le lancement"
+
+#: fileItem.js:487
+msgid "Allow Launching"
+msgstr "Autoriser le lancement"
+
+#: fileItem.js:550
+msgid "Open"
+msgstr "Ouvrir"
+
+#: fileItem.js:553
+msgid "Cut"
+msgstr "Couper"
+
+#: fileItem.js:554
+msgid "Copy"
+msgstr "Copier"
+
+#: fileItem.js:556
+msgid "Rename"
+msgstr "Renommer"
+
+#: fileItem.js:557
+msgid "Move to Trash"
+msgstr "Mettre à la corbeille"
+
+#: fileItem.js:567
+msgid "Empty Trash"
+msgstr "Vider la corbeille"
+
+#: fileItem.js:573
+msgid "Properties"
+msgstr "Propriétés"
+
+#: fileItem.js:575
+msgid "Show in Files"
+msgstr "Montrer dans Fichiers"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Icon size"
+msgstr "Taille d’icônes"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:13
+msgid "Set the size for the desktop icons."
+msgstr "Définir la taille des icônes du bureau."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show personal folder"
+msgstr "Montrer le dossier personnel"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:18
+msgid "Show the personal folder in the desktop."
+msgstr "Montrer le dossier personnel sur le bureau."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show trash icon"
+msgstr "Montrer l’icône de la corbeille"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:23
+msgid "Show the trash icon in the desktop."
+msgstr "Montrer la corbeille sur le bureau."
+
+#~ msgid "Ok"
+#~ msgstr "Valider"
diff --git a/extensions/desktop-icons/po/fur.po b/extensions/desktop-icons/po/fur.po
new file mode 100644
index 0000000..3ab6129
--- /dev/null
+++ b/extensions/desktop-icons/po/fur.po
@@ -0,0 +1,187 @@
+# Friulian translation for desktop-icons.
+# Copyright (C) 2019 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Fabio Tomat <f.t.public@gmail.com>, 2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2019-03-01 12:11+0000\n"
+"PO-Revision-Date: 2019-03-05 22:20+0100\n"
+"Last-Translator: Fabio Tomat <f.t.public@gmail.com>\n"
+"Language-Team: Friulian <fur@li.org>\n"
+"Language: fur\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.2.1\n"
+
+#: createFolderDialog.js:48
+msgid "New folder name"
+msgstr "Gnûf non de cartele"
+
+#: createFolderDialog.js:72
+msgid "Create"
+msgstr "Cree"
+
+#: createFolderDialog.js:74 desktopGrid.js:586
+msgid "Cancel"
+msgstr "Anule"
+
+#: createFolderDialog.js:145
+msgid "Folder names cannot contain “/”."
+msgstr "I nons des cartelis no puedin contignî il caratar “/”"
+
+#: createFolderDialog.js:148
+msgid "A folder cannot be called “.”."
+msgstr "Une cartele no pues jessi clamade “.”."
+
+#: createFolderDialog.js:151
+msgid "A folder cannot be called “..”."
+msgstr "Une cartele no pues jessi clamade “..”."
+
+#: createFolderDialog.js:153
+msgid "Folders with “.” at the beginning of their name are hidden."
+msgstr "Lis cartelis cul “.” al inizi dal lôr non a son platadis."
+
+#: createFolderDialog.js:155
+msgid "There is already a file or folder with that name."
+msgstr "Un file o une cartele cul stes non e esist za."
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Dimension pes iconis dal scritori"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Piçule"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Standard"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Largje"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Mostre la cartele personâl intal scritori"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Mostre la icone de scovacere intal scritori"
+
+#: desktopGrid.js:320
+msgid "New Folder"
+msgstr "Gnove cartele"
+
+#: desktopGrid.js:322
+msgid "Paste"
+msgstr "Tache"
+
+#: desktopGrid.js:323
+msgid "Undo"
+msgstr "Anule"
+
+#: desktopGrid.js:324
+msgid "Redo"
+msgstr "Torne fâ"
+
+#: desktopGrid.js:326
+msgid "Show Desktop in Files"
+msgstr "Mostre Scritori in File"
+
+#: desktopGrid.js:327 fileItem.js:606
+msgid "Open in Terminal"
+msgstr "Vierç in Terminâl"
+
+#: desktopGrid.js:329
+msgid "Change Background…"
+msgstr "Cambie sfont…"
+
+#: desktopGrid.js:331
+msgid "Display Settings"
+msgstr "Impostazions visôr"
+
+#: desktopGrid.js:332
+msgid "Settings"
+msgstr "Impostazions"
+
+#: desktopGrid.js:576
+msgid "Enter file name…"
+msgstr "Inserìs il non dal file…"
+
+#: desktopGrid.js:580
+msgid "OK"
+msgstr "Va ben"
+
+#: fileItem.js:490
+msgid "Don’t Allow Launching"
+msgstr "No sta permeti inviament"
+
+#: fileItem.js:492
+msgid "Allow Launching"
+msgstr "Permet inviament"
+
+#: fileItem.js:574
+msgid "Open"
+msgstr "Vierç"
+
+#: fileItem.js:578
+msgid "Open With Other Application"
+msgstr "Vierç cuntune altre aplicazion"
+
+#: fileItem.js:582
+msgid "Cut"
+msgstr "Taie"
+
+#: fileItem.js:583
+msgid "Copy"
+msgstr "Copie"
+
+#: fileItem.js:585
+msgid "Rename…"
+msgstr "Cambie non..."
+
+#: fileItem.js:586
+msgid "Move to Trash"
+msgstr "Sposte te scovacere"
+
+#: fileItem.js:596
+msgid "Empty Trash"
+msgstr "Disvuede scovacere"
+
+#: fileItem.js:602
+msgid "Properties"
+msgstr "Propietâts"
+
+#: fileItem.js:604
+msgid "Show in Files"
+msgstr "Mostre in File"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:11
+msgid "Icon size"
+msgstr "Dimension icone"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Set the size for the desktop icons."
+msgstr "Stabilìs la dimension pes iconis dal scritori."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:16
+msgid "Show personal folder"
+msgstr "Mostre cartele personâl"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show the personal folder in the desktop."
+msgstr "Mostre la cartele personâl intal scritori."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:21
+msgid "Show trash icon"
+msgstr "Mostre la icone de scovacere"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show the trash icon in the desktop."
+msgstr "Mostre la icone de scovacere intal scritori."
diff --git a/extensions/desktop-icons/po/hr.po b/extensions/desktop-icons/po/hr.po
new file mode 100644
index 0000000..0d26696
--- /dev/null
+++ b/extensions/desktop-icons/po/hr.po
@@ -0,0 +1,186 @@
+# Croatian translation for gnome-shell-extension-desktop-icons
+# Copyright (c) 2019 Rosetta Contributors and Canonical Ltd 2019
+# This file is distributed under the same license as the gnome-shell-extension-desktop-icons package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell-extension-desktop-icons\n"
+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
+"POT-Creation-Date: 2019-03-05 11:27+0000\n"
+"PO-Revision-Date: 2019-03-23 16:03+0000\n"
+"Last-Translator: gogo <trebelnik2@gmail.com>\n"
+"Language-Team: Croatian <hr@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Launchpad-Export-Date: 2019-03-27 09:36+0000\n"
+"X-Generator: Launchpad (build 18910)\n"
+
+#: createFolderDialog.js:48
+msgid "New folder name"
+msgstr "Novi naziv mape"
+
+#: createFolderDialog.js:72
+msgid "Create"
+msgstr "Stvori"
+
+#: createFolderDialog.js:74 desktopGrid.js:586
+msgid "Cancel"
+msgstr "Odustani"
+
+#: createFolderDialog.js:145
+msgid "Folder names cannot contain “/”."
+msgstr "Naziv mape ne može sadržavati “/”."
+
+#: createFolderDialog.js:148
+msgid "A folder cannot be called “.”."
+msgstr "Mapa se ne može nazvati “.”."
+
+#: createFolderDialog.js:151
+msgid "A folder cannot be called “..”."
+msgstr "Mapa se ne može nazvati “..”."
+
+#: createFolderDialog.js:153
+msgid "Folders with “.” at the beginning of their name are hidden."
+msgstr "Mape sa “.” na početku njihovih naziva su skrivene."
+
+#: createFolderDialog.js:155
+msgid "There is already a file or folder with that name."
+msgstr "Već postoji datoteka ili mapa s tim nazivom."
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Veličina ikona radne površine"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Male"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Standardne"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Velike"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Prikaži osobnu mapu na radnoj površini"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Prikaži mapu smeća na radnoj površini"
+
+#: desktopGrid.js:320
+msgid "New Folder"
+msgstr "Nova mapa"
+
+#: desktopGrid.js:322
+msgid "Paste"
+msgstr "Zalijepi"
+
+#: desktopGrid.js:323
+msgid "Undo"
+msgstr "Poništi"
+
+#: desktopGrid.js:324
+msgid "Redo"
+msgstr "Ponovi"
+
+#: desktopGrid.js:326
+msgid "Show Desktop in Files"
+msgstr "Prikaži radnu površinu u Datotekama"
+
+#: desktopGrid.js:327 fileItem.js:606
+msgid "Open in Terminal"
+msgstr "Otvori u Terminalu"
+
+#: desktopGrid.js:329
+msgid "Change Background…"
+msgstr "Promijeni pozadinu…"
+
+#: desktopGrid.js:331
+msgid "Display Settings"
+msgstr "Postavke zaslona"
+
+#: desktopGrid.js:332
+msgid "Settings"
+msgstr "Postavke"
+
+#: desktopGrid.js:576
+msgid "Enter file name…"
+msgstr "Upiši naziv datoteke…"
+
+#: desktopGrid.js:580
+msgid "OK"
+msgstr "U redu"
+
+#: fileItem.js:490
+msgid "Don’t Allow Launching"
+msgstr "Ne dopuštaj pokretanje"
+
+#: fileItem.js:492
+msgid "Allow Launching"
+msgstr "Dopusti pokretanje"
+
+#: fileItem.js:574
+msgid "Open"
+msgstr "Otvori"
+
+#: fileItem.js:578
+msgid "Open With Other Application"
+msgstr "Otvori s drugom aplikacijom"
+
+#: fileItem.js:582
+msgid "Cut"
+msgstr "Izreži"
+
+#: fileItem.js:583
+msgid "Copy"
+msgstr "Kopiraj"
+
+#: fileItem.js:585
+msgid "Rename…"
+msgstr "Preimenuj…"
+
+#: fileItem.js:586
+msgid "Move to Trash"
+msgstr "Premjesti u smeće"
+
+#: fileItem.js:596
+msgid "Empty Trash"
+msgstr "Isprazni smeće"
+
+#: fileItem.js:602
+msgid "Properties"
+msgstr "Svojstva"
+
+#: fileItem.js:604
+msgid "Show in Files"
+msgstr "Prikaži u Datotekama"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:11
+msgid "Icon size"
+msgstr "Veličina ikona"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Set the size for the desktop icons."
+msgstr "Postavi veličinu ikona radne površine."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:16
+msgid "Show personal folder"
+msgstr "Prikaži osobnu mapu"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show the personal folder in the desktop."
+msgstr "Prikaži osobnu mapu na radnoj površini."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:21
+msgid "Show trash icon"
+msgstr "Prikaži ikonu smeća"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show the trash icon in the desktop."
+msgstr "Prikaži ikonu smeća na radnoj površini."
diff --git a/extensions/desktop-icons/po/hu.po b/extensions/desktop-icons/po/hu.po
new file mode 100644
index 0000000..a350dd1
--- /dev/null
+++ b/extensions/desktop-icons/po/hu.po
@@ -0,0 +1,190 @@
+# Hungarian translation for desktop-icons.
+# Copyright (C) 2019 The Free Software Foundation, inc.
+# This file is distributed under the same license as the desktop-icons package.
+#
+# Balázs Úr <ur.balazs at fsf dot hu>, 2019.
+msgid ""
+msgstr ""
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-i"
+"cons/issues\n"
+"POT-Creation-Date: 2019-03-01 12:11+0000\n"
+"PO-Revision-Date: 2019-03-07 23:45+0100\n"
+"Last-Translator: Balázs Úr <ur.balazs at fsf dot hu>\n"
+"Language-Team: Hungarian <gnome-hu-list at gnome dot org>\n"
+"Language: hu\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Lokalize 2.0\n"
+
+#: createFolderDialog.js:48
+#| msgid "New Folder"
+msgid "New folder name"
+msgstr "Új mappa neve"
+
+#: createFolderDialog.js:72
+msgid "Create"
+msgstr "Létrehozás"
+
+#: createFolderDialog.js:74 desktopGrid.js:586
+msgid "Cancel"
+msgstr "Mégse"
+
+#: createFolderDialog.js:145
+msgid "Folder names cannot contain “/”."
+msgstr "A mappanevek nem tartalmazhatnak „/” karaktert."
+
+#: createFolderDialog.js:148
+msgid "A folder cannot be called “.”."
+msgstr "Egy mappának nem lehet „.” a neve."
+
+#: createFolderDialog.js:151
+msgid "A folder cannot be called “..”."
+msgstr "Egy mappának nem lehet „..” a neve."
+
+#: createFolderDialog.js:153
+msgid "Folders with “.” at the beginning of their name are hidden."
+msgstr "A „.” karakterrel kezdődő nevű mappák rejtettek."
+
+#: createFolderDialog.js:155
+msgid "There is already a file or folder with that name."
+msgstr "Már van egy fájl vagy mappa azzal a névvel."
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Az asztali ikonok mérete"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Kicsi"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Szabványos"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Nagy"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "A személyes mappa megjelenítése az asztalon"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "A kuka ikon megjelenítése az asztalon"
+
+#: desktopGrid.js:320
+msgid "New Folder"
+msgstr "Új mappa"
+
+#: desktopGrid.js:322
+msgid "Paste"
+msgstr "Beillesztés"
+
+#: desktopGrid.js:323
+msgid "Undo"
+msgstr "Visszavonás"
+
+#: desktopGrid.js:324
+msgid "Redo"
+msgstr "Újra"
+
+#: desktopGrid.js:326
+msgid "Show Desktop in Files"
+msgstr "Asztal megjelenítése a Fájlokban"
+
+#: desktopGrid.js:327 fileItem.js:606
+msgid "Open in Terminal"
+msgstr "Megnyitás terminálban"
+
+#: desktopGrid.js:329
+msgid "Change Background…"
+msgstr "Háttér megváltoztatása…"
+
+#: desktopGrid.js:331
+msgid "Display Settings"
+msgstr "Megjelenítés beállításai"
+
+#: desktopGrid.js:332
+msgid "Settings"
+msgstr "Beállítások"
+
+#: desktopGrid.js:576
+msgid "Enter file name…"
+msgstr "Adjon meg egy fájlnevet…"
+
+#: desktopGrid.js:580
+msgid "OK"
+msgstr "Rendben"
+
+#: fileItem.js:490
+msgid "Don’t Allow Launching"
+msgstr "Ne engedélyezzen indítást"
+
+#: fileItem.js:492
+msgid "Allow Launching"
+msgstr "Indítás engedélyezése"
+
+#: fileItem.js:574
+msgid "Open"
+msgstr "Megnyitás"
+
+#: fileItem.js:578
+msgid "Open With Other Application"
+msgstr "Megnyitás egyéb alkalmazással"
+
+#: fileItem.js:582
+msgid "Cut"
+msgstr "Kivágás"
+
+#: fileItem.js:583
+msgid "Copy"
+msgstr "Másolás"
+
+#: fileItem.js:585
+msgid "Rename…"
+msgstr "Átnevezés…"
+
+#: fileItem.js:586
+msgid "Move to Trash"
+msgstr "Áthelyezés a Kukába"
+
+#: fileItem.js:596
+msgid "Empty Trash"
+msgstr "Kuka ürítése"
+
+#: fileItem.js:602
+msgid "Properties"
+msgstr "Tulajdonságok"
+
+#: fileItem.js:604
+msgid "Show in Files"
+msgstr "Megjelenítés a Fájlokban"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:11
+msgid "Icon size"
+msgstr "Ikonméret"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Set the size for the desktop icons."
+msgstr "Az asztali ikonok méretének beállítása."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:16
+msgid "Show personal folder"
+msgstr "Személyes mappa megjelenítése"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show the personal folder in the desktop."
+msgstr "A személyes mappa megjelenítése az asztalon."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:21
+msgid "Show trash icon"
+msgstr "Kuka ikon megjelenítése"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show the trash icon in the desktop."
+msgstr "A kuka ikon megjelenítése az asztalon."
+
diff --git a/extensions/desktop-icons/po/id.po b/extensions/desktop-icons/po/id.po
new file mode 100644
index 0000000..b809c3d
--- /dev/null
+++ b/extensions/desktop-icons/po/id.po
@@ -0,0 +1,190 @@
+# Indonesian translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Kukuh Syafaat <kukuhsyafaat@gnome.org>, 2018, 2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2019-03-01 12:11+0000\n"
+"PO-Revision-Date: 2019-03-02 19:13+0700\n"
+"Last-Translator: Kukuh Syafaat <kukuhsyafaat@gnome.org>\n"
+"Language-Team: Indonesian <gnome-l10n-id@googlegroups.com>\n"
+"Language: id\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.2.1\n"
+
+#: createFolderDialog.js:48
+msgid "New folder name"
+msgstr "Nama folder baru"
+
+#: createFolderDialog.js:72
+msgid "Create"
+msgstr "Buat"
+
+#: createFolderDialog.js:74 desktopGrid.js:586
+msgid "Cancel"
+msgstr "Batal"
+
+#: createFolderDialog.js:145
+msgid "Folder names cannot contain “/”."
+msgstr "Nama folder tak boleh memuat \"/\"."
+
+#: createFolderDialog.js:148
+msgid "A folder cannot be called “.”."
+msgstr "Sebuah folder tak bisa dinamai \".\"."
+
+#: createFolderDialog.js:151
+msgid "A folder cannot be called “..”."
+msgstr "Sebuah folder tak bisa dinamai \"..\"."
+
+#: createFolderDialog.js:153
+msgid "Folders with “.” at the beginning of their name are hidden."
+msgstr "Folder dengan \".\" di awal nama mereka disembunyikan."
+
+#: createFolderDialog.js:155
+msgid "There is already a file or folder with that name."
+msgstr "Folder dengan nama itu sudah ada."
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Ukuran untuk ikon destop"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Kecil"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Standar"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Besar"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Tampilkan folder pribadi di destop"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Tampilkan ikon tong sampah di destop"
+
+#: desktopGrid.js:320
+msgid "New Folder"
+msgstr "Folder Baru"
+
+#: desktopGrid.js:322
+msgid "Paste"
+msgstr "Tempel"
+
+#: desktopGrid.js:323
+msgid "Undo"
+msgstr "Tak Jadi"
+
+#: desktopGrid.js:324
+msgid "Redo"
+msgstr "Jadi Lagi"
+
+#: desktopGrid.js:326
+msgid "Show Desktop in Files"
+msgstr "Tampilkan Destop pada Berkas"
+
+#: desktopGrid.js:327 fileItem.js:606
+msgid "Open in Terminal"
+msgstr "Buka dalam Terminal"
+
+#: desktopGrid.js:329
+msgid "Change Background…"
+msgstr "Ubah Latar Belakang…"
+
+#: desktopGrid.js:331
+msgid "Display Settings"
+msgstr "Pengaturan Tampilan"
+
+#: desktopGrid.js:332
+msgid "Settings"
+msgstr "Pengaturan"
+
+#: desktopGrid.js:576
+msgid "Enter file name…"
+msgstr "Masukkan nama berkas…"
+
+#: desktopGrid.js:580
+msgid "OK"
+msgstr "OK"
+
+#: fileItem.js:490
+msgid "Don’t Allow Launching"
+msgstr "Jangan Izinkan Peluncuran"
+
+#: fileItem.js:492
+msgid "Allow Launching"
+msgstr "Izinkan Peluncuran"
+
+#: fileItem.js:574
+msgid "Open"
+msgstr "Buka"
+
+#: fileItem.js:578
+msgid "Open With Other Application"
+msgstr "Buka Dengan Aplikasi Lain"
+
+#: fileItem.js:582
+msgid "Cut"
+msgstr "Potong"
+
+#: fileItem.js:583
+msgid "Copy"
+msgstr "Salin"
+
+#: fileItem.js:585
+msgid "Rename…"
+msgstr "Ganti Nama…"
+
+#: fileItem.js:586
+msgid "Move to Trash"
+msgstr "Pindahkan ke Tong Sampah"
+
+#: fileItem.js:596
+msgid "Empty Trash"
+msgstr "Kosongkan Tong Sampah"
+
+#: fileItem.js:602
+msgid "Properties"
+msgstr "Properti"
+
+#: fileItem.js:604
+msgid "Show in Files"
+msgstr "Tampilkan pada Berkas"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:11
+msgid "Icon size"
+msgstr "Ukuran ikon"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Set the size for the desktop icons."
+msgstr "Set ukuran untuk ikon destop."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:16
+msgid "Show personal folder"
+msgstr "Tampilkan folder pribadi"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show the personal folder in the desktop."
+msgstr "Tampilkan folder pribadi di destop."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:21
+msgid "Show trash icon"
+msgstr "Tampilkan ikon tong sampah"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show the trash icon in the desktop."
+msgstr "Tampilkan ikon tong sampah di destop."
+
+#~ msgid "Huge"
+#~ msgstr "Sangat besar"
diff --git a/extensions/desktop-icons/po/it.po b/extensions/desktop-icons/po/it.po
new file mode 100644
index 0000000..5001da4
--- /dev/null
+++ b/extensions/desktop-icons/po/it.po
@@ -0,0 +1,189 @@
+# Italian translation for desktop-icons.
+# Copyright (C) 2019 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Massimo Branchini <max.bra.gtalk@gmail.com>, 2019.
+# Milo Casagrande <milo@milo.name>, 2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2019-03-01 12:11+0000\n"
+"PO-Revision-Date: 2019-03-12 09:51+0100\n"
+"Last-Translator: Milo Casagrande <milo@milo.name>\n"
+"Language-Team: Italian <tp@lists.linux.it>\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.2.1\n"
+
+#: createFolderDialog.js:48
+msgid "New folder name"
+msgstr "Nuova cartella"
+
+#: createFolderDialog.js:72
+msgid "Create"
+msgstr "Crea"
+
+#: createFolderDialog.js:74 desktopGrid.js:586
+msgid "Cancel"
+msgstr "Annulla"
+
+#: createFolderDialog.js:145
+msgid "Folder names cannot contain “/”."
+msgstr "I nomi di cartelle non possono contenere il carattere «/»"
+
+#: createFolderDialog.js:148
+msgid "A folder cannot be called “.”."
+msgstr "Una cartella non può essere chiamata «.»."
+
+#: createFolderDialog.js:151
+msgid "A folder cannot be called “..”."
+msgstr "Una cartella non può essere chiamata «..»."
+
+#: createFolderDialog.js:153
+msgid "Folders with “.” at the beginning of their name are hidden."
+msgstr "Cartelle il cui nome inizia con «.» sono nascoste."
+
+#: createFolderDialog.js:155
+msgid "There is already a file or folder with that name."
+msgstr "Esiste già un file o una cartella con quel nome."
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Dimensione delle icone della scrivania"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Piccola"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Normale"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Grande"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Mostra la cartella personale sulla scrivania"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Mostra il cestino sulla scrivania"
+
+#: desktopGrid.js:320
+msgid "New Folder"
+msgstr "Nuova cartella"
+
+#: desktopGrid.js:322
+msgid "Paste"
+msgstr "Incolla"
+
+#: desktopGrid.js:323
+msgid "Undo"
+msgstr "Annulla"
+
+#: desktopGrid.js:324
+msgid "Redo"
+msgstr "Ripeti"
+
+#: desktopGrid.js:326
+msgid "Show Desktop in Files"
+msgstr "Mostra la scrivania in File"
+
+#: desktopGrid.js:327 fileItem.js:606
+msgid "Open in Terminal"
+msgstr "Apri in Terminale"
+
+#: desktopGrid.js:329
+msgid "Change Background…"
+msgstr "Cambia lo sfondo…"
+
+#: desktopGrid.js:331
+msgid "Display Settings"
+msgstr "Impostazioni dello schermo"
+
+#: desktopGrid.js:332
+msgid "Settings"
+msgstr "Impostazioni"
+
+#: desktopGrid.js:576
+msgid "Enter file name…"
+msgstr "Indicare un nome per il file…"
+
+#: desktopGrid.js:580
+msgid "OK"
+msgstr "Ok"
+
+#: fileItem.js:490
+msgid "Don’t Allow Launching"
+msgstr "Non permettere l'esecuzione"
+
+#: fileItem.js:492
+msgid "Allow Launching"
+msgstr "Permetti l'esecuzione"
+
+#: fileItem.js:574
+msgid "Open"
+msgstr "Apri"
+
+#: fileItem.js:578
+msgid "Open With Other Application"
+msgstr "Apri con altra applicazione"
+
+#: fileItem.js:582
+msgid "Cut"
+msgstr "Taglia"
+
+#: fileItem.js:583
+msgid "Copy"
+msgstr "Copia"
+
+#: fileItem.js:585
+msgid "Rename…"
+msgstr "Rinomina…"
+
+#: fileItem.js:586
+msgid "Move to Trash"
+msgstr "Sposta nel cestino"
+
+#: fileItem.js:596
+msgid "Empty Trash"
+msgstr "Svuota il cestino"
+
+#: fileItem.js:602
+msgid "Properties"
+msgstr "Proprietà"
+
+#: fileItem.js:604
+msgid "Show in Files"
+msgstr "Mostra in File"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:11
+msgid "Icon size"
+msgstr "Dimensione dell'icona"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Set the size for the desktop icons."
+msgstr "Imposta la grandezza delle icone della scrivania."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:16
+msgid "Show personal folder"
+msgstr "Mostra la cartella personale"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show the personal folder in the desktop."
+msgstr "Mostra la cartella personale sulla scrivania."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:21
+msgid "Show trash icon"
+msgstr "Mostra il cestino"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show the trash icon in the desktop."
+msgstr "Mostra il cestino sulla scrivania."
diff --git a/extensions/desktop-icons/po/ja.po b/extensions/desktop-icons/po/ja.po
new file mode 100644
index 0000000..3b103e0
--- /dev/null
+++ b/extensions/desktop-icons/po/ja.po
@@ -0,0 +1,187 @@
+# Japanese translation for desktop-icons.
+# Copyright (C) 2019 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# sicklylife <translation@sicklylife.jp>, 2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2019-03-14 12:56+0000\n"
+"PO-Revision-Date: 2019-03-15 06:30+0000\n"
+"Last-Translator: sicklylife <translation@sicklylife.jp>\n"
+"Language-Team: Japanese <gnome-translation@gnome.gr.jp>\n"
+"Language: ja\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: createFolderDialog.js:48
+msgid "New folder name"
+msgstr "新しいフォルダー名"
+
+#: createFolderDialog.js:72
+msgid "Create"
+msgstr "作成"
+
+#: createFolderDialog.js:74 desktopGrid.js:586
+msgid "Cancel"
+msgstr "キャンセル"
+
+#: createFolderDialog.js:145
+msgid "Folder names cannot contain “/”."
+msgstr "“/”は、フォルダー名に含められません。"
+
+#: createFolderDialog.js:148
+msgid "A folder cannot be called “.”."
+msgstr "“.”という名前をフォルダーに付けられません。"
+
+#: createFolderDialog.js:151
+msgid "A folder cannot be called “..”."
+msgstr "“..”という名前をフォルダーに付けられません。"
+
+#: createFolderDialog.js:153
+msgid "Folders with “.” at the beginning of their name are hidden."
+msgstr "名前が“.”で始まるフォルダーは、隠しフォルダーになります。"
+
+#: createFolderDialog.js:155
+msgid "There is already a file or folder with that name."
+msgstr "その名前のファイルかフォルダーがすでに存在します。"
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "デスクトップアイコンのサイズ"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "小さい"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "標準"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "大きい"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "デスクトップにホームフォルダーを表示する"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "デスクトップにゴミ箱を表示する"
+
+#: desktopGrid.js:320
+msgid "New Folder"
+msgstr "新しいフォルダー"
+
+#: desktopGrid.js:322
+msgid "Paste"
+msgstr "貼り付け"
+
+#: desktopGrid.js:323
+msgid "Undo"
+msgstr "元に戻す"
+
+#: desktopGrid.js:324
+msgid "Redo"
+msgstr "やり直す"
+
+#: desktopGrid.js:326
+msgid "Show Desktop in Files"
+msgstr "“ファイル”でデスクトップを表示する"
+
+#: desktopGrid.js:327 fileItem.js:606
+msgid "Open in Terminal"
+msgstr "端末を開く"
+
+#: desktopGrid.js:329
+msgid "Change Background…"
+msgstr "背景を変更する…"
+
+#: desktopGrid.js:331
+msgid "Display Settings"
+msgstr "ディスプレイの設定"
+
+#: desktopGrid.js:332
+msgid "Settings"
+msgstr "設定"
+
+#: desktopGrid.js:576
+msgid "Enter file name…"
+msgstr "ファイル名を入力してください…"
+
+#: desktopGrid.js:580
+msgid "OK"
+msgstr "OK"
+
+#: fileItem.js:490
+msgid "Don’t Allow Launching"
+msgstr "起動を許可しない"
+
+#: fileItem.js:492
+msgid "Allow Launching"
+msgstr "起動を許可する"
+
+#: fileItem.js:574
+msgid "Open"
+msgstr "開く"
+
+#: fileItem.js:578
+msgid "Open With Other Application"
+msgstr "別のアプリケーションで開く"
+
+#: fileItem.js:582
+msgid "Cut"
+msgstr "切り取り"
+
+#: fileItem.js:583
+msgid "Copy"
+msgstr "コピー"
+
+#: fileItem.js:585
+msgid "Rename…"
+msgstr "名前の変更…"
+
+#: fileItem.js:586
+msgid "Move to Trash"
+msgstr "ゴミ箱へ移動する"
+
+#: fileItem.js:596
+msgid "Empty Trash"
+msgstr "ゴミ箱を空にする"
+
+#: fileItem.js:602
+msgid "Properties"
+msgstr "プロパティ"
+
+#: fileItem.js:604
+msgid "Show in Files"
+msgstr "“ファイル”で表示する"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:11
+msgid "Icon size"
+msgstr "アイコンサイズ"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Set the size for the desktop icons."
+msgstr "デスクトップのアイコンサイズを設定します。"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:16
+msgid "Show personal folder"
+msgstr "ホームフォルダーを表示する"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show the personal folder in the desktop."
+msgstr "デスクトップにホームフォルダーを表示します。"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:21
+msgid "Show trash icon"
+msgstr "ゴミ箱アイコンを表示する"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show the trash icon in the desktop."
+msgstr "デスクトップにゴミ箱のアイコンを表示します。"
diff --git a/extensions/desktop-icons/po/meson.build b/extensions/desktop-icons/po/meson.build
new file mode 100644
index 0000000..b2e9e42
--- /dev/null
+++ b/extensions/desktop-icons/po/meson.build
@@ -0,0 +1 @@
+i18n.gettext (meson.project_name (), preset: 'glib')
diff --git a/extensions/desktop-icons/po/nl.po b/extensions/desktop-icons/po/nl.po
new file mode 100644
index 0000000..b2f7dab
--- /dev/null
+++ b/extensions/desktop-icons/po/nl.po
@@ -0,0 +1,188 @@
+# Dutch translation for desktop-icons.
+# Copyright (C) 2019 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Nathan Follens <nthn@unseen.is>, 2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2019-03-01 12:11+0000\n"
+"PO-Revision-Date: 2019-03-04 19:46+0100\n"
+"Last-Translator: Nathan Follens <nthn@unseen.is>\n"
+"Language-Team: Dutch <gnome-nl-list@gnome.org>\n"
+"Language: nl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.2.1\n"
+
+#: createFolderDialog.js:48
+msgid "New folder name"
+msgstr "Nieuwe mapnaam"
+
+#: createFolderDialog.js:72
+msgid "Create"
+msgstr "Aanmaken"
+
+#: createFolderDialog.js:74 desktopGrid.js:586
+msgid "Cancel"
+msgstr "Annuleren"
+
+#: createFolderDialog.js:145
+msgid "Folder names cannot contain “/”."
+msgstr "Mapnamen kunnen geen ‘/’ bevatten."
+
+#: createFolderDialog.js:148
+msgid "A folder cannot be called “.”."
+msgstr "Een map kan niet ‘.’ worden genoemd."
+
+#: createFolderDialog.js:151
+msgid "A folder cannot be called “..”."
+msgstr "Een map kan niet ‘..’ worden genoemd."
+
+#: createFolderDialog.js:153
+msgid "Folders with “.” at the beginning of their name are hidden."
+msgstr "Mappen waarvan de naam begint met ‘.’ zijn verborgen."
+
+#: createFolderDialog.js:155
+msgid "There is already a file or folder with that name."
+msgstr "Er bestaat al een bestand of map met die naam."
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Grootte van bureaubladpictogrammen"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Klein"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Standaard"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Groot"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Toon de persoonlijke map op het bureaublad"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Toon het prullenbakpictogram op het bureaublad"
+
+#: desktopGrid.js:320
+msgid "New Folder"
+msgstr "Nieuwe map"
+
+#: desktopGrid.js:322
+msgid "Paste"
+msgstr "Plakken"
+
+#: desktopGrid.js:323
+msgid "Undo"
+msgstr "Ongedaan maken"
+
+#: desktopGrid.js:324
+msgid "Redo"
+msgstr "Opnieuw"
+
+#: desktopGrid.js:326
+msgid "Show Desktop in Files"
+msgstr "Bureaublad tonen in Bestanden"
+
+#: desktopGrid.js:327 fileItem.js:606
+msgid "Open in Terminal"
+msgstr "Openen in terminalvenster"
+
+#: desktopGrid.js:329
+msgid "Change Background…"
+msgstr "Achtergrond aanpassen…"
+
+#: desktopGrid.js:331
+msgid "Display Settings"
+msgstr "Scherminstellingen"
+
+#: desktopGrid.js:332
+msgid "Settings"
+msgstr "Instellingen"
+
+#: desktopGrid.js:576
+msgid "Enter file name…"
+msgstr "Voer bestandsnaam in…"
+
+#: desktopGrid.js:580
+msgid "OK"
+msgstr "Oké"
+
+#: fileItem.js:490
+msgid "Don’t Allow Launching"
+msgstr "Toepassingen starten niet toestaan"
+
+#: fileItem.js:492
+msgid "Allow Launching"
+msgstr "Toepassingen starten toestaan"
+
+#: fileItem.js:574
+msgid "Open"
+msgstr "Openen"
+
+#: fileItem.js:578
+msgid "Open With Other Application"
+msgstr "Met andere toepassing openen"
+
+#: fileItem.js:582
+msgid "Cut"
+msgstr "Knippen"
+
+#: fileItem.js:583
+msgid "Copy"
+msgstr "Kopiëren"
+
+#: fileItem.js:585
+msgid "Rename…"
+msgstr "Hernoemen…"
+
+#: fileItem.js:586
+msgid "Move to Trash"
+msgstr "Verplaatsen naar prullenbak"
+
+#: fileItem.js:596
+msgid "Empty Trash"
+msgstr "Prullenbak legen"
+
+#: fileItem.js:602
+msgid "Properties"
+msgstr "Eigenschappen"
+
+#: fileItem.js:604
+msgid "Show in Files"
+msgstr "Tonen in Bestanden"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:11
+msgid "Icon size"
+msgstr "Pictogramgrootte"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Set the size for the desktop icons."
+msgstr "Stel de grootte van de bureaubladpictogrammen in."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:16
+msgid "Show personal folder"
+msgstr "Persoonlijke map tonen"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show the personal folder in the desktop."
+msgstr "Toon de persoonlijke map op het bureaublad."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:21
+msgid "Show trash icon"
+msgstr "Prullenbakpictogram tonen"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show the trash icon in the desktop."
+msgstr "Toon het prullenbakpictogram op het bureaublad."
diff --git a/extensions/desktop-icons/po/pl.po b/extensions/desktop-icons/po/pl.po
new file mode 100644
index 0000000..f57b3be
--- /dev/null
+++ b/extensions/desktop-icons/po/pl.po
@@ -0,0 +1,193 @@
+# Polish translation for desktop-icons.
+# Copyright © 2018-2019 the desktop-icons authors.
+# This file is distributed under the same license as the desktop-icons package.
+# Piotr Drąg <piotrdrag@gmail.com>, 2018-2019.
+# Aviary.pl <community-poland@mozilla.org>, 2018-2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: desktop-icons\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2019-04-29 14:11+0000\n"
+"PO-Revision-Date: 2019-05-01 13:03+0200\n"
+"Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n"
+"Language-Team: Polish <community-poland@mozilla.org>\n"
+"Language: pl\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==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
+"|| n%100>=20) ? 1 : 2);\n"
+
+#: createFolderDialog.js:48
+msgid "New folder name"
+msgstr "Nazwa nowego katalogu"
+
+#: createFolderDialog.js:72
+msgid "Create"
+msgstr "Utwórz"
+
+#: createFolderDialog.js:74 desktopGrid.js:592
+msgid "Cancel"
+msgstr "Anuluj"
+
+#: createFolderDialog.js:145
+msgid "Folder names cannot contain “/”."
+msgstr "Nazwy katalogów nie mogą zawierać znaku „/”."
+
+#: createFolderDialog.js:148
+msgid "A folder cannot be called “.”."
+msgstr "Katalog nie może mieć nazwy „.”."
+
+#: createFolderDialog.js:151
+msgid "A folder cannot be called “..”."
+msgstr "Katalog nie może mieć nazwy „..”."
+
+#: createFolderDialog.js:153
+msgid "Folders with “.” at the beginning of their name are hidden."
+msgstr "Katalogi z „.” na początku nazwy są ukryte."
+
+#: createFolderDialog.js:155
+msgid "There is already a file or folder with that name."
+msgstr "Plik lub katalog o tej nazwie już istnieje."
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Rozmiar ikon na pulpicie"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Mały"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Standardowy"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Duży"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Katalog domowy na pulpicie"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Kosz na pulpicie"
+
+#: desktopGrid.js:323
+msgid "New Folder"
+msgstr "Nowy katalog"
+
+#: desktopGrid.js:325
+msgid "Paste"
+msgstr "Wklej"
+
+#: desktopGrid.js:326
+msgid "Undo"
+msgstr "Cofnij"
+
+#: desktopGrid.js:327
+msgid "Redo"
+msgstr "Ponów"
+
+#: desktopGrid.js:329
+msgid "Show Desktop in Files"
+msgstr "Wyświetl pulpit w menedżerze plików"
+
+#: desktopGrid.js:330 fileItem.js:610
+msgid "Open in Terminal"
+msgstr "Otwórz w terminalu"
+
+#: desktopGrid.js:332
+msgid "Change Background…"
+msgstr "Zmień tło…"
+
+#: desktopGrid.js:334
+msgid "Display Settings"
+msgstr "Ustawienia ekranu"
+
+#: desktopGrid.js:335
+msgid "Settings"
+msgstr "Ustawienia"
+
+#: desktopGrid.js:582
+msgid "Enter file name…"
+msgstr "Nazwa pliku…"
+
+#: desktopGrid.js:586
+msgid "OK"
+msgstr "OK"
+
+#: desktopIconsUtil.js:61
+msgid "Command not found"
+msgstr "Nie odnaleziono polecenia"
+
+#: fileItem.js:494
+msgid "Don’t Allow Launching"
+msgstr "Nie zezwalaj na uruchamianie"
+
+#: fileItem.js:496
+msgid "Allow Launching"
+msgstr "Zezwól na uruchamianie"
+
+#: fileItem.js:578
+msgid "Open"
+msgstr "Otwórz"
+
+#: fileItem.js:582
+msgid "Open With Other Application"
+msgstr "Otwórz za pomocą innego programu"
+
+#: fileItem.js:586
+msgid "Cut"
+msgstr "Wytnij"
+
+#: fileItem.js:587
+msgid "Copy"
+msgstr "Skopiuj"
+
+#: fileItem.js:589
+msgid "Rename…"
+msgstr "Zmień nazwę…"
+
+#: fileItem.js:590
+msgid "Move to Trash"
+msgstr "Przenieś do kosza"
+
+#: fileItem.js:600
+msgid "Empty Trash"
+msgstr "Opróżnij kosz"
+
+#: fileItem.js:606
+msgid "Properties"
+msgstr "Właściwości"
+
+#: fileItem.js:608
+msgid "Show in Files"
+msgstr "Wyświetl w menedżerze plików"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:11
+msgid "Icon size"
+msgstr "Rozmiar ikon"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Set the size for the desktop icons."
+msgstr "Ustawia rozmiar ikon na pulpicie."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:16
+msgid "Show personal folder"
+msgstr "Katalog domowy"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show the personal folder in the desktop."
+msgstr "Wyświetla katalog domowy na pulpicie."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:21
+msgid "Show trash icon"
+msgstr "Kosz"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show the trash icon in the desktop."
+msgstr "Wyświetla kosz na pulpicie."
diff --git a/extensions/desktop-icons/po/pt_BR.po b/extensions/desktop-icons/po/pt_BR.po
new file mode 100644
index 0000000..8d61c72
--- /dev/null
+++ b/extensions/desktop-icons/po/pt_BR.po
@@ -0,0 +1,199 @@
+# Brazilian Portuguese translation for desktop-icons.
+# Copyright (C) 2019 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Enrico Nicoletto <liverig@gmail.com>, 2018.
+# Rafael Fontenelle <rafaelff@gnome.org>, 2018-2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2019-04-29 14:11+0000\n"
+"PO-Revision-Date: 2019-04-29 17:35-0300\n"
+"Last-Translator: Rafael Fontenelle <rafaelff@gnome.org>\n"
+"Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n"
+"Language: pt_BR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+"X-Generator: Gtranslator 3.32.0\n"
+
+#: createFolderDialog.js:48
+msgid "New folder name"
+msgstr "Nome da nova pasta"
+
+#: createFolderDialog.js:72
+msgid "Create"
+msgstr "Criar"
+
+#: createFolderDialog.js:74 desktopGrid.js:592
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: createFolderDialog.js:145
+msgid "Folder names cannot contain “/”."
+msgstr "Nomes de pastas não podem conter “/”."
+
+#: createFolderDialog.js:148
+msgid "A folder cannot be called “.”."
+msgstr "Uma pasta não pode ser chamada “.”."
+
+#: createFolderDialog.js:151
+msgid "A folder cannot be called “..”."
+msgstr "Uma pasta não pode ser chamada “..”."
+
+#: createFolderDialog.js:153
+msgid "Folders with “.” at the beginning of their name are hidden."
+msgstr "Pastas com “.” no começo de seus nomes são ocultas."
+
+#: createFolderDialog.js:155
+msgid "There is already a file or folder with that name."
+msgstr "Já existe um arquivo ou uma pasta com esse nome."
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Tamanho para os ícones da área de trabalho"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Pequeno"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Padrão"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Grande"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Mostrar a pasta pessoal na área de trabalho"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Mostrar o ícone da lixeira na área de trabalho"
+
+#: desktopGrid.js:323
+msgid "New Folder"
+msgstr "Nova pasta"
+
+#: desktopGrid.js:325
+msgid "Paste"
+msgstr "Colar"
+
+#: desktopGrid.js:326
+msgid "Undo"
+msgstr "Desfazer"
+
+#: desktopGrid.js:327
+msgid "Redo"
+msgstr "Refazer"
+
+#: desktopGrid.js:329
+msgid "Show Desktop in Files"
+msgstr "Mostrar a área de trabalho no Arquivos"
+
+#: desktopGrid.js:330 fileItem.js:610
+msgid "Open in Terminal"
+msgstr "Abrir no terminal"
+
+#: desktopGrid.js:332
+msgid "Change Background…"
+msgstr "Alterar plano de fundo…"
+
+#: desktopGrid.js:334
+msgid "Display Settings"
+msgstr "Configurações de exibição"
+
+#: desktopGrid.js:335
+msgid "Settings"
+msgstr "Configurações"
+
+#: desktopGrid.js:582
+msgid "Enter file name…"
+msgstr "Insira um nome de arquivo…"
+
+#: desktopGrid.js:586
+msgid "OK"
+msgstr "OK"
+
+#: desktopIconsUtil.js:61
+msgid "Command not found"
+msgstr "Comando não encontrado"
+
+#: fileItem.js:494
+msgid "Don’t Allow Launching"
+msgstr "Não permitir iniciar"
+
+#: fileItem.js:496
+msgid "Allow Launching"
+msgstr "Permitir iniciar"
+
+#: fileItem.js:578
+msgid "Open"
+msgstr "Abrir"
+
+#: fileItem.js:582
+msgid "Open With Other Application"
+msgstr "Abrir com outro aplicativo"
+
+#: fileItem.js:586
+msgid "Cut"
+msgstr "Recortar"
+
+#: fileItem.js:587
+msgid "Copy"
+msgstr "Copiar"
+
+#: fileItem.js:589
+msgid "Rename…"
+msgstr "Renomear…"
+
+#: fileItem.js:590
+msgid "Move to Trash"
+msgstr "Mover para a lixeira"
+
+#: fileItem.js:600
+msgid "Empty Trash"
+msgstr "Esvaziar lixeira"
+
+#: fileItem.js:606
+msgid "Properties"
+msgstr "Propriedades"
+
+#: fileItem.js:608
+msgid "Show in Files"
+msgstr "Mostrar no Arquivos"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:11
+msgid "Icon size"
+msgstr "Tamanho do ícone"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Set the size for the desktop icons."
+msgstr "Define o tamanho para os ícones da área de trabalho."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:16
+msgid "Show personal folder"
+msgstr "Mostrar pasta pessoal"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show the personal folder in the desktop."
+msgstr "Mostra a pasta pessoal na área de trabalho."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:21
+msgid "Show trash icon"
+msgstr "Mostrar ícone da lixeira"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show the trash icon in the desktop."
+msgstr "Mostra o ícone da lixeira na área de trabalho."
+
+#~ msgid "Huge"
+#~ msgstr "Enorme"
+
+#~ msgid "Ok"
+#~ msgstr "Ok"
diff --git a/extensions/desktop-icons/po/ru.po b/extensions/desktop-icons/po/ru.po
new file mode 100644
index 0000000..4094f16
--- /dev/null
+++ b/extensions/desktop-icons/po/ru.po
@@ -0,0 +1,153 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Eaglers <eaglersdeveloper@gmail.com>, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-11-22 08:42+0000\n"
+"PO-Revision-Date: 2018-11-22 22:02+0300\n"
+"Last-Translator: Stas Solovey <whats_up@tut.by>\n"
+"Language-Team: Russian <gnome-cyr@gnome.org>\n"
+"Language: ru\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 2.2\n"
+
+#: prefs.js:89
+msgid "Size for the desktop icons"
+msgstr "Размер значков"
+
+#: prefs.js:89
+msgid "Small"
+msgstr "Маленький"
+
+#: prefs.js:89
+msgid "Standard"
+msgstr "Стандартный"
+
+#: prefs.js:89
+msgid "Large"
+msgstr "Большой"
+
+#: prefs.js:89
+msgid "Huge"
+msgstr "Огромный"
+
+#: prefs.js:90
+msgid "Show the personal folder in the desktop"
+msgstr "Показывать домашнюю папку на рабочем столе"
+
+#: prefs.js:91
+msgid "Show the trash icon in the desktop"
+msgstr "Показывать «Корзину» на рабочем столе"
+
+#: desktopGrid.js:185 desktopGrid.js:304
+msgid "New Folder"
+msgstr "Создать папку"
+
+#: desktopGrid.js:306
+msgid "Paste"
+msgstr "Вставить"
+
+#: desktopGrid.js:307
+msgid "Undo"
+msgstr "Отменить"
+
+#: desktopGrid.js:308
+msgid "Redo"
+msgstr "Повторить"
+
+#: desktopGrid.js:310
+msgid "Open Desktop in Files"
+msgstr "Открыть «Рабочий стол» в «Файлах»"
+
+#: desktopGrid.js:311
+msgid "Open Terminal"
+msgstr "Открыть терминал"
+
+#: desktopGrid.js:313
+msgid "Change Background…"
+msgstr "Изменить фон…"
+
+#: desktopGrid.js:314
+msgid "Display Settings"
+msgstr "Настройки дисплея"
+
+#: desktopGrid.js:315
+msgid "Settings"
+msgstr "Параметры"
+
+#: desktopGrid.js:569
+msgid "Enter file name…"
+msgstr "Ввести имя файла…"
+
+#: desktopGrid.js:573
+msgid "Ok"
+msgstr "ОК"
+
+#: desktopGrid.js:579
+msgid "Cancel"
+msgstr "Отмена"
+
+#: fileItem.js:390
+msgid "Open"
+msgstr "Открыть"
+
+#: fileItem.js:393
+msgid "Cut"
+msgstr "Вырезать"
+
+#: fileItem.js:394
+msgid "Copy"
+msgstr "Вставить"
+
+#: fileItem.js:395
+msgid "Rename"
+msgstr "Переименовать"
+
+#: fileItem.js:396
+msgid "Move to Trash"
+msgstr "Переместить в корзину"
+
+#: fileItem.js:400
+msgid "Empty trash"
+msgstr "Очистить корзину"
+
+#: fileItem.js:406
+msgid "Properties"
+msgstr "Свойства"
+
+#: fileItem.js:408
+msgid "Show in Files"
+msgstr "Показать в «Файлах»"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Icon size"
+msgstr "Размер значков"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:13
+msgid "Set the size for the desktop icons."
+msgstr "Установить размер значков на рабочем столе."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show personal folder"
+msgstr "Показывать домашнюю папку"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:18
+msgid "Show the personal folder in the desktop."
+msgstr "Показывать значок домашней папки на рабочем столе."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show trash icon"
+msgstr "Показывать значок корзины"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:23
+msgid "Show the trash icon in the desktop."
+msgstr "Показывать значок корзины на рабочем столе."
diff --git a/extensions/desktop-icons/po/sv.po b/extensions/desktop-icons/po/sv.po
new file mode 100644
index 0000000..928cbe9
--- /dev/null
+++ b/extensions/desktop-icons/po/sv.po
@@ -0,0 +1,197 @@
+# Swedish translation for desktop-icons.
+# Copyright © 2018, 2019 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Anders Jonsson <anders.jonsson@norsjovallen.se>, 2018, 2019.
+# Josef Andersson <l10nl18nsweja@gmail.com>, 2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2019-03-01 12:11+0000\n"
+"PO-Revision-Date: 2019-03-11 21:50+0100\n"
+"Last-Translator: Anders Jonsson <anders.jonsson@norsjovallen.se>\n"
+"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
+"Language: sv\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.2.1\n"
+
+#: createFolderDialog.js:48
+msgid "New folder name"
+msgstr "Nytt mappnamn"
+
+#: createFolderDialog.js:72
+msgid "Create"
+msgstr "Skapa"
+
+#: createFolderDialog.js:74 desktopGrid.js:586
+msgid "Cancel"
+msgstr "Avbryt"
+
+#: createFolderDialog.js:145
+msgid "Folder names cannot contain “/”."
+msgstr "Mappnamn kan inte innehålla ”/”."
+
+#: createFolderDialog.js:148
+msgid "A folder cannot be called “.”."
+msgstr "En mapp kan inte kallas ”.”."
+
+#: createFolderDialog.js:151
+msgid "A folder cannot be called “..”."
+msgstr "En mapp kan inte kallas ”..”."
+
+#: createFolderDialog.js:153
+msgid "Folders with “.” at the beginning of their name are hidden."
+msgstr "Mappar med ”.” i början på sitt namn är dolda."
+
+#: createFolderDialog.js:155
+msgid "There is already a file or folder with that name."
+msgstr "Det finns redan en fil eller mapp med det namnet"
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Storlek för skrivbordsikonerna"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Liten"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Standard"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Stor"
+
+# TODO: *ON* the desktop?
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Visa den personliga mappen på skrivbordet"
+
+# TODO: *ON* the desktop?
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Visa papperskorgsikonen på skrivbordet"
+
+#: desktopGrid.js:320
+msgid "New Folder"
+msgstr "Ny mapp"
+
+#: desktopGrid.js:322
+msgid "Paste"
+msgstr "Klistra in"
+
+#: desktopGrid.js:323
+msgid "Undo"
+msgstr "Ångra"
+
+#: desktopGrid.js:324
+msgid "Redo"
+msgstr "Gör om"
+
+#: desktopGrid.js:326
+msgid "Show Desktop in Files"
+msgstr "Visa skrivbord i Filer"
+
+#: desktopGrid.js:327 fileItem.js:606
+msgid "Open in Terminal"
+msgstr "Öppna i terminal"
+
+#: desktopGrid.js:329
+msgid "Change Background…"
+msgstr "Ändra bakgrund…"
+
+#: desktopGrid.js:331
+msgid "Display Settings"
+msgstr "Visningsinställningar"
+
+#: desktopGrid.js:332
+msgid "Settings"
+msgstr "Inställningar"
+
+#: desktopGrid.js:576
+msgid "Enter file name…"
+msgstr "Ange filnamn…"
+
+#: desktopGrid.js:580
+msgid "OK"
+msgstr "OK"
+
+#: fileItem.js:490
+msgid "Don’t Allow Launching"
+msgstr "Tillåt ej programstart"
+
+#: fileItem.js:492
+msgid "Allow Launching"
+msgstr "Tillåt programstart"
+
+#: fileItem.js:574
+msgid "Open"
+msgstr "Öppna"
+
+#: fileItem.js:578
+msgid "Open With Other Application"
+msgstr "Öppna med annat program"
+
+#: fileItem.js:582
+msgid "Cut"
+msgstr "Klipp ut"
+
+#: fileItem.js:583
+msgid "Copy"
+msgstr "Kopiera"
+
+#: fileItem.js:585
+msgid "Rename…"
+msgstr "Byt namn…"
+
+#: fileItem.js:586
+msgid "Move to Trash"
+msgstr "Flytta till papperskorgen"
+
+#: fileItem.js:596
+msgid "Empty Trash"
+msgstr "Töm papperskorgen"
+
+#: fileItem.js:602
+msgid "Properties"
+msgstr "Egenskaper"
+
+#: fileItem.js:604
+msgid "Show in Files"
+msgstr "Visa i Filer"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:11
+msgid "Icon size"
+msgstr "Ikonstorlek"
+
+# TODO: *ON* the desktop?
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Set the size for the desktop icons."
+msgstr "Ställ in storleken för skrivbordsikonerna."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:16
+msgid "Show personal folder"
+msgstr "Visa personlig mapp"
+
+# TODO: *ON* the desktop?
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show the personal folder in the desktop."
+msgstr "Visa den personliga mappen på skrivbordet."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:21
+msgid "Show trash icon"
+msgstr "Visa papperskorgsikon"
+
+# TODO: *ON* the desktop?
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show the trash icon in the desktop."
+msgstr "Visa papperskorgsikonen på skrivbordet."
+
+#~ msgid "Huge"
+#~ msgstr "Enorm"
diff --git a/extensions/desktop-icons/po/tr.po b/extensions/desktop-icons/po/tr.po
new file mode 100644
index 0000000..2e5f5a7
--- /dev/null
+++ b/extensions/desktop-icons/po/tr.po
@@ -0,0 +1,191 @@
+# Turkish translation for desktop-icons.
+# Copyright (C) 2000-2019 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+#
+# Sabri Ünal <libreajans@gmail.com>, 2019.
+# Serdar Sağlam <teknomobil@yandex.com>, 2019
+# Emin Tufan Çetin <etcetin@gmail.com>, 2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2019-03-09 14:47+0000\n"
+"PO-Revision-Date: 2019-03-13 13:43+0300\n"
+"Last-Translator: Emin Tufan Çetin <etcetin@gmail.com>\n"
+"Language-Team: Türkçe <gnome-turk@gnome.org>\n"
+"Language: tr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Gtranslator 3.30.1\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: createFolderDialog.js:48
+msgid "New folder name"
+msgstr "Yeni klasör adı"
+
+#: createFolderDialog.js:72
+msgid "Create"
+msgstr "Oluştur"
+
+#: createFolderDialog.js:74 desktopGrid.js:586
+msgid "Cancel"
+msgstr "İptal"
+
+#: createFolderDialog.js:145
+msgid "Folder names cannot contain “/”."
+msgstr "Klasör adları “/” içeremez."
+
+#: createFolderDialog.js:148
+msgid "A folder cannot be called “.”."
+msgstr "Bir klasör “.” olarak adlandırılamaz."
+
+#: createFolderDialog.js:151
+msgid "A folder cannot be called “..”."
+msgstr "Bir klasör “..” olarak adlandırılamaz."
+
+#: createFolderDialog.js:153
+msgid "Folders with “.” at the beginning of their name are hidden."
+msgstr "Adlarının başında “.” bulunan klasörler gizlenir."
+
+#: createFolderDialog.js:155
+msgid "There is already a file or folder with that name."
+msgstr "Zaten bu adda bir dosya veya klasör var."
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Masaüstü simgeleri boyutu"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Küçük"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Standart"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Büyük"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Kişisel klasörü masaüstünde göster"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Çöp kutusunu masaüstünde göster"
+
+#: desktopGrid.js:320
+msgid "New Folder"
+msgstr "Yeni Klasör"
+
+#: desktopGrid.js:322
+msgid "Paste"
+msgstr "Yapıştır"
+
+#: desktopGrid.js:323
+msgid "Undo"
+msgstr "Geri Al"
+
+#: desktopGrid.js:324
+msgid "Redo"
+msgstr "Yinele"
+
+#: desktopGrid.js:326
+msgid "Show Desktop in Files"
+msgstr "Masaüstünü Dosyalarʼda Göster"
+
+#: desktopGrid.js:327 fileItem.js:606
+msgid "Open in Terminal"
+msgstr "Uçbirimde Aç"
+
+#: desktopGrid.js:329
+msgid "Change Background…"
+msgstr "Arka Planı Değiştir…"
+
+#: desktopGrid.js:331
+msgid "Display Settings"
+msgstr "Görüntü Ayarları"
+
+#: desktopGrid.js:332
+msgid "Settings"
+msgstr "Ayarlar"
+
+#: desktopGrid.js:576
+msgid "Enter file name…"
+msgstr "Dosya adını gir…"
+
+#: desktopGrid.js:580
+msgid "OK"
+msgstr "Tamam"
+
+#: fileItem.js:490
+msgid "Don’t Allow Launching"
+msgstr "Başlatmaya İzin Verme"
+
+#: fileItem.js:492
+msgid "Allow Launching"
+msgstr "Başlatmaya İzin Ver"
+
+#: fileItem.js:574
+msgid "Open"
+msgstr "Aç"
+
+#: fileItem.js:578
+msgid "Open With Other Application"
+msgstr "Başka Uygulamayla Aç"
+
+#: fileItem.js:582
+msgid "Cut"
+msgstr "Kes"
+
+#: fileItem.js:583
+msgid "Copy"
+msgstr "Kopyala"
+
+#: fileItem.js:585
+msgid "Rename…"
+msgstr "Yeniden Adlandır…"
+
+#: fileItem.js:586
+msgid "Move to Trash"
+msgstr "Çöpe Taşı"
+
+#: fileItem.js:596
+msgid "Empty Trash"
+msgstr "Çöpü Boşalt"
+
+#: fileItem.js:602
+msgid "Properties"
+msgstr "Özellikler"
+
+#: fileItem.js:604
+msgid "Show in Files"
+msgstr "Dosyalarʼda Göster"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:11
+msgid "Icon size"
+msgstr "Simge boyutu"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Set the size for the desktop icons."
+msgstr "Masaüstü simgelerinin boyutunu ayarla."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:16
+msgid "Show personal folder"
+msgstr "Kişisel klasörü göster"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show the personal folder in the desktop."
+msgstr "Kişisel klasörü masaüstünde göster."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:21
+msgid "Show trash icon"
+msgstr "Çöp kutusunu göster"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show the trash icon in the desktop."
+msgstr "Çöp kutusu simgesini masaüstünde göster."
diff --git a/extensions/desktop-icons/po/zh_TW.po b/extensions/desktop-icons/po/zh_TW.po
new file mode 100644
index 0000000..8ce4ab9
--- /dev/null
+++ b/extensions/desktop-icons/po/zh_TW.po
@@ -0,0 +1,135 @@
+# Chinese (Taiwan) translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Yi-Jyun Pan <pan93412@gmail.com>, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-10-22 14:12+0000\n"
+"PO-Revision-Date: 2018-10-24 21:31+0800\n"
+"Language-Team: Chinese (Taiwan) <chinese-l10n@googlegroups.com>\n"
+"Language: zh_TW\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Last-Translator: pan93412 <pan93412@gmail.com>\n"
+"X-Generator: Poedit 2.2\n"
+
+#: prefs.js:89
+msgid "Size for the desktop icons"
+msgstr "桌面圖示的大小"
+
+#: prefs.js:89
+msgid "Small"
+msgstr "小圖示"
+
+#: prefs.js:89
+msgid "Standard"
+msgstr "標準大小圖示"
+
+#: prefs.js:89
+msgid "Large"
+msgstr "大圖示"
+
+#: prefs.js:89
+msgid "Huge"
+msgstr "巨大圖示"
+
+#: prefs.js:90
+msgid "Show the personal folder in the desktop"
+msgstr "在桌面顯示個人資料夾"
+
+#: prefs.js:91
+msgid "Show the trash icon in the desktop"
+msgstr "在桌面顯示垃圾桶圖示"
+
+#: desktopGrid.js:178 desktopGrid.js:297
+msgid "New Folder"
+msgstr "新增資料夾"
+
+#: desktopGrid.js:299
+msgid "Paste"
+msgstr "貼上"
+
+#: desktopGrid.js:300
+msgid "Undo"
+msgstr "復原"
+
+#: desktopGrid.js:301
+msgid "Redo"
+msgstr "重做"
+
+#: desktopGrid.js:303
+msgid "Open Desktop in Files"
+msgstr "在《檔案》中開啟桌面"
+
+#: desktopGrid.js:304
+msgid "Open Terminal"
+msgstr "開啟終端器"
+
+#: desktopGrid.js:306
+msgid "Change Background…"
+msgstr "變更背景圖片…"
+
+#: desktopGrid.js:307
+msgid "Display Settings"
+msgstr "顯示設定"
+
+#: desktopGrid.js:308
+msgid "Settings"
+msgstr "設定"
+
+#: fileItem.js:223
+msgid "Open"
+msgstr "開啟"
+
+#: fileItem.js:226
+msgid "Cut"
+msgstr "剪下"
+
+#: fileItem.js:227
+msgid "Copy"
+msgstr "複製"
+
+#: fileItem.js:228
+msgid "Move to Trash"
+msgstr "移動到垃圾桶"
+
+#: fileItem.js:232
+msgid "Empty trash"
+msgstr "清空回收桶"
+
+#: fileItem.js:238
+msgid "Properties"
+msgstr "屬性"
+
+#: fileItem.js:240
+msgid "Show in Files"
+msgstr "在《檔案》中顯示"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Icon size"
+msgstr "圖示大小"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:13
+msgid "Set the size for the desktop icons."
+msgstr "設定桌面圖示的大小。"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show personal folder"
+msgstr "顯示個人資料夾"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:18
+msgid "Show the personal folder in the desktop."
+msgstr "在桌面顯示個人資料夾。"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show trash icon"
+msgstr "顯示垃圾桶圖示"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:23
+msgid "Show the trash icon in the desktop."
+msgstr "在桌面顯示垃圾桶圖示。"
diff --git a/extensions/desktop-icons/prefs.js b/extensions/desktop-icons/prefs.js
new file mode 100644
index 0000000..4b8d986
--- /dev/null
+++ b/extensions/desktop-icons/prefs.js
@@ -0,0 +1,159 @@
+
+/* Desktop Icons GNOME Shell extension
+ *
+ * Copyright (C) 2017 Carlos Soriano <csoriano@redhat.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+const Gtk = imports.gi.Gtk;
+const GObject = imports.gi.GObject;
+const Gio = imports.gi.Gio;
+const GioSSS = Gio.SettingsSchemaSource;
+const ExtensionUtils = imports.misc.extensionUtils;
+const Gettext = imports.gettext;
+
+const Config = imports.misc.config;
+
+var _ = Gettext.domain('desktop-icons').gettext;
+
+const SCHEMA_NAUTILUS = 'org.gnome.nautilus.preferences';
+const SCHEMA_GTK = 'org.gtk.Settings.FileChooser';
+const SCHEMA = 'org.gnome.shell.extensions.desktop-icons';
+
+const ICON_SIZE = { 'small': 48, 'standard': 64, 'large': 96 };
+const ICON_WIDTH = { 'small': 120, 'standard': 128, 'large': 128 };
+const ICON_HEIGHT = { 'small': 98, 'standard': 114, 'large': 146 };
+
+var FileType = {
+ NONE: null,
+ USER_DIRECTORY_HOME: 'show-home',
+ USER_DIRECTORY_TRASH: 'show-trash',
+}
+
+var nautilusSettings;
+var gtkSettings;
+var settings;
+// This is already in Nautilus settings, so it should not be made tweakable here
+var CLICK_POLICY_SINGLE = false;
+
+function initTranslations() {
+ let extension = ExtensionUtils.getCurrentExtension();
+
+ let localedir = extension.dir.get_child('locale');
+ if (localedir.query_exists(null))
+ Gettext.bindtextdomain('desktop-icons', localedir.get_path());
+ else
+ Gettext.bindtextdomain('desktop-icons', Config.LOCALEDIR);
+}
+
+function init() {
+ let schemaSource = GioSSS.get_default();
+ let schemaGtk = schemaSource.lookup(SCHEMA_GTK, true);
+ gtkSettings = new Gio.Settings({ settings_schema: schemaGtk });
+ let schemaObj = schemaSource.lookup(SCHEMA_NAUTILUS, true);
+ if (!schemaObj) {
+ nautilusSettings = null;
+ } else {
+ nautilusSettings = new Gio.Settings({ settings_schema: schemaObj });;
+ nautilusSettings.connect('changed', _onNautilusSettingsChanged);
+ _onNautilusSettingsChanged();
+ }
+ settings = get_schema(SCHEMA);
+}
+
+function get_schema(schema) {
+ let extension = ExtensionUtils.getCurrentExtension();
+
+ // 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 });
+}
+
+function buildPrefsWidget() {
+ initTranslations();
+ let frame = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, border_width: 10, spacing: 10 });
+
+ frame.add(buildSelector('icon-size', _("Size for the desktop icons"), { 'small': _("Small"), 'standard': _("Standard"), 'large': _("Large") }));
+ frame.add(buildSwitcher('show-home', _("Show the personal folder in the desktop")));
+ frame.add(buildSwitcher('show-trash', _("Show the trash icon in the desktop")));
+ frame.show_all();
+ return frame;
+}
+
+function buildSwitcher(key, labelText) {
+ let hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, spacing: 10 });
+ let label = new Gtk.Label({ label: labelText, xalign: 0 });
+ let switcher = new Gtk.Switch({ active: settings.get_boolean(key) });
+ settings.bind(key, switcher, 'active', 3);
+ hbox.pack_start(label, true, true, 0);
+ hbox.add(switcher);
+ return hbox;
+}
+
+function buildSelector(key, labelText, elements) {
+ let listStore = new Gtk.ListStore();
+ listStore.set_column_types ([GObject.TYPE_STRING, GObject.TYPE_STRING]);
+ let schemaKey = settings.settings_schema.get_key(key);
+ let values = schemaKey.get_range().get_child_value(1).get_child_value(0).get_strv();
+ for (let val of values) {
+ let iter = listStore.append();
+ let visibleText = val;
+ if (visibleText in elements)
+ visibleText = elements[visibleText];
+ listStore.set (iter, [0, 1], [visibleText, val]);
+ }
+ let hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, spacing: 10 });
+ let label = new Gtk.Label({ label: labelText, xalign: 0 });
+ let combo = new Gtk.ComboBox({model: listStore});
+ let rendererText = new Gtk.CellRendererText();
+ combo.pack_start (rendererText, false);
+ combo.add_attribute (rendererText, 'text', 0);
+ combo.set_id_column(1);
+ settings.bind(key, combo, 'active-id', 3);
+ hbox.pack_start(label, true, true, 0);
+ hbox.add(combo);
+ return hbox;
+}
+
+function _onNautilusSettingsChanged() {
+ CLICK_POLICY_SINGLE = nautilusSettings.get_string('click-policy') == 'single';
+}
+
+function get_icon_size() {
+ // this one doesn't need scaling because Gnome Shell automagically scales the icons
+ return ICON_SIZE[settings.get_string('icon-size')];
+}
+
+function get_desired_width(scale_factor) {
+ return ICON_WIDTH[settings.get_string('icon-size')] * scale_factor;
+}
+
+function get_desired_height(scale_factor) {
+ return ICON_HEIGHT[settings.get_string('icon-size')] * scale_factor;
+}
diff --git a/extensions/desktop-icons/schemas/meson.build b/extensions/desktop-icons/schemas/meson.build
new file mode 100644
index 0000000..2b17916
--- /dev/null
+++ b/extensions/desktop-icons/schemas/meson.build
@@ -0,0 +1,6 @@
+gnome.compile_schemas()
+
+install_data(
+ 'org.gnome.shell.extensions.desktop-icons.gschema.xml',
+ install_dir : schema_dir
+)
diff --git a/extensions/desktop-icons/schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml b/extensions/desktop-icons/schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml
new file mode 100644
index 0000000..bb4e50f
--- /dev/null
+++ b/extensions/desktop-icons/schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist gettext-domain="desktop-icons">
+ <enum id="org.gnome.shell.extension.desktop-icons.ZoomLevel">
+ <value value="0" nick="small"/>
+ <value value="1" nick="standard"/>
+ <value value="2" nick="large"/>
+ </enum>
+ <schema path="/org/gnome/shell/extensions/desktop-icons/" id="org.gnome.shell.extensions.desktop-icons">
+ <key name="icon-size" enum="org.gnome.shell.extension.desktop-icons.ZoomLevel">
+ <default>'standard'</default>
+ <summary>Icon size</summary>
+ <description>Set the size for the desktop icons.</description>
+ </key>
+ <key type="b" name="show-home">
+ <default>true</default>
+ <summary>Show personal folder</summary>
+ <description>Show the personal folder in the desktop.</description>
+ </key>
+ <key type="b" name="show-trash">
+ <default>true</default>
+ <summary>Show trash icon</summary>
+ <description>Show the trash icon in the desktop.</description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/extensions/desktop-icons/stylesheet.css b/extensions/desktop-icons/stylesheet.css
new file mode 100644
index 0000000..17d4b32
--- /dev/null
+++ b/extensions/desktop-icons/stylesheet.css
@@ -0,0 +1,38 @@
+.file-item {
+ padding: 4px;
+ border: 1px;
+ margin: 1px;
+}
+
+.file-item:hover {
+ background-color: rgba(238, 238, 238, 0.2);
+}
+
+.name-label {
+ text-shadow: 1px 1px black;
+ color: white;
+ text-align: center;
+}
+
+.draggable {
+ background-color: red;
+}
+
+.rename-popup {
+ min-width: 300px;
+ margin: 6px;
+}
+
+.create-folder-dialog-entry {
+ width: 20em;
+ margin-bottom: 6px;
+}
+
+.create-folder-dialog-label {
+ padding-bottom: .4em;
+}
+
+.create-folder-dialog-error-box {
+ padding-top: 16px;
+ spacing: 6px;
+}
diff --git a/meson.build b/meson.build
index cf855a0..6e8c41f 100644
--- a/meson.build
+++ b/meson.build
@@ -33,6 +33,7 @@ uuid_suffix = '@gnome-shell-extensions.gcampax.github.com'
classic_extensions = [
'apps-menu',
+ 'desktop-icons',
'places-menu',
'launch-new-instance',
'window-list'
diff --git a/po/cs.po b/po/cs.po
index 04bb195..e8f2fa3 100644
--- a/po/cs.po
+++ b/po/cs.po
@@ -1,11 +1,28 @@
+# #-#-#-#-# cs.po (gnome-shell-extensions) #-#-#-#-#
+# #-#-#-#-# cs.po (gnome-shell-extensions) #-#-#-#-#
# Czech 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.
# Petr Kovar <pknbe@volny.cz>, 2013.
# Marek Černocký <marek@manet.cz>, 2011, 2012, 2013, 2014, 2015, 2017.
#
+# #-#-#-#-# cs.po (desktop-icons master) #-#-#-#-#
+# Czech translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Marek Černocký <marek@manet.cz>, 2018.
+#
+# #-#-#-#-# cs.po (desktop-icons master) #-#-#-#-#
+# Czech translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Marek Černocký <marek@manet.cz>, 2018.
+#
+#, fuzzy
msgid ""
msgstr ""
+"#-#-#-#-# cs.po (gnome-shell-extensions) #-#-#-#-#\n"
+"#-#-#-#-# cs.po (gnome-shell-extensions) #-#-#-#-#\n"
"Project-Id-Version: gnome-shell-extensions\n"
"Report-Msgid-Bugs-To: https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=extensions\n"
@@ -19,6 +36,34 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
"X-Generator: Gtranslator 2.91.6\n"
+"#-#-#-#-# cs.po (desktop-icons master) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-10-01 20:15+0000\n"
+"PO-Revision-Date: 2018-10-02 11:10+0200\n"
+"Last-Translator: Marek Černocký <marek@manet.cz>\n"
+"Language-Team: čeština <gnome-cs-list@gnome.org>\n"
+"Language: cs\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==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+"X-Generator: Gtranslator 2.91.7\n"
+"#-#-#-#-# cs.po (desktop-icons master) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-10-01 20:15+0000\n"
+"PO-Revision-Date: 2018-10-02 11:10+0200\n"
+"Last-Translator: Marek Černocký <marek@manet.cz>\n"
+"Language-Team: čeština <gnome-cs-list@gnome.org>\n"
+"Language: cs\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==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+"X-Generator: Gtranslator 2.91.7\n"
#: data/gnome-classic.desktop.in:3 data/gnome-classic.session.desktop.in:3
msgid "GNOME Classic"
@@ -350,3 +395,119 @@ msgstr "Název"
#, javascript-format
msgid "Workspace %d"
msgstr "Pracovní plocha %d"
+
+#: prefs.js:89
+msgid "Size for the desktop icons"
+msgstr "Velikost ikon na pracovní ploše"
+
+#: prefs.js:89
+msgid "Small"
+msgstr "malé"
+
+#: prefs.js:89
+msgid "Standard"
+msgstr "standardní"
+
+#: prefs.js:89
+msgid "Large"
+msgstr "velké"
+
+#: prefs.js:89
+msgid "Huge"
+msgstr "obrovské"
+
+#: prefs.js:90
+msgid "Show the personal folder in the desktop"
+msgstr "Zobrazovat osobní složku na pracovní ploše"
+
+#: prefs.js:91
+msgid "Show the trash icon in the desktop"
+msgstr "Zobrazovat ikonu koše na pracovní ploše"
+
+#: desktopGrid.js:178 desktopGrid.js:297
+msgid "New Folder"
+msgstr "Nová složka"
+
+#: desktopGrid.js:299
+msgid "Paste"
+msgstr "Vložit"
+
+#: desktopGrid.js:300
+msgid "Undo"
+msgstr "Zpět"
+
+#: desktopGrid.js:301
+msgid "Redo"
+msgstr "Znovu"
+
+#: desktopGrid.js:303
+msgid "Open Desktop in Files"
+msgstr "Otevřít plochu v Souborech"
+
+#: desktopGrid.js:304
+msgid "Open Terminal"
+msgstr "Otevřít terminál"
+
+#: desktopGrid.js:306
+msgid "Change Background…"
+msgstr "Změnit pozadí…"
+
+#: desktopGrid.js:307
+msgid "Display Settings"
+msgstr "Zobrazit nastavení"
+
+#: desktopGrid.js:308
+msgid "Settings"
+msgstr "Nastavení"
+
+#: fileItem.js:226
+msgid "Open"
+msgstr "Otevřít"
+
+#: fileItem.js:229
+msgid "Cut"
+msgstr "Vyjmout"
+
+#: fileItem.js:230
+msgid "Copy"
+msgstr "Kopírovat"
+
+#: fileItem.js:231
+msgid "Move to Trash"
+msgstr "Přesunout do koše"
+
+#: fileItem.js:235
+msgid "Empty trash"
+msgstr "Vyprázdnit koš"
+
+#: fileItem.js:241
+msgid "Properties"
+msgstr "Vlastnosti"
+
+#: fileItem.js:243
+msgid "Show in Files"
+msgstr "Zobrazit v Souborech"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Icon size"
+msgstr "Velikost ikon"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:13
+msgid "Set the size for the desktop icons."
+msgstr "Nastavit velikost pro ikony na pracovní ploše."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show personal folder"
+msgstr "Zobrazovat osobní složku"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:18
+msgid "Show the personal folder in the desktop."
+msgstr "Zobrazovat osobní složku na pracovní ploše."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show trash icon"
+msgstr "Zobrazovat koš"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:23
+msgid "Show the trash icon in the desktop."
+msgstr "Zobrazovat ikonu koše na pracovní ploše."
diff --git a/po/da.po b/po/da.po
index 5a4c257..eeeef12 100644
--- a/po/da.po
+++ b/po/da.po
@@ -1,3 +1,5 @@
+# #-#-#-#-# da.po (gnome-shell-extensions master) #-#-#-#-#
+# #-#-#-#-# da.po (gnome-shell-extensions master) #-#-#-#-#
# Danish translation for gnome-shell-extensions.
# Copyright (C) 2011-2017 gnome-shell-extensions's COPYRIGHT HOLDER
# This file is distributed under the same license as the gnome-shell-extensions package.
@@ -6,8 +8,23 @@
# Ask Hjorth Larsen <asklarsen@gmail.com>, 2015, 2017.
# Joe Hansen <joedalton2@yahoo.dk>, 2017.
#
+# #-#-#-#-# da.po (desktop-icons master) #-#-#-#-#
+# Danish translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Alan Mortensen <alanmortensen.am@gmail.com>, 2018.
+#
+# #-#-#-#-# da.po (desktop-icons master) #-#-#-#-#
+# Danish translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Alan Mortensen <alanmortensen.am@gmail.com>, 2018.
+#
+#, fuzzy
msgid ""
msgstr ""
+"#-#-#-#-# da.po (gnome-shell-extensions master) #-#-#-#-#\n"
+"#-#-#-#-# da.po (gnome-shell-extensions master) #-#-#-#-#\n"
"Project-Id-Version: gnome-shell-extensions master\n"
"Report-Msgid-Bugs-To: https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=extensions\n"
@@ -20,6 +37,34 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"#-#-#-#-# da.po (desktop-icons master) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-10-26 12:04+0000\n"
+"PO-Revision-Date: 2018-10-27 16:42+0200\n"
+"Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
+"Language: da\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Last-Translator: Alan Mortensen <alanmortensen.am@gmail.com>\n"
+"X-Generator: Poedit 2.0.6\n"
+"#-#-#-#-# da.po (desktop-icons master) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-10-26 12:04+0000\n"
+"PO-Revision-Date: 2018-10-27 16:42+0200\n"
+"Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
+"Language: da\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Last-Translator: Alan Mortensen <alanmortensen.am@gmail.com>\n"
+"X-Generator: Poedit 2.0.6\n"
#: data/gnome-classic.desktop.in:3 data/gnome-classic.session.desktop.in:3
msgid "GNOME Classic"
@@ -359,3 +404,119 @@ msgstr "Navn"
#, javascript-format
msgid "Workspace %d"
msgstr "Arbejdsområde %d"
+
+#: prefs.js:89
+msgid "Size for the desktop icons"
+msgstr "Størrelsen på skrivebordsikoner"
+
+#: prefs.js:89
+msgid "Small"
+msgstr "Små"
+
+#: prefs.js:89
+msgid "Standard"
+msgstr "Standard"
+
+#: prefs.js:89
+msgid "Large"
+msgstr "Store"
+
+#: prefs.js:89
+msgid "Huge"
+msgstr "Enorme"
+
+#: prefs.js:90
+msgid "Show the personal folder in the desktop"
+msgstr "Vis den personlige mappe på skrivebordet"
+
+#: prefs.js:91
+msgid "Show the trash icon in the desktop"
+msgstr "Vis papirkurvsikonet på skrivebordet"
+
+#: desktopGrid.js:178 desktopGrid.js:297
+msgid "New Folder"
+msgstr "Ny mappe"
+
+#: desktopGrid.js:299
+msgid "Paste"
+msgstr "Indsæt"
+
+#: desktopGrid.js:300
+msgid "Undo"
+msgstr "Fortryd"
+
+#: desktopGrid.js:301
+msgid "Redo"
+msgstr "Omgør"
+
+#: desktopGrid.js:303
+msgid "Open Desktop in Files"
+msgstr "Åbn skrivebordet i Filer"
+
+#: desktopGrid.js:304
+msgid "Open Terminal"
+msgstr "Åbn Terminal"
+
+#: desktopGrid.js:306
+msgid "Change Background…"
+msgstr "Skift baggrund …"
+
+#: desktopGrid.js:307
+msgid "Display Settings"
+msgstr "Skærmindstillinger"
+
+#: desktopGrid.js:308
+msgid "Settings"
+msgstr "Indstillinger"
+
+#: fileItem.js:385
+msgid "Open"
+msgstr "Åbn"
+
+#: fileItem.js:388
+msgid "Cut"
+msgstr "Klip"
+
+#: fileItem.js:389
+msgid "Copy"
+msgstr "Kopiér"
+
+#: fileItem.js:390
+msgid "Move to Trash"
+msgstr "Flyt til papirkurven"
+
+#: fileItem.js:394
+msgid "Empty trash"
+msgstr "Tøm papirkurven"
+
+#: fileItem.js:400
+msgid "Properties"
+msgstr "Egenskaber"
+
+#: fileItem.js:402
+msgid "Show in Files"
+msgstr "Vis i Filer"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Icon size"
+msgstr "Ikonstørrelse"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:13
+msgid "Set the size for the desktop icons."
+msgstr "Angiv størrelsen på skrivebordsikoner."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show personal folder"
+msgstr "Vis personlig mappe"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:18
+msgid "Show the personal folder in the desktop."
+msgstr "Vis den personlige mappe på skrivebordet."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show trash icon"
+msgstr "Vis papirkurvsikon"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:23
+msgid "Show the trash icon in the desktop."
+msgstr "Vis papirkurvsikonet på skrivebordet."
diff --git a/po/de.po b/po/de.po
index 01924b8..6ca034e 100644
--- a/po/de.po
+++ b/po/de.po
@@ -1,3 +1,5 @@
+# #-#-#-#-# de.po (gnome-shell-extensions master) #-#-#-#-#
+# #-#-#-#-# de.po (gnome-shell-extensions master) #-#-#-#-#
# German 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.
@@ -7,8 +9,23 @@
# Wolfgang Stöggl <c72578@yahoo.de>, 2014.
# Paul Seyfert <pseyfert@mathphys.fsk.uni-heidelberg.de>, 2017.
#
+# #-#-#-#-# de.po (desktop-icons master) #-#-#-#-#
+# German translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Mario Blättermann <mario.blaettermann@gmail.com>, 2018.
+#
+# #-#-#-#-# de.po (desktop-icons master) #-#-#-#-#
+# German translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Mario Blättermann <mario.blaettermann@gmail.com>, 2018.
+#
+#, fuzzy
msgid ""
msgstr ""
+"#-#-#-#-# de.po (gnome-shell-extensions master) #-#-#-#-#\n"
+"#-#-#-#-# de.po (gnome-shell-extensions master) #-#-#-#-#\n"
"Project-Id-Version: gnome-shell-extensions master\n"
"Report-Msgid-Bugs-To: https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=extensions\n"
@@ -22,6 +39,34 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.0.2\n"
+"#-#-#-#-# de.po (desktop-icons master) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-10-02 21:39+0000\n"
+"PO-Revision-Date: 2018-10-03 21:28+0200\n"
+"Last-Translator: Mario Blättermann <mario.blaettermann@gmail.com>\n"
+"Language-Team: German <gnome-de@gnome.org>\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.1.1\n"
+"#-#-#-#-# de.po (desktop-icons master) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-10-02 21:39+0000\n"
+"PO-Revision-Date: 2018-10-03 21:28+0200\n"
+"Last-Translator: Mario Blättermann <mario.blaettermann@gmail.com>\n"
+"Language-Team: German <gnome-de@gnome.org>\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.1.1\n"
#: data/gnome-classic.desktop.in:3 data/gnome-classic.session.desktop.in:3
msgid "GNOME Classic"
@@ -366,3 +411,119 @@ msgstr "Name"
#, javascript-format
msgid "Workspace %d"
msgstr "Arbeitsfläche %d"
+
+#: prefs.js:89
+msgid "Size for the desktop icons"
+msgstr "Größe der Arbeitsflächensymbole"
+
+#: prefs.js:89
+msgid "Small"
+msgstr "Klein"
+
+#: prefs.js:89
+msgid "Standard"
+msgstr "Standard"
+
+#: prefs.js:89
+msgid "Large"
+msgstr "Groß"
+
+#: prefs.js:89
+msgid "Huge"
+msgstr "Riesig"
+
+#: prefs.js:90
+msgid "Show the personal folder in the desktop"
+msgstr "Den persönlichen Ordner auf der Arbeitsfläche anzeigen"
+
+#: prefs.js:91
+msgid "Show the trash icon in the desktop"
+msgstr "Papierkorb-Symbol auf der Arbeitsfläche anzeigen"
+
+#: desktopGrid.js:178 desktopGrid.js:297
+msgid "New Folder"
+msgstr "Neuer Ordner"
+
+#: desktopGrid.js:299
+msgid "Paste"
+msgstr "Einfügen"
+
+#: desktopGrid.js:300
+msgid "Undo"
+msgstr "Rückgängig"
+
+#: desktopGrid.js:301
+msgid "Redo"
+msgstr "Wiederholen"
+
+#: desktopGrid.js:303
+msgid "Open Desktop in Files"
+msgstr "Arbeitsfläche in Dateiverwaltung öffnen"
+
+#: desktopGrid.js:304
+msgid "Open Terminal"
+msgstr "Terminal öffnen"
+
+#: desktopGrid.js:306
+msgid "Change Background…"
+msgstr "Hintergrund ändern …"
+
+#: desktopGrid.js:307
+msgid "Display Settings"
+msgstr "Anzeigeeinstellungen"
+
+#: desktopGrid.js:308
+msgid "Settings"
+msgstr "Einstellungen"
+
+#: fileItem.js:226
+msgid "Open"
+msgstr "Öffnen"
+
+#: fileItem.js:229
+msgid "Cut"
+msgstr "Ausschneiden"
+
+#: fileItem.js:230
+msgid "Copy"
+msgstr "Kopieren"
+
+#: fileItem.js:231
+msgid "Move to Trash"
+msgstr "In den Papierkorb verschieben"
+
+#: fileItem.js:235
+msgid "Empty trash"
+msgstr "Papierkorb leeren"
+
+#: fileItem.js:241
+msgid "Properties"
+msgstr "Eigenschaften"
+
+#: fileItem.js:243
+msgid "Show in Files"
+msgstr "In Dateiverwaltung anzeigen"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Icon size"
+msgstr "Symbolgröße"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:13
+msgid "Set the size for the desktop icons."
+msgstr "Die Größe der Arbeitsflächensymbole festlegen."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show personal folder"
+msgstr "Persönlichen Ordner anzeigen"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:18
+msgid "Show the personal folder in the desktop."
+msgstr "Den persönlichen Ordner auf der Arbeitsfläche anzeigen."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show trash icon"
+msgstr "Papierkorb-Symbol anzeigen"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:23
+msgid "Show the trash icon in the desktop."
+msgstr "Das Papierkorb-Symbol auf der Arbeitsfläche anzeigen."
diff --git a/po/es.po b/po/es.po
index a3c0703..18cf8d4 100644
--- a/po/es.po
+++ b/po/es.po
@@ -1,3 +1,5 @@
+# #-#-#-#-# es.po (gnome-shell-extensions master) #-#-#-#-#
+# #-#-#-#-# es.po (gnome-shell-extensions master) #-#-#-#-#
# 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.
@@ -6,8 +8,27 @@
#
# Daniel Mustieles <daniel.mustieles@gmail.com>, 2011-2015, 2017.
#
+# #-#-#-#-# es.po (PACKAGE VERSION) #-#-#-#-#
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# Sergio Costas <rastersoft@gmail.com>, 2018.
+# Daniel Mustieles <daniel.mustieles@gmail.com>, 2018, 2019.
+#
+# #-#-#-#-# es.po (PACKAGE VERSION) #-#-#-#-#
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# Sergio Costas <rastersoft@gmail.com>, 2018.
+# Daniel Mustieles <daniel.mustieles@gmail.com>, 2018, 2019.
+#
+#, fuzzy
msgid ""
msgstr ""
+"#-#-#-#-# es.po (gnome-shell-extensions master) #-#-#-#-#\n"
+"#-#-#-#-# es.po (gnome-shell-extensions master) #-#-#-#-#\n"
"Project-Id-Version: gnome-shell-extensions master\n"
"Report-Msgid-Bugs-To: https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=extensions\n"
@@ -21,6 +42,34 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Gtranslator 2.91.6\n"
+"#-#-#-#-# es.po (PACKAGE VERSION) #-#-#-#-#\n"
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-12-14 09:12+0000\n"
+"PO-Revision-Date: 2019-01-08 16:12+0100\n"
+"Last-Translator: Daniel Mustieles <daniel.mustieles@gmail.com>\n"
+"Language-Team: es <gnome-es-list@gnome.org>\n"
+"Language: es\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Gtranslator 2.91.7\n"
+"#-#-#-#-# es.po (PACKAGE VERSION) #-#-#-#-#\n"
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-12-14 09:12+0000\n"
+"PO-Revision-Date: 2019-01-08 16:12+0100\n"
+"Last-Translator: Daniel Mustieles <daniel.mustieles@gmail.com>\n"
+"Language-Team: es <gnome-es-list@gnome.org>\n"
+"Language: es\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Gtranslator 2.91.7\n"
#: data/gnome-classic.desktop.in:3 data/gnome-classic.session.desktop.in:3
msgid "GNOME Classic"
@@ -361,6 +410,146 @@ msgstr "Nombre"
msgid "Workspace %d"
msgstr "Área de trabajo %d"
+#: desktopGrid.js:311
+msgid "Display Settings"
+msgstr "Configuración de pantalla"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Icon size"
+msgstr "Tamaño de los iconos"
+
+#: desktopGrid.js:578
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Tamaño de los iconos del escritorio"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Pequeño"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Estándar"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Grande"
+
+#: prefs.js:102
+msgid "Huge"
+msgstr "Inmenso"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Mostrar la carpeta personal en el escritorio"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Mostrar la papelera en el escritorio"
+
+#: desktopGrid.js:182 desktopGrid.js:301
+msgid "New Folder"
+msgstr "Nueva carpeta"
+
+#: desktopGrid.js:303
+msgid "Paste"
+msgstr "Pegar"
+
+#: desktopGrid.js:304
+msgid "Undo"
+msgstr "Deshacer"
+
+#: desktopGrid.js:305
+msgid "Redo"
+msgstr "Rehacer"
+
+#: desktopGrid.js:307
+msgid "Open Desktop in Files"
+msgstr "Abrir el escritorio en Files"
+
+#: desktopGrid.js:308
+msgid "Open Terminal"
+msgstr "Abrir un terminal"
+
+#: desktopGrid.js:310
+msgid "Change Background…"
+msgstr "Cambiar el fondo..."
+
+#: desktopGrid.js:312
+msgid "Settings"
+msgstr "Configuración"
+
+#: desktopGrid.js:568
+msgid "Enter file name…"
+msgstr "Introduzca el nombre del archivo…"
+
+#: desktopGrid.js:572
+msgid "OK"
+msgstr "Aceptar"
+
+#: fileItem.js:485
+msgid "Don’t Allow Launching"
+msgstr "No permitir lanzar"
+
+#: fileItem.js:487
+msgid "Allow Launching"
+msgstr "Permitir lanzar"
+
+#: fileItem.js:550
+msgid "Open"
+msgstr "Abrir"
+
+#: fileItem.js:553
+msgid "Cut"
+msgstr "Cortar"
+
+#: fileItem.js:554
+msgid "Copy"
+msgstr "Copiar"
+
+#: fileItem.js:556
+msgid "Rename"
+msgstr "Renombrar"
+
+#: fileItem.js:557
+msgid "Move to Trash"
+msgstr "Mover a la papelera"
+
+#: fileItem.js:567
+msgid "Empty Trash"
+msgstr "Vaciar la papelera"
+
+#: fileItem.js:573
+msgid "Properties"
+msgstr "Propiedades"
+
+#: fileItem.js:575
+msgid "Show in Files"
+msgstr "Mostrar en Files"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:13
+msgid "Set the size for the desktop icons."
+msgstr "Establece el tamaño de los iconos del escritorio."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show personal folder"
+msgstr "Mostrar la carpeta personal"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:18
+msgid "Show the personal folder in the desktop."
+msgstr "Mostrar la carpeta personal en el escritorio."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show trash icon"
+msgstr "Mostrar la papelera"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:23
+msgid "Show the trash icon in the desktop."
+msgstr "Mostrar la papelera en el escritorio."
+
#~ msgid "CPU"
#~ msgstr "CPU"
@@ -409,9 +598,6 @@ msgstr "Área de trabajo %d"
#~ msgid "Display"
#~ msgstr "Pantalla"
-#~ msgid "Display Settings"
-#~ msgstr "Configuración de pantalla"
-
#~ msgid "File System"
#~ msgstr "Sistema de archivos"
@@ -459,9 +645,6 @@ msgstr "Área de trabajo %d"
#~ "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."
@@ -632,9 +815,6 @@ msgstr "Área de trabajo %d"
#~ msgid "Alt Tab Behaviour"
#~ msgstr "Comportamiento de Alt+Tab"
-#~ msgid "Cancel"
-#~ msgstr "Cancelar"
-
#~ msgid "Notifications"
#~ msgstr "Notificaciones"
@@ -668,3 +848,27 @@ msgstr "Área de trabajo %d"
#~ msgid "Busy"
#~ msgstr "Ocupado"
+
+#~ msgid "Ok"
+#~ msgstr "Aceptar"
+
+#~ msgid "huge"
+#~ msgstr "inmenso"
+
+#~ msgid "Maximum width for the icons and filename."
+#~ msgstr "Ancho máximo de los iconos y el nombre de fichero."
+
+#~ msgid "Shows the Documents folder in the desktop."
+#~ msgstr "Muestra la carpeta Documentos en el escritorio."
+
+#~ msgid "Shows the Downloads folder in the desktop."
+#~ msgstr "Muestra la carpeta Descargas en el escritorio."
+
+#~ msgid "Shows the Music folder in the desktop."
+#~ msgstr "Muestra la carpeta Música en el escritorio."
+
+#~ msgid "Shows the Pictures folder in the desktop."
+#~ msgstr "Muestra la carpeta Imágenes en el escritorio."
+
+#~ msgid "Shows the Videos folder in the desktop."
+#~ msgstr "Muestra la carpeta Vídeos en el escritorio."
diff --git a/po/fi.po b/po/fi.po
index e036448..73b33cc 100644
--- a/po/fi.po
+++ b/po/fi.po
@@ -1,3 +1,5 @@
+# #-#-#-#-# fi.po (gnome-shell-extensions) #-#-#-#-#
+# #-#-#-#-# fi.po (gnome-shell-extensions) #-#-#-#-#
# Finnish translation of gnome-shell-extensions.
# Copyright (C) 2011 Ville-Pekka Vainio
# This file is distributed under the same license as the gnome-shell-extensions package.
@@ -7,8 +9,23 @@
# Ville-Pekka Vainio <vpvainio@iki.fi>, 2011.
# Jiri Grönroos <jiri.gronroos+l10n@iki.fi>, 2012, 2013, 2014, 2015, 2017.
#
+# #-#-#-#-# fi.po (desktop-icons master) #-#-#-#-#
+# Finnish translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Jiri Grönroos <jiri.gronroos@iki.fi>, 2018.
+#
+# #-#-#-#-# fi.po (desktop-icons master) #-#-#-#-#
+# Finnish translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Jiri Grönroos <jiri.gronroos@iki.fi>, 2018.
+#
+#, fuzzy
msgid ""
msgstr ""
+"#-#-#-#-# fi.po (gnome-shell-extensions) #-#-#-#-#\n"
+"#-#-#-#-# fi.po (gnome-shell-extensions) #-#-#-#-#\n"
"Project-Id-Version: gnome-shell-extensions\n"
"Report-Msgid-Bugs-To: https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=extensions\n"
@@ -24,6 +41,34 @@ msgstr ""
"X-Generator: Gtranslator 2.91.7\n"
"X-Project-Style: gnome\n"
"X-POT-Import-Date: 2012-03-05 15:06:12+0000\n"
+"#-#-#-#-# fi.po (desktop-icons master) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-10-14 08:09+0000\n"
+"PO-Revision-Date: 2018-10-14 12:44+0300\n"
+"Language-Team: Finnish <lokalisointi-lista@googlegroups.com>\n"
+"Language: fi\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Last-Translator: Jiri Grönroos <jiri.gronroos+l10n@iki.fi>\n"
+"X-Generator: Poedit 2.0.6\n"
+"#-#-#-#-# fi.po (desktop-icons master) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-10-14 08:09+0000\n"
+"PO-Revision-Date: 2018-10-14 12:44+0300\n"
+"Language-Team: Finnish <lokalisointi-lista@googlegroups.com>\n"
+"Language: fi\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Last-Translator: Jiri Grönroos <jiri.gronroos+l10n@iki.fi>\n"
+"X-Generator: Poedit 2.0.6\n"
#: data/gnome-classic.desktop.in:3 data/gnome-classic.session.desktop.in:3
msgid "GNOME Classic"
@@ -352,6 +397,122 @@ msgstr "Nimi"
msgid "Workspace %d"
msgstr "Työtila %d"
+#: desktopGrid.js:307
+msgid "Display Settings"
+msgstr "Näytön asetukset"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Icon size"
+msgstr "Kuvakekoko"
+
+#: prefs.js:89
+msgid "Size for the desktop icons"
+msgstr "Työpöytäkuvakkeiden koko"
+
+#: prefs.js:89
+msgid "Small"
+msgstr "Pieni"
+
+#: prefs.js:89
+msgid "Standard"
+msgstr "Normaali"
+
+#: prefs.js:89
+msgid "Large"
+msgstr "Suuri"
+
+#: prefs.js:89
+msgid "Huge"
+msgstr "Valtava"
+
+#: prefs.js:90
+msgid "Show the personal folder in the desktop"
+msgstr "Näytä kotikansio työpöydällä"
+
+#: prefs.js:91
+msgid "Show the trash icon in the desktop"
+msgstr "Näytä roskakorin kuvake työpöydällä"
+
+#: desktopGrid.js:178 desktopGrid.js:297
+msgid "New Folder"
+msgstr "Uusi kansio"
+
+#: desktopGrid.js:299
+msgid "Paste"
+msgstr "Liitä"
+
+#: desktopGrid.js:300
+msgid "Undo"
+msgstr "Kumoa"
+
+#: desktopGrid.js:301
+msgid "Redo"
+msgstr "Tee uudeleen"
+
+#: desktopGrid.js:303
+msgid "Open Desktop in Files"
+msgstr "Avaa työpöytä tiedostonhallinnassa"
+
+#: desktopGrid.js:304
+msgid "Open Terminal"
+msgstr "Avaa pääte"
+
+#: desktopGrid.js:306
+msgid "Change Background…"
+msgstr "Vaihda taustakuvaa…"
+
+#: desktopGrid.js:308
+msgid "Settings"
+msgstr "Asetukset"
+
+#: fileItem.js:211
+msgid "Open"
+msgstr "Avaa"
+
+#: fileItem.js:214
+msgid "Cut"
+msgstr "Leikkaa"
+
+#: fileItem.js:215
+msgid "Copy"
+msgstr "Kopioi"
+
+#: fileItem.js:216
+msgid "Move to Trash"
+msgstr "Siirrä roskakoriin"
+
+#: fileItem.js:220
+msgid "Empty trash"
+msgstr "Tyhjennä roskakori"
+
+#: fileItem.js:226
+msgid "Properties"
+msgstr "Ominaisuudet"
+
+#: fileItem.js:228
+msgid "Show in Files"
+msgstr "Näytä tiedostonhallinnassa"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:13
+msgid "Set the size for the desktop icons."
+msgstr "Aseta työpöytäkuvakkeiden koko."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show personal folder"
+msgstr "Näytä kotikansio"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:18
+msgid "Show the personal folder in the desktop."
+msgstr "Näytä kotikansio työpöydällä."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show trash icon"
+msgstr "Näytä roskakorin kuvake"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:23
+msgid "Show the trash icon in the desktop."
+msgstr "Näytä roskakorin kuvake työpöydällä."
+
#~ msgid "CPU"
#~ msgstr "Suoritin"
@@ -388,9 +549,6 @@ msgstr "Työtila %d"
#~ msgid "Display"
#~ msgstr "Näyttö"
-#~ msgid "Display Settings"
-#~ msgstr "Näytön asetukset"
-
#~ msgid "Drag here to add favorites"
#~ msgstr "Raahaa tähän lisätäksesi suosikkeihin"
@@ -412,9 +570,6 @@ msgstr "Työtila %d"
#~ msgstr ""
#~ "Asettaa telakan sijainnin näytöllä. Sallitut arvot ovat 'right' tai 'left'"
-#~ msgid "Icon size"
-#~ msgstr "Kuvakkeiden koko"
-
#~ msgid "Sets icon size of the dock."
#~ msgstr "Asettaa telakan kuvakkeiden koon."
diff --git a/po/fr.po b/po/fr.po
index 6825d0d..a61cc76 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -1,3 +1,5 @@
+# #-#-#-#-# fr.po (gnome-shell-extensions master) #-#-#-#-#
+# #-#-#-#-# fr.po (gnome-shell-extensions master) #-#-#-#-#
# French translation for gnome-shell-extensions.
# Copyright (C) 2011-12 Listed translators
# This file is distributed under the same license as the gnome-shell-extensions package.
@@ -5,8 +7,25 @@
# Alain Lojewski <allomervan@gmail.com>, 2012-2013.
# Charles Monzat <charles.monzat@numericable.fr>, 2018.
#
+# #-#-#-#-# fr.po (desktop-icons master) #-#-#-#-#
+# French translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# ghentdebian <ghent.debian@gmail.com>, 2018.
+# Charles Monzat <charles.monzat@numericable.fr>, 2018.
+#
+# #-#-#-#-# fr.po (desktop-icons master) #-#-#-#-#
+# French translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# ghentdebian <ghent.debian@gmail.com>, 2018.
+# Charles Monzat <charles.monzat@numericable.fr>, 2018.
+#
+#, fuzzy
msgid ""
msgstr ""
+"#-#-#-#-# fr.po (gnome-shell-extensions master) #-#-#-#-#\n"
+"#-#-#-#-# fr.po (gnome-shell-extensions master) #-#-#-#-#\n"
"Project-Id-Version: gnome-shell-extensions master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell-extensions/"
"issues\n"
@@ -19,6 +38,33 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"#-#-#-#-# fr.po (desktop-icons master) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-12-14 09:12+0000\n"
+"PO-Revision-Date: 2018-12-16 17:47+0100\n"
+"Last-Translator: Charles Monzat <charles.monzat@numericable.fr>\n"
+"Language-Team: GNOME French Team <gnomefr@traduc.org>\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+"X-Generator: Gtranslator 3.30.0\n"
+"#-#-#-#-# fr.po (desktop-icons master) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-12-14 09:12+0000\n"
+"PO-Revision-Date: 2018-12-16 17:47+0100\n"
+"Last-Translator: Charles Monzat <charles.monzat@numericable.fr>\n"
+"Language-Team: GNOME French Team <gnomefr@traduc.org>\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
"X-Generator: Gtranslator 3.30.0\n"
#: data/gnome-classic.desktop.in:3 data/gnome-classic.session.desktop.in:3
@@ -354,9 +400,151 @@ msgstr "Espace de travail %d"
#~ msgstr ""
#~ "Retarder les changements de focus en mode souris jusqu’à ce que le "
#~ "pointeur arrête de bouger"
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Taille des icônes du bureau"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Petite"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Normale"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Grande"
+
+#: prefs.js:102
+msgid "Huge"
+msgstr "Immense"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Montrer le dossier personnel sur le bureau"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Montrer la corbeille sur le bureau"
+
+#: desktopGrid.js:182 desktopGrid.js:301
+msgid "New Folder"
+msgstr "Nouveau dossier"
+
+#: desktopGrid.js:303
+msgid "Paste"
+msgstr "Coller"
+
+#: desktopGrid.js:304
+msgid "Undo"
+msgstr "Annuler"
+
+#: desktopGrid.js:305
+msgid "Redo"
+msgstr "Refaire"
+
+#: desktopGrid.js:307
+msgid "Open Desktop in Files"
+msgstr "Ouvrir le bureau dans Fichiers"
+
+#: desktopGrid.js:308
+msgid "Open Terminal"
+msgstr "Ouvrir un terminal"
+
+#: desktopGrid.js:310
+msgid "Change Background…"
+msgstr "Changer l’arrière-plan…"
+
+#: desktopGrid.js:311
+msgid "Display Settings"
+msgstr "Configuration d’affichage"
+
+#: desktopGrid.js:312
+msgid "Settings"
+msgstr "Paramètres"
+
+#: desktopGrid.js:568
+msgid "Enter file name…"
+msgstr "Saisir un nom de fichier…"
+
+#: desktopGrid.js:572
+msgid "OK"
+msgstr "Valider"
+
+#: desktopGrid.js:578
+msgid "Cancel"
+msgstr "Annuler"
+
+#: fileItem.js:485
+msgid "Don’t Allow Launching"
+msgstr "Ne pas autoriser le lancement"
+
+#: fileItem.js:487
+msgid "Allow Launching"
+msgstr "Autoriser le lancement"
+
+#: fileItem.js:550
+msgid "Open"
+msgstr "Ouvrir"
+
+#: fileItem.js:553
+msgid "Cut"
+msgstr "Couper"
+
+#: fileItem.js:554
+msgid "Copy"
+msgstr "Copier"
+
+#: fileItem.js:556
+msgid "Rename"
+msgstr "Renommer"
+
+#: fileItem.js:557
+msgid "Move to Trash"
+msgstr "Mettre à la corbeille"
+
+#: fileItem.js:567
+msgid "Empty Trash"
+msgstr "Vider la corbeille"
+
+#: fileItem.js:573
+msgid "Properties"
+msgstr "Propriétés"
+
+#: fileItem.js:575
+msgid "Show in Files"
+msgstr "Montrer dans Fichiers"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Icon size"
+msgstr "Taille d’icônes"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:13
+msgid "Set the size for the desktop icons."
+msgstr "Définir la taille des icônes du bureau."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show personal folder"
+msgstr "Montrer le dossier personnel"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:18
+msgid "Show the personal folder in the desktop."
+msgstr "Montrer le dossier personnel sur le bureau."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show trash icon"
+msgstr "Montrer l’icône de la corbeille"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:23
+msgid "Show the trash icon in the desktop."
+msgstr "Montrer la corbeille sur le bureau."
#~ msgid "CPU"
#~ msgstr "CPU"
#~ msgid "Memory"
#~ msgstr "Mémoire"
+
+#~ msgid "Ok"
+#~ msgstr "Valider"
diff --git a/po/id.po b/po/id.po
index 8986a5e..033ae90 100644
--- a/po/id.po
+++ b/po/id.po
@@ -1,11 +1,28 @@
+# #-#-#-#-# id.po (gnome-shell-extensions master) #-#-#-#-#
+# #-#-#-#-# id.po (gnome-shell-extensions master) #-#-#-#-#
# Indonesian 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.
#
# Andika Triwidada <andika@gmail.com>, 2012, 2013.
# Dirgita <dirgitadevina@yahoo.co.id>, 2012.
+# #-#-#-#-# id.po (desktop-icons master) #-#-#-#-#
+# Indonesian translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+# #-#-#-#-# id.po (desktop-icons master) #-#-#-#-#
+# Indonesian translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
msgid ""
msgstr ""
+"#-#-#-#-# id.po (gnome-shell-extensions master) #-#-#-#-#\n"
+"#-#-#-#-# id.po (gnome-shell-extensions master) #-#-#-#-#\n"
"Project-Id-Version: gnome-shell-extensions master\n"
"Report-Msgid-Bugs-To: https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=extensions\n"
@@ -20,6 +37,32 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Generator: Poedit 2.0.2\n"
+"#-#-#-#-# id.po (desktop-icons master) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-10-02 09:18+0000\n"
+"PO-Revision-Date: 2018-10-02 20:30+0700\n"
+"Language-Team: Indonesian <gnome-l10n-id@googlegroups.com>\n"
+"Language: id\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Last-Translator: Kukuh Syafaat <kukuhsyafaat@gnome.org>\n"
+"X-Generator: Poedit 2.0.6\n"
+"#-#-#-#-# id.po (desktop-icons master) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-10-02 09:18+0000\n"
+"PO-Revision-Date: 2018-10-02 20:30+0700\n"
+"Language-Team: Indonesian <gnome-l10n-id@googlegroups.com>\n"
+"Language: id\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Last-Translator: Kukuh Syafaat <kukuhsyafaat@gnome.org>\n"
+"X-Generator: Poedit 2.0.6\n"
#: data/gnome-classic.desktop.in:3 data/gnome-classic.session.desktop.in:3
msgid "GNOME Classic"
@@ -358,6 +401,122 @@ msgstr "Nama"
msgid "Workspace %d"
msgstr "Ruang Kerja %d"
+#: prefs.js:89
+msgid "Size for the desktop icons"
+msgstr "Ukuran untuk ikon destop"
+
+#: prefs.js:89
+msgid "Small"
+msgstr "Kecil"
+
+#: prefs.js:89
+msgid "Standard"
+msgstr "Standar"
+
+#: prefs.js:89
+msgid "Large"
+msgstr "Besar"
+
+#: prefs.js:89
+msgid "Huge"
+msgstr "Sangat besar"
+
+#: prefs.js:90
+msgid "Show the personal folder in the desktop"
+msgstr "Tampilkan folder pribadi di destop"
+
+#: prefs.js:91
+msgid "Show the trash icon in the desktop"
+msgstr "Tampilkan ikon tong sampah di destop"
+
+#: desktopGrid.js:178 desktopGrid.js:297
+msgid "New Folder"
+msgstr "Folder Baru"
+
+#: desktopGrid.js:299
+msgid "Paste"
+msgstr "Tempel"
+
+#: desktopGrid.js:300
+msgid "Undo"
+msgstr "Tak Jadi"
+
+#: desktopGrid.js:301
+msgid "Redo"
+msgstr "Jadi Lagi"
+
+#: desktopGrid.js:303
+msgid "Open Desktop in Files"
+msgstr "Buka Destop pada Berkas"
+
+#: desktopGrid.js:304
+msgid "Open Terminal"
+msgstr "Buka Terminal"
+
+#: desktopGrid.js:306
+msgid "Change Background…"
+msgstr "Ubah Latar Belakang…"
+
+#: desktopGrid.js:307
+msgid "Display Settings"
+msgstr "Pengaturan Tampilan"
+
+#: desktopGrid.js:308
+msgid "Settings"
+msgstr "Pengaturan"
+
+#: fileItem.js:226
+msgid "Open"
+msgstr "Buka"
+
+#: fileItem.js:229
+msgid "Cut"
+msgstr "Potong"
+
+#: fileItem.js:230
+msgid "Copy"
+msgstr "Salin"
+
+#: fileItem.js:231
+msgid "Move to Trash"
+msgstr "Pindahkan ke Tong Sampah"
+
+#: fileItem.js:235
+msgid "Empty trash"
+msgstr "Kosongkan Tong Sampah"
+
+#: fileItem.js:241
+msgid "Properties"
+msgstr "Properti"
+
+#: fileItem.js:243
+msgid "Show in Files"
+msgstr "Tampilkan pada Berkas"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Icon size"
+msgstr "Ukuran ikon"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:13
+msgid "Set the size for the desktop icons."
+msgstr "Set ukuran untuk ikon destop."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show personal folder"
+msgstr "Tampilkan folder pribadi"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:18
+msgid "Show the personal folder in the desktop."
+msgstr "Tampilkan folder pribadi di destop."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show trash icon"
+msgstr "Tampilkan ikon tong sampah"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:23
+msgid "Show the trash icon in the desktop."
+msgstr "Tampilkan ikon tong sampah di destop."
+
#~ msgid "CPU"
#~ msgstr "CPU"
diff --git a/po/it.po b/po/it.po
index 4e3a59c..1d6fd26 100644
--- a/po/it.po
+++ b/po/it.po
@@ -1,3 +1,5 @@
+# #-#-#-#-# it.po (gnome-shell-extensions) #-#-#-#-#
+# #-#-#-#-# it.po (gnome-shell-extensions) #-#-#-#-#
# Italian translations for GNOME Shell extensions
# Copyright (C) 2011 Giovanni Campagna et al.
# Copyright (C) 2012, 2013, 2014, 2015, 2017 The Free Software Foundation, Inc.
@@ -6,8 +8,23 @@
# Milo Casagrande <milo@milo.name>, 2013, 2014, 2015, 2017.
# Gianvito Cavasoli <gianvito@gmx.it>, 2017.
#
+# #-#-#-#-# it.po (desktop-icons master) #-#-#-#-#
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Massimo Branchini <max.bra.gtalk@gmail.com>, 2018.
+#
+# #-#-#-#-# it.po (desktop-icons master) #-#-#-#-#
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Massimo Branchini <max.bra.gtalk@gmail.com>, 2018.
+#
+#, fuzzy
msgid ""
msgstr ""
+"#-#-#-#-# it.po (gnome-shell-extensions) #-#-#-#-#\n"
+"#-#-#-#-# it.po (gnome-shell-extensions) #-#-#-#-#\n"
"Project-Id-Version: gnome-shell-extensions\n"
"Report-Msgid-Bugs-To: https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=extensions\n"
@@ -21,6 +38,34 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 1.8.12\n"
+"#-#-#-#-# it.po (desktop-icons master) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-11-29 09:07+0000\n"
+"PO-Revision-Date: 2018-12-05 11:14+0100\n"
+"Last-Translator: Massimo Branchini <max.bra.gtalk@gmail.com>\n"
+"Language-Team: Italian <tp@lists.linux.it>\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.2\n"
+"#-#-#-#-# it.po (desktop-icons master) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-11-29 09:07+0000\n"
+"PO-Revision-Date: 2018-12-05 11:14+0100\n"
+"Last-Translator: Massimo Branchini <max.bra.gtalk@gmail.com>\n"
+"Language-Team: Italian <tp@lists.linux.it>\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.2\n"
#: data/gnome-classic.desktop.in:3 data/gnome-classic.session.desktop.in:3
msgid "GNOME Classic"
@@ -362,3 +407,135 @@ msgstr "Nome"
#, javascript-format
msgid "Workspace %d"
msgstr "Spazio di lavoro %d"
+
+#: prefs.js:93
+msgid "Size for the desktop icons"
+msgstr "Dimensione delle icone della scrivania"
+
+#: prefs.js:93
+msgid "Small"
+msgstr "Piccola"
+
+#: prefs.js:93
+msgid "Standard"
+msgstr "Normale"
+
+#: prefs.js:93
+msgid "Large"
+msgstr "Grande"
+
+#: prefs.js:93
+msgid "Huge"
+msgstr "Enorme"
+
+#: prefs.js:94
+msgid "Show the personal folder in the desktop"
+msgstr "Mostra la cartella personale sulla scrivania"
+
+#: prefs.js:95
+msgid "Show the trash icon in the desktop"
+msgstr "Mostra il cestino sulla scrivania"
+
+#: desktopGrid.js:185 desktopGrid.js:304
+msgid "New Folder"
+msgstr "Nuova cartella"
+
+#: desktopGrid.js:306
+msgid "Paste"
+msgstr "Incolla"
+
+#: desktopGrid.js:307
+msgid "Undo"
+msgstr "Annulla"
+
+#: desktopGrid.js:308
+msgid "Redo"
+msgstr "Ripeti"
+
+#: desktopGrid.js:310
+msgid "Open Desktop in Files"
+msgstr "Apri la scrivania in File"
+
+#: desktopGrid.js:311
+msgid "Open Terminal"
+msgstr "Apri un terminale"
+
+#: desktopGrid.js:313
+msgid "Change Background…"
+msgstr "Cambia lo sfondo…"
+
+#: desktopGrid.js:314
+msgid "Display Settings"
+msgstr "Impostazioni dello schermo"
+
+#: desktopGrid.js:315
+msgid "Settings"
+msgstr "Impostazioni"
+
+#: desktopGrid.js:569
+msgid "Enter file name…"
+msgstr "Indicare un nome per il file…"
+
+#: desktopGrid.js:573
+msgid "Ok"
+msgstr "Ok"
+
+#: desktopGrid.js:579
+msgid "Cancel"
+msgstr "Annulla"
+
+#: fileItem.js:393
+msgid "Open"
+msgstr "Apri"
+
+#: fileItem.js:396
+msgid "Cut"
+msgstr "Taglia"
+
+#: fileItem.js:397
+msgid "Copy"
+msgstr "Copia"
+
+#: fileItem.js:398
+msgid "Rename"
+msgstr "Rinomina"
+
+#: fileItem.js:399
+msgid "Move to Trash"
+msgstr "Sposta nel cestino"
+
+#: fileItem.js:403
+msgid "Empty Trash"
+msgstr "Svuota il cestino"
+
+#: fileItem.js:409
+msgid "Properties"
+msgstr "Proprietà"
+
+#: fileItem.js:411
+msgid "Show in Files"
+msgstr "Mostra in File"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Icon size"
+msgstr "Dimensione dell'icona"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:13
+msgid "Set the size for the desktop icons."
+msgstr "Imposta la grandezza delle icone della scrivania."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show personal folder"
+msgstr "Mostra la cartella personale"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:18
+msgid "Show the personal folder in the desktop."
+msgstr "Mostra la cartella personale sulla scrivania."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show trash icon"
+msgstr "Mostra il cestino"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:23
+msgid "Show the trash icon in the desktop."
+msgstr "Mostra il cestino sulla scrivania."
diff --git a/po/pl.po b/po/pl.po
index 35799ee..94dd40b 100644
--- a/po/pl.po
+++ b/po/pl.po
@@ -1,11 +1,30 @@
+# #-#-#-#-# pl.po (gnome-shell-extensions) #-#-#-#-#
+# #-#-#-#-# pl.po (gnome-shell-extensions) #-#-#-#-#
# Polish translation for gnome-shell-extensions.
# Copyright © 2011-2017 the gnome-shell-extensions authors.
# This file is distributed under the same license as the gnome-shell-extensions package.
# Piotr Drąg <piotrdrag@gmail.com>, 2011-2017.
# Aviary.pl <community-poland@mozilla.org>, 2011-2017.
#
+# #-#-#-#-# pl.po (desktop-icons) #-#-#-#-#
+# Polish translation for desktop-icons.
+# Copyright © 2018-2019 the desktop-icons authors.
+# This file is distributed under the same license as the desktop-icons package.
+# Piotr Drąg <piotrdrag@gmail.com>, 2018-2019.
+# Aviary.pl <community-poland@mozilla.org>, 2018-2019.
+#
+# #-#-#-#-# pl.po (desktop-icons) #-#-#-#-#
+# Polish translation for desktop-icons.
+# Copyright © 2018-2019 the desktop-icons authors.
+# This file is distributed under the same license as the desktop-icons package.
+# Piotr Drąg <piotrdrag@gmail.com>, 2018-2019.
+# Aviary.pl <community-poland@mozilla.org>, 2018-2019.
+#
+#, fuzzy
msgid ""
msgstr ""
+"#-#-#-#-# pl.po (gnome-shell-extensions) #-#-#-#-#\n"
+"#-#-#-#-# pl.po (gnome-shell-extensions) #-#-#-#-#\n"
"Project-Id-Version: gnome-shell-extensions\n"
"Report-Msgid-Bugs-To: https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=extensions\n"
@@ -19,6 +38,34 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2);\n"
+"#-#-#-#-# pl.po (desktop-icons) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2019-01-15 10:59+0000\n"
+"PO-Revision-Date: 2019-01-15 20:57+0100\n"
+"Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n"
+"Language-Team: Polish <community-poland@mozilla.org>\n"
+"Language: pl\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==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
+"|| n%100>=20) ? 1 : 2);\n"
+"#-#-#-#-# pl.po (desktop-icons) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2019-01-15 10:59+0000\n"
+"PO-Revision-Date: 2019-01-15 20:57+0100\n"
+"Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n"
+"Language-Team: Polish <community-poland@mozilla.org>\n"
+"Language: pl\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==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
+"|| n%100>=20) ? 1 : 2);\n"
#: data/gnome-classic.desktop.in:3 data/gnome-classic.session.desktop.in:3
msgid "GNOME Classic"
@@ -358,3 +405,139 @@ msgstr "Nazwa"
#, javascript-format
msgid "Workspace %d"
msgstr "%d. obszar roboczy"
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Rozmiar ikon na pulpicie"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Mały"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Standardowy"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Duży"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Katalog domowy na pulpicie"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Kosz na pulpicie"
+
+#: desktopGrid.js:187 desktopGrid.js:306
+msgid "New Folder"
+msgstr "Nowy katalog"
+
+#: desktopGrid.js:308
+msgid "Paste"
+msgstr "Wklej"
+
+#: desktopGrid.js:309
+msgid "Undo"
+msgstr "Cofnij"
+
+#: desktopGrid.js:310
+msgid "Redo"
+msgstr "Ponów"
+
+#: desktopGrid.js:312
+msgid "Show Desktop in Files"
+msgstr "Wyświetl pulpit w menedżerze plików"
+
+#: desktopGrid.js:313 fileItem.js:586
+msgid "Open in Terminal"
+msgstr "Otwórz w terminalu"
+
+#: desktopGrid.js:315
+msgid "Change Background…"
+msgstr "Zmień tło…"
+
+#: desktopGrid.js:317
+msgid "Display Settings"
+msgstr "Ustawienia ekranu"
+
+#: desktopGrid.js:318
+msgid "Settings"
+msgstr "Ustawienia"
+
+#: desktopGrid.js:559
+msgid "Enter file name…"
+msgstr "Nazwa pliku…"
+
+#: desktopGrid.js:563
+msgid "OK"
+msgstr "OK"
+
+#: desktopGrid.js:569
+msgid "Cancel"
+msgstr "Anuluj"
+
+#: fileItem.js:490
+msgid "Don’t Allow Launching"
+msgstr "Nie zezwalaj na uruchamianie"
+
+#: fileItem.js:492
+msgid "Allow Launching"
+msgstr "Zezwól na uruchamianie"
+
+#: fileItem.js:559
+msgid "Open"
+msgstr "Otwórz"
+
+#: fileItem.js:562
+msgid "Cut"
+msgstr "Wytnij"
+
+#: fileItem.js:563
+msgid "Copy"
+msgstr "Skopiuj"
+
+#: fileItem.js:565
+msgid "Rename…"
+msgstr "Zmień nazwę…"
+
+#: fileItem.js:566
+msgid "Move to Trash"
+msgstr "Przenieś do kosza"
+
+#: fileItem.js:576
+msgid "Empty Trash"
+msgstr "Opróżnij kosz"
+
+#: fileItem.js:582
+msgid "Properties"
+msgstr "Właściwości"
+
+#: fileItem.js:584
+msgid "Show in Files"
+msgstr "Wyświetl w menedżerze plików"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:11
+msgid "Icon size"
+msgstr "Rozmiar ikon"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Set the size for the desktop icons."
+msgstr "Ustawia rozmiar ikon na pulpicie."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:16
+msgid "Show personal folder"
+msgstr "Katalog domowy"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show the personal folder in the desktop."
+msgstr "Wyświetla katalog domowy na pulpicie."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:21
+msgid "Show trash icon"
+msgstr "Kosz"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show the trash icon in the desktop."
+msgstr "Wyświetla kosz na pulpicie."
diff --git a/po/pt_BR.po b/po/pt_BR.po
index d029648..77f7a0d 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -1,3 +1,5 @@
+# #-#-#-#-# pt_BR.po (gnome-shell-extensions master) #-#-#-#-#
+# #-#-#-#-# pt_BR.po (gnome-shell-extensions master) #-#-#-#-#
# Brazilian Portuguese translation for gnome-shell-extensions.
# Copyright (C) 2017 gnome-shell-extensions's COPYRIGHT HOLDER
# This file is distributed under the same license as the gnome-shell-extensions package.
@@ -9,8 +11,23 @@
# Og Maciel <ogmaciel@gnome.org>, 2012.
# Enrico Nicoletto <liverig@gmail.com>, 2013, 2014.
# Rafael Fontenelle <rafaelff@gnome.org>, 2013, 2017.
+# #-#-#-#-# pt_BR.po (desktop-icons master) #-#-#-#-#
+# Brazilian Portuguese translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Enrico Nicoletto <liverig@gmail.com>, 2018.
+# Rafael Fontenelle <rafaelff@gnome.org>, 2018.
+# #-#-#-#-# pt_BR.po (desktop-icons master) #-#-#-#-#
+# Brazilian Portuguese translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Enrico Nicoletto <liverig@gmail.com>, 2018.
+# Rafael Fontenelle <rafaelff@gnome.org>, 2018.
+#, fuzzy
msgid ""
msgstr ""
+"#-#-#-#-# pt_BR.po (gnome-shell-extensions master) #-#-#-#-#\n"
+"#-#-#-#-# pt_BR.po (gnome-shell-extensions master) #-#-#-#-#\n"
"Project-Id-Version: gnome-shell-extensions master\n"
"Report-Msgid-Bugs-To: https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=extensions\n"
@@ -25,6 +42,34 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Virtaal 1.0.0-beta1\n"
"X-Project-Style: gnome\n"
+"#-#-#-#-# pt_BR.po (desktop-icons master) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-12-14 09:12+0000\n"
+"PO-Revision-Date: 2018-12-17 01:01-0200\n"
+"Last-Translator: Rafael Fontenelle <rafaelff@gnome.org>\n"
+"Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n"
+"Language: pt_BR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Virtaal 1.0.0-beta1\n"
+"#-#-#-#-# pt_BR.po (desktop-icons master) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-12-14 09:12+0000\n"
+"PO-Revision-Date: 2018-12-17 01:01-0200\n"
+"Last-Translator: Rafael Fontenelle <rafaelff@gnome.org>\n"
+"Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n"
+"Language: pt_BR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Virtaal 1.0.0-beta1\n"
#: data/gnome-classic.desktop.in:3 data/gnome-classic.session.desktop.in:3
msgid "GNOME Classic"
@@ -366,6 +411,146 @@ msgstr "Nome"
msgid "Workspace %d"
msgstr "Espaço de trabalho %d"
+#: desktopGrid.js:311
+msgid "Display Settings"
+msgstr "Configurações de exibição"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Icon size"
+msgstr "Tamanho do ícone"
+
+#: desktopGrid.js:578
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: prefs.js:102
+msgid "Size for the desktop icons"
+msgstr "Tamanho para os ícones da área de trabalho"
+
+#: prefs.js:102
+msgid "Small"
+msgstr "Pequeno"
+
+#: prefs.js:102
+msgid "Standard"
+msgstr "Padrão"
+
+#: prefs.js:102
+msgid "Large"
+msgstr "Grande"
+
+#: prefs.js:102
+msgid "Huge"
+msgstr "Enorme"
+
+#: prefs.js:103
+msgid "Show the personal folder in the desktop"
+msgstr "Mostrar a pasta pessoal na área de trabalho"
+
+#: prefs.js:104
+msgid "Show the trash icon in the desktop"
+msgstr "Mostrar o ícone da lixeira na área de trabalho"
+
+#: desktopGrid.js:182 desktopGrid.js:301
+msgid "New Folder"
+msgstr "Nova pasta"
+
+#: desktopGrid.js:303
+msgid "Paste"
+msgstr "Colar"
+
+#: desktopGrid.js:304
+msgid "Undo"
+msgstr "Desfazer"
+
+#: desktopGrid.js:305
+msgid "Redo"
+msgstr "Refazer"
+
+#: desktopGrid.js:307
+msgid "Open Desktop in Files"
+msgstr "Abrir área de trabalho no Arquivos"
+
+#: desktopGrid.js:308
+msgid "Open Terminal"
+msgstr "Abrir terminal"
+
+#: desktopGrid.js:310
+msgid "Change Background…"
+msgstr "Alterar plano de fundo…"
+
+#: desktopGrid.js:312
+msgid "Settings"
+msgstr "Configurações"
+
+#: desktopGrid.js:568
+msgid "Enter file name…"
+msgstr "Insira um nome de arquivo…"
+
+#: desktopGrid.js:572
+msgid "OK"
+msgstr "OK"
+
+#: fileItem.js:485
+msgid "Don’t Allow Launching"
+msgstr "Não permitir iniciar"
+
+#: fileItem.js:487
+msgid "Allow Launching"
+msgstr "Permitir iniciar"
+
+#: fileItem.js:550
+msgid "Open"
+msgstr "Abrir"
+
+#: fileItem.js:553
+msgid "Cut"
+msgstr "Recortar"
+
+#: fileItem.js:554
+msgid "Copy"
+msgstr "Copiar"
+
+#: fileItem.js:556
+msgid "Rename"
+msgstr "Renomear"
+
+#: fileItem.js:557
+msgid "Move to Trash"
+msgstr "Mover para a lixeira"
+
+#: fileItem.js:567
+msgid "Empty Trash"
+msgstr "Esvaziar lixeira"
+
+#: fileItem.js:573
+msgid "Properties"
+msgstr "Propriedades"
+
+#: fileItem.js:575
+msgid "Show in Files"
+msgstr "Mostrar no Arquivos"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:13
+msgid "Set the size for the desktop icons."
+msgstr "Define o tamanho para os ícones da área de trabalho."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show personal folder"
+msgstr "Mostrar pasta pessoal"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:18
+msgid "Show the personal folder in the desktop."
+msgstr "Mostra a pasta pessoal na área de trabalho."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show trash icon"
+msgstr "Mostrar ícone da lixeira"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:23
+msgid "Show the trash icon in the desktop."
+msgstr "Mostra o ícone da lixeira na área de trabalho."
+
#~ msgid "CPU"
#~ msgstr "CPU"
@@ -414,9 +599,6 @@ msgstr "Espaço de trabalho %d"
#~ msgid "Display"
#~ msgstr "Tela"
-#~ msgid "Display Settings"
-#~ msgstr "Configurações de tela"
-
#~ msgid "The application icon mode."
#~ msgstr "O modo de ícone do aplicativo."
@@ -451,9 +633,6 @@ msgstr "Espaço de trabalho %d"
#~ "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."
@@ -613,9 +792,6 @@ msgstr "Espaço de trabalho %d"
#~ 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."
@@ -639,3 +815,6 @@ msgstr "Espaço de trabalho %d"
#~ msgid "Log Out..."
#~ msgstr "Encerrar sessão..."
+
+#~ msgid "Ok"
+#~ msgstr "Ok"
diff --git a/po/ru.po b/po/ru.po
index c18c0ba..5e48a26 100644
--- a/po/ru.po
+++ b/po/ru.po
@@ -1,11 +1,28 @@
+# #-#-#-#-# ru.po (gnome-shell-extensions gnome-3-0) #-#-#-#-#
+# #-#-#-#-# ru.po (gnome-shell-extensions gnome-3-0) #-#-#-#-#
# Russian 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.
# Yuri Myasoedov <omerta13@yandex.ru>, 2011, 2012, 2013.
# Stas Solovey <whats_up@tut.by>, 2011, 2012, 2013, 2015, 2017.
#
+# #-#-#-#-# ru.po #-#-#-#-#
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Eaglers <eaglersdeveloper@gmail.com>, 2018.
+#
+# #-#-#-#-# ru.po #-#-#-#-#
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Eaglers <eaglersdeveloper@gmail.com>, 2018.
+#
+#, fuzzy
msgid ""
msgstr ""
+"#-#-#-#-# ru.po (gnome-shell-extensions gnome-3-0) #-#-#-#-#\n"
+"#-#-#-#-# ru.po (gnome-shell-extensions gnome-3-0) #-#-#-#-#\n"
"Project-Id-Version: gnome-shell-extensions gnome-3-0\n"
"Report-Msgid-Bugs-To: https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=extensions\n"
@@ -20,6 +37,36 @@ msgstr ""
"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 2.0.3\n"
+"#-#-#-#-# ru.po #-#-#-#-#\n"
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-11-22 08:42+0000\n"
+"PO-Revision-Date: 2018-11-22 22:02+0300\n"
+"Last-Translator: Stas Solovey <whats_up@tut.by>\n"
+"Language-Team: Russian <gnome-cyr@gnome.org>\n"
+"Language: ru\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 2.2\n"
+"#-#-#-#-# ru.po #-#-#-#-#\n"
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-11-22 08:42+0000\n"
+"PO-Revision-Date: 2018-11-22 22:02+0300\n"
+"Last-Translator: Stas Solovey <whats_up@tut.by>\n"
+"Language-Team: Russian <gnome-cyr@gnome.org>\n"
+"Language: ru\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 2.2\n"
#: data/gnome-classic.desktop.in:3 data/gnome-classic.session.desktop.in:3
msgid "GNOME Classic"
@@ -360,6 +407,138 @@ msgstr "Название"
msgid "Workspace %d"
msgstr "Рабочая область %d"
+#: prefs.js:89
+msgid "Size for the desktop icons"
+msgstr "Размер значков"
+
+#: prefs.js:89
+msgid "Small"
+msgstr "Маленький"
+
+#: prefs.js:89
+msgid "Standard"
+msgstr "Стандартный"
+
+#: prefs.js:89
+msgid "Large"
+msgstr "Большой"
+
+#: prefs.js:89
+msgid "Huge"
+msgstr "Огромный"
+
+#: prefs.js:90
+msgid "Show the personal folder in the desktop"
+msgstr "Показывать домашнюю папку на рабочем столе"
+
+#: prefs.js:91
+msgid "Show the trash icon in the desktop"
+msgstr "Показывать «Корзину» на рабочем столе"
+
+#: desktopGrid.js:185 desktopGrid.js:304
+msgid "New Folder"
+msgstr "Создать папку"
+
+#: desktopGrid.js:306
+msgid "Paste"
+msgstr "Вставить"
+
+#: desktopGrid.js:307
+msgid "Undo"
+msgstr "Отменить"
+
+#: desktopGrid.js:308
+msgid "Redo"
+msgstr "Повторить"
+
+#: desktopGrid.js:310
+msgid "Open Desktop in Files"
+msgstr "Открыть «Рабочий стол» в «Файлах»"
+
+#: desktopGrid.js:311
+msgid "Open Terminal"
+msgstr "Открыть терминал"
+
+#: desktopGrid.js:313
+msgid "Change Background…"
+msgstr "Изменить фон…"
+
+#: desktopGrid.js:314
+msgid "Display Settings"
+msgstr "Настройки дисплея"
+
+#: desktopGrid.js:315
+msgid "Settings"
+msgstr "Параметры"
+
+#: desktopGrid.js:569
+msgid "Enter file name…"
+msgstr "Ввести имя файла…"
+
+#: desktopGrid.js:573
+msgid "Ok"
+msgstr "ОК"
+
+#: desktopGrid.js:579
+msgid "Cancel"
+msgstr "Отмена"
+
+#: fileItem.js:390
+msgid "Open"
+msgstr "Открыть"
+
+#: fileItem.js:393
+msgid "Cut"
+msgstr "Вырезать"
+
+#: fileItem.js:394
+msgid "Copy"
+msgstr "Вставить"
+
+#: fileItem.js:395
+msgid "Rename"
+msgstr "Переименовать"
+
+#: fileItem.js:396
+msgid "Move to Trash"
+msgstr "Переместить в корзину"
+
+#: fileItem.js:400
+msgid "Empty trash"
+msgstr "Очистить корзину"
+
+#: fileItem.js:406
+msgid "Properties"
+msgstr "Свойства"
+
+#: fileItem.js:408
+msgid "Show in Files"
+msgstr "Показать в «Файлах»"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Icon size"
+msgstr "Размер значков"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:13
+msgid "Set the size for the desktop icons."
+msgstr "Установить размер значков на рабочем столе."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show personal folder"
+msgstr "Показывать домашнюю папку"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:18
+msgid "Show the personal folder in the desktop."
+msgstr "Показывать значок домашней папки на рабочем столе."
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show trash icon"
+msgstr "Показывать значок корзины"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:23
+msgid "Show the trash icon in the desktop."
+msgstr "Показывать значок корзины на рабочем столе."
+
#~ msgid "CPU"
#~ msgstr "ЦП"
diff --git a/po/zh_TW.po b/po/zh_TW.po
index 74a95f8..8a675aa 100644
--- a/po/zh_TW.po
+++ b/po/zh_TW.po
@@ -1,10 +1,27 @@
+# #-#-#-#-# zh_TW.po (gnome-shell-extensions gnome-3-0) #-#-#-#-#
+# #-#-#-#-# zh_TW.po (gnome-shell-extensions gnome-3-0) #-#-#-#-#
# Chinese (Taiwan) 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.
# Cheng-Chia Tseng <pswo10680@gmail.com>, 2011.
#
+# #-#-#-#-# zh_TW.po (desktop-icons master) #-#-#-#-#
+# Chinese (Taiwan) translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Yi-Jyun Pan <pan93412@gmail.com>, 2018.
+#
+# #-#-#-#-# zh_TW.po (desktop-icons master) #-#-#-#-#
+# Chinese (Taiwan) translation for desktop-icons.
+# Copyright (C) 2018 desktop-icons's COPYRIGHT HOLDER
+# This file is distributed under the same license as the desktop-icons package.
+# Yi-Jyun Pan <pan93412@gmail.com>, 2018.
+#
+#, fuzzy
msgid ""
msgstr ""
+"#-#-#-#-# zh_TW.po (gnome-shell-extensions gnome-3-0) #-#-#-#-#\n"
+"#-#-#-#-# zh_TW.po (gnome-shell-extensions gnome-3-0) #-#-#-#-#\n"
"Project-Id-Version: gnome-shell-extensions gnome-3-0\n"
"Report-Msgid-Bugs-To: https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=extensions\n"
@@ -17,6 +34,32 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.0.3\n"
+"#-#-#-#-# zh_TW.po (desktop-icons master) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-10-22 14:12+0000\n"
+"PO-Revision-Date: 2018-10-24 21:31+0800\n"
+"Language-Team: Chinese (Taiwan) <chinese-l10n@googlegroups.com>\n"
+"Language: zh_TW\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Last-Translator: pan93412 <pan93412@gmail.com>\n"
+"X-Generator: Poedit 2.2\n"
+"#-#-#-#-# zh_TW.po (desktop-icons master) #-#-#-#-#\n"
+"Project-Id-Version: desktop-icons master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/ShellExtensions/desktop-"
+"icons/issues\n"
+"POT-Creation-Date: 2018-10-22 14:12+0000\n"
+"PO-Revision-Date: 2018-10-24 21:31+0800\n"
+"Language-Team: Chinese (Taiwan) <chinese-l10n@googlegroups.com>\n"
+"Language: zh_TW\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Last-Translator: pan93412 <pan93412@gmail.com>\n"
+"X-Generator: Poedit 2.2\n"
#: data/gnome-classic.desktop.in:3 data/gnome-classic.session.desktop.in:3
msgid "GNOME Classic"
@@ -344,6 +387,122 @@ msgstr "名稱"
msgid "Workspace %d"
msgstr "工作區 %d"
+#: desktopGrid.js:307
+msgid "Display Settings"
+msgstr "顯示設定"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:12
+msgid "Icon size"
+msgstr "圖示大小"
+
+#: prefs.js:89
+msgid "Size for the desktop icons"
+msgstr "桌面圖示的大小"
+
+#: prefs.js:89
+msgid "Small"
+msgstr "小圖示"
+
+#: prefs.js:89
+msgid "Standard"
+msgstr "標準大小圖示"
+
+#: prefs.js:89
+msgid "Large"
+msgstr "大圖示"
+
+#: prefs.js:89
+msgid "Huge"
+msgstr "巨大圖示"
+
+#: prefs.js:90
+msgid "Show the personal folder in the desktop"
+msgstr "在桌面顯示個人資料夾"
+
+#: prefs.js:91
+msgid "Show the trash icon in the desktop"
+msgstr "在桌面顯示垃圾桶圖示"
+
+#: desktopGrid.js:178 desktopGrid.js:297
+msgid "New Folder"
+msgstr "新增資料夾"
+
+#: desktopGrid.js:299
+msgid "Paste"
+msgstr "貼上"
+
+#: desktopGrid.js:300
+msgid "Undo"
+msgstr "復原"
+
+#: desktopGrid.js:301
+msgid "Redo"
+msgstr "重做"
+
+#: desktopGrid.js:303
+msgid "Open Desktop in Files"
+msgstr "在《檔案》中開啟桌面"
+
+#: desktopGrid.js:304
+msgid "Open Terminal"
+msgstr "開啟終端器"
+
+#: desktopGrid.js:306
+msgid "Change Background…"
+msgstr "變更背景圖片…"
+
+#: desktopGrid.js:308
+msgid "Settings"
+msgstr "設定"
+
+#: fileItem.js:223
+msgid "Open"
+msgstr "開啟"
+
+#: fileItem.js:226
+msgid "Cut"
+msgstr "剪下"
+
+#: fileItem.js:227
+msgid "Copy"
+msgstr "複製"
+
+#: fileItem.js:228
+msgid "Move to Trash"
+msgstr "移動到垃圾桶"
+
+#: fileItem.js:232
+msgid "Empty trash"
+msgstr "清空回收桶"
+
+#: fileItem.js:238
+msgid "Properties"
+msgstr "屬性"
+
+#: fileItem.js:240
+msgid "Show in Files"
+msgstr "在《檔案》中顯示"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:13
+msgid "Set the size for the desktop icons."
+msgstr "設定桌面圖示的大小。"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:17
+msgid "Show personal folder"
+msgstr "顯示個人資料夾"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:18
+msgid "Show the personal folder in the desktop."
+msgstr "在桌面顯示個人資料夾。"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:22
+msgid "Show trash icon"
+msgstr "顯示垃圾桶圖示"
+
+#: schemas/org.gnome.shell.extensions.desktop-icons.gschema.xml:23
+msgid "Show the trash icon in the desktop."
+msgstr "在桌面顯示垃圾桶圖示。"
+
#~ msgid "CPU"
#~ msgstr "CPU"
@@ -371,9 +530,6 @@ msgstr "工作區 %d"
#~ msgid "Display"
#~ msgstr "顯示"
-#~ msgid "Display Settings"
-#~ msgstr "顯示設定值"
-
#~ msgid "Suspend"
#~ msgstr "暫停"
@@ -481,9 +637,6 @@ msgstr "工作區 %d"
#~ msgid "Enable/disable autohide"
#~ msgstr "啟用/停用自動隱藏"
-#~ msgid "Icon size"
-#~ msgstr "圖示大小"
-
#~ msgid "Position of the dock"
#~ msgstr "Dock 的位置"
--
2.21.0