summarylogtreecommitdiffstats
path: root/xuzhen_fixes_pr142_thru_f772075.patch
diff options
context:
space:
mode:
authorTed Alff2020-10-21 21:14:48 -0400
committerTed Alff2020-10-21 21:14:48 -0400
commita05a502576e5a046a6289f5d7c82a1839fd76e6c (patch)
tree9216ed0bd2854c4d6f1fbe140dab03181e8ae0e9 /xuzhen_fixes_pr142_thru_f772075.patch
parent305a19647c9efe4ea1f30f8ce57d31b4906d0568 (diff)
downloadaur-a05a502576e5a046a6289f5d7c82a1839fd76e6c.tar.gz
Add PR142 from xuzhen waiting for it to be pulled.
Diffstat (limited to 'xuzhen_fixes_pr142_thru_f772075.patch')
-rw-r--r--xuzhen_fixes_pr142_thru_f772075.patch1911
1 files changed, 1911 insertions, 0 deletions
diff --git a/xuzhen_fixes_pr142_thru_f772075.patch b/xuzhen_fixes_pr142_thru_f772075.patch
new file mode 100644
index 00000000000..dd12f42978e
--- /dev/null
+++ b/xuzhen_fixes_pr142_thru_f772075.patch
@@ -0,0 +1,1911 @@
+From b8f102546e590034092e99b422d37b7f7bb9c5b0 Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sun, 9 Aug 2020 12:19:23 +0800
+Subject: [PATCH 01/25] fixed issue #140
+
+---
+ dockx | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/dockx b/dockx
+index bbc9535..a45a9a2 100755
+--- a/dockx
++++ b/dockx
+@@ -823,11 +823,11 @@ class DockX(CairoDockX):
+ XDisplay.get_atom('CARDINAL'), 32,
+ strut[:4],
+ X.PropModeReplace)
+-
+ topw.change_property(XDisplay.get_atom('_NET_WM_STRUT_PARTIAL'),
+ XDisplay.get_atom('CARDINAL'), 32,
+ strut,
+ X.PropModeReplace)
++ XDisplay.flush()
+
+ def __get_monitor_and_strut_borders(self):
+ # This function returns the distance from screen edges to
+
+From e16735bea70290a6bd4beafe448c8befa06502ee Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sun, 9 Aug 2020 18:17:38 +0800
+Subject: [PATCH 02/25] fixed X11 timestamp
+
+---
+ dockbarx/windowbutton.py | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/dockbarx/windowbutton.py b/dockbarx/windowbutton.py
+index 2554dc7..751a15a 100644
+--- a/dockbarx/windowbutton.py
++++ b/dockbarx/windowbutton.py
+@@ -21,6 +21,7 @@
+ gi.require_version("Gtk", "3.0")
+ from gi.repository import Gtk
+ from gi.repository import Gdk
++from gi.repository import GdkX11
+ from gi.repository import GdkPixbuf
+ from gi.repository import GLib
+ from gi.repository import Pango
+@@ -225,7 +226,7 @@ def action_select_or_minimize_window(self, widget=None,
+ if event:
+ t = event.time
+ else:
+- t = 0
++ t = GdkX11.x11_get_server_time(Gdk.get_default_root_window())
+ if self.wnck.get_workspace() is not None \
+ and self.screen.get_active_workspace() != self.wnck.get_workspace():
+ self.wnck.get_workspace().activate(t)
+
+From 2744ee6234ab3bf38875269eaeaaf22a27995e42 Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sat, 15 Aug 2020 13:23:56 +0800
+Subject: [PATCH 03/25] fixed locked popup behavior
+
+---
+ dockbarx/cairowidgets.py | 3 +-
+ dockbarx/dockbar.py | 8 ++-
+ dockbarx/groupbutton.py | 107 ++++++++++++++++++++-------------------
+ dockx | 11 ++--
+ 4 files changed, 71 insertions(+), 58 deletions(-)
+
+diff --git a/dockbarx/cairowidgets.py b/dockbarx/cairowidgets.py
+index 3263330..a4967af 100644
+--- a/dockbarx/cairowidgets.py
++++ b/dockbarx/cairowidgets.py
+@@ -556,7 +556,8 @@ def point(self, new_pointer, ap=0):
+
+ def on_draw(self, widget, ctx):
+ #~ self.set_shape_mask()
+- w,h = self.get_size()
++ a = self.get_allocation()
++ w, h = a.width, a.height
+ if self.is_composited():
+ ctx.set_source_rgba(1, 1, 1, 0)
+ else:
+diff --git a/dockbarx/dockbar.py b/dockbarx/dockbar.py
+index 39ffbb0..104476a 100644
+--- a/dockbarx/dockbar.py
++++ b/dockbarx/dockbar.py
+@@ -535,7 +535,7 @@ def load(self):
+
+ self.reload(tell_parent=False)
+
+- def reload(self, event=None, data=None, tell_parent=True):
++ def reload(self, event=None, data=None, tell_parent=True, locked_group=None):
+ """Reloads DockbarX."""
+ logger.info("DockbarX reload")
+ # Clear away the old stuff, if any.
+@@ -609,6 +609,12 @@ def reload(self, event=None, data=None, tell_parent=True):
+ # Initiate group buttons with windows
+ for window in self.screen.get_windows():
+ self.__on_window_opened(self.screen, window)
++ # restore locked popup
++ if locked_group is not None:
++ for group in self.groups:
++ if group.identifier == locked_group:
++ group.add_locked_popup()
++ break
+
+ self.screen.connect("window-opened", self.__on_window_opened)
+ self.screen.connect("window-closed", self.__on_window_closed)
+diff --git a/dockbarx/groupbutton.py b/dockbarx/groupbutton.py
+index 978d3a2..65249e4 100644
+--- a/dockbarx/groupbutton.py
++++ b/dockbarx/groupbutton.py
+@@ -345,11 +345,11 @@ def show_launch_popup(self):
+ def add_locked_popup(self):
+ if self.locked_popup:
+ return
++ self.popup.hide()
+ locked_popup = self.globals.get_locked_popup()
+ if locked_popup:
+ locked_popup.destroy()
+ self.locked_popup = LockedPopup(self)
+- self.popup.hide()
+ self.globals.set_locked_popup(self.locked_popup)
+ self.locked_popup.show()
+
+@@ -357,6 +357,7 @@ def remove_locked_popup(self):
+ if self.locked_popup:
+ self.locked_popup.destroy()
+ self.popup.hide()
++ self.locked_popup = None
+
+ #### Window handling
+ def add_window(self, wnck_window):
+@@ -2049,7 +2050,7 @@ def get_child_(self):
+ else:
+ return children[0]
+
+- def on_size_allocate(self, widget, allocation):
++ def on_size_allocate(self, widget, allocation, no_move=False):
+ if allocation == self.last_allocation:
+ return
+ group = self.group_r()
+@@ -2060,7 +2061,7 @@ def on_size_allocate(self, widget, allocation):
+ offset = int(self.popup_style.get("%s_distance" % self.popup_type, -7))
+ dummy, wx, wy = window.get_origin()
+ b_alloc = group.button.get_allocation()
+- width, height = self.get_size()
++ width, height = allocation.width, allocation.height
+
+ if Gtk.MAJOR_VERSION > 3 or Gtk.MINOR_VERSION >= 22:
+ mgeo = group.get_monitor().get_geometry();
+@@ -2119,7 +2120,8 @@ def on_size_allocate(self, widget, allocation):
+ x = x + b_alloc.width + offset
+ p = wy + b_alloc.y + (b_alloc.height // 2) - y
+ self.point(direction, p)
+- self.move(x, y)
++ if not no_move:
++ self.move(x, y)
+ try:
+ child_func = self.get_child_().on_popup_reallocate
+ except AttributeError:
+@@ -2286,15 +2288,18 @@ def __init__(self, group):
+ group.window_list.apply_mini_mode()
+ if not group.get_windows():
+ self.hide()
+- else:
+- # not work
+- # GLib.idle_add(self.__on_realized)
+- pass
++ self.set_type_hint(Gdk.WindowTypeHint.DOCK)
++ self.set_keep_above(True)
++ self.set_decorated(False)
++ self.set_accept_focus(False)
++ self.set_resizable(False)
+ self.overlap_sid = self.globals.connect("locked-list-overlap-changed", self.__set_own_strut)
+- self.connect("size-allocate", self.on_size_allocate)
++ self.size_allocate_sid = self.connect("size-allocate", self.on_size_allocate)
++ self.connect("realize", self.__on_realized)
+
+ def show(self):
+- CairoPopup.show(self)
++ CairoPopup.show_all(self)
++ self.on_size_allocate(self, self.get_allocation())
+
+ def hide(self):
+ CairoPopup.hide(self)
+@@ -2315,7 +2320,7 @@ def on_size_allocate(self, widget, allocation):
+ else:
+ mgeo = Gdk.Screen.get_default().get_monitor_geometry(group.get_monitor())
+
+- width, height = self.get_size()
++ width, height = allocation.width, allocation.height
+ if self.dockbar_r().orient in ("down", "up"):
+ button_window = group.button.get_window()
+ if button_window:
+@@ -2326,7 +2331,7 @@ def on_size_allocate(self, widget, allocation):
+ GroupPopup.on_size_allocate(self, widget, allocation)
+ self.__set_own_strut()
+ return
+- GroupPopup.on_size_allocate(self, widget, allocation)
++ GroupPopup.on_size_allocate(self, widget, allocation, no_move=True)
+ strut = self.__get_other_strut(mgeo.width // 2 - width // 2,
+ mgeo.width // 2 + width // 2)
+ self.move(mgeo.width // 2 - width // 2, mgeo.height - height - strut - 1)
+@@ -2340,44 +2345,40 @@ def on_size_allocate(self, widget, allocation):
+ child_func(self)
+
+ def __set_own_strut(self, *args):
+- # Todo: This doesn't work at all. Find out why and uncomment.
+- return
+- #~ global display
+- #~ global X
+- #~ if display is None:
+- #~ from Xlib import display
+- #~ win = self.get_window()
+- #~ if win:
+- #~ if self.globals.settings["locked_list_no_overlap"] is False:
+- #~ topw = XDisplay.create_resource_object('window',
+- #~ win.get_toplevel().get_xid())
+- #~ topw.delete_property(XDisplay.get_atom("_NET_WM_STRUT"))
+- #~ topw.delete_property(XDisplay.get_atom("_NET_WM_STRUT_PARTIAL"))
+- #~ return
+- #~ if X is None:
+- #~ from Xlib import X
+- #~ group = self.group_r()
+- #~ a = self.get_allocation()
+- #~ x, y = self.get_position()
+- #~ if Gtk.MAJOR_VERSION > 3 or Gtk.MINOR_VERSION >= 22:
+- #~ mgeo = group.get_monitor().get_geometry();
+- #~ else:
+- #~ mgeo = Gdk.Screen.get_default().get_monitor_geometry(group.get_monitor())
+- #~ height = mgeo.y + mgeo.height - y
+- #~ x1 = mgeo.x + x
+- #~ x2 = mgeo.x + x + a.width
+- #~ strut = [0, 0, 0, height, 0, 0, 0, 0, 0, 0, x1, x2]
+- #~ topw = XDisplay.create_resource_object('window',
+- #~ win.get_toplevel().get_xid())
+- #~ topw.change_property(XDisplay.get_atom('_NET_WM_STRUT'),
+- #~ XDisplay.get_atom('CARDINAL'), 32,
+- #~ strut[:4],
+- #~ X.PropModeReplace)
+-
+- #~ topw.change_property(XDisplay.get_atom('_NET_WM_STRUT_PARTIAL'),
+- #~ XDisplay.get_atom('CARDINAL'), 32,
+- #~ strut,
+- #~ X.PropModeReplace)
++ win = self.get_window()
++ if not win:
++ return
++ topw = XDisplay.create_resource_object('window',
++ win.get_toplevel().get_xid())
++ if self.globals.settings["locked_list_no_overlap"] is False:
++ topw = XDisplay.create_resource_object('window',
++ win.get_toplevel().get_xid())
++ topw.delete_property(XDisplay.get_atom("_NET_WM_STRUT"))
++ topw.delete_property(XDisplay.get_atom("_NET_WM_STRUT_PARTIAL"))
++ return
++ global X
++ if X is None:
++ from Xlib import X
++ group = self.group_r()
++ a = self.get_allocation()
++ x, y = self.get_position()
++ if Gtk.MAJOR_VERSION > 3 or Gtk.MINOR_VERSION >= 22:
++ mgeo = group.get_monitor().get_geometry();
++ else:
++ mgeo = Gdk.Screen.get_default().get_monitor_geometry(group.get_monitor())
++ height = mgeo.y + mgeo.height - y
++ x1 = max(mgeo.x + x, 0)
++ x2 = max(mgeo.x + x + a.width, 0)
++ strut = [0, 0, 0, height, 0, 0, 0, 0, 0, 0, x1, x2]
++ topw.change_property(XDisplay.get_atom('_NET_WM_STRUT'),
++ XDisplay.get_atom('CARDINAL'), 32,
++ strut[:4],
++ X.PropModeReplace)
++ topw.change_property(XDisplay.get_atom('_NET_WM_STRUT_PARTIAL'),
++ XDisplay.get_atom('CARDINAL'), 32,
++ strut,
++ X.PropModeReplace)
++ XDisplay.flush()
+
+ def __get_other_strut(self, x1, x2):
+ # if Gtk.MAJOR_VERSION > 3 or Gtk.MINOR_VERSION >= 22:
+@@ -2410,15 +2411,15 @@ def __get_other_strut(self, x1, x2):
+ strut = max(strut, prop2.value[3])
+ return strut
+
+- def __on_realized(self):
+- while not self.get_window():
+- Gtk.main_iteration()
++ def __on_realized(self, widget):
++ self.get_window().set_override_redirect(False)
+ self.__set_own_strut()
+
+ def destroy(self):
+ group = self.group_r()
+ group.locked_popup = None
+ self.globals.disconnect(self.overlap_sid)
++ self.disconnect(self.size_allocate_sid)
+ self.childbox.remove(group.window_list)
+ group.popup.set_child_(group.window_list)
+ group.window_list.apply_normal_mode()
+diff --git a/dockx b/dockx
+index a45a9a2..9c6f821 100755
+--- a/dockx
++++ b/dockx
+@@ -1127,7 +1127,7 @@ class DockX(CairoDockX):
+ orient = {"top": "up", "left": "left",
+ "right": "right", "bottom": "down"}[pos]
+ self.dockbar.set_orient(orient)
+- self.reload()
++ self.reload(keep_locked_popup = True)
+
+ def __on_overlap_changed(self, *args):
+ a = self.get_allocation()
+@@ -1290,9 +1290,14 @@ class DockX(CairoDockX):
+ #self.applets.find_applets()
+ self.__rebuild()
+
+- def reload(self, *args):
++ def reload(self, keep_locked_popup=False, *args):
+ self.db_loaded = False
+- self.dockbar.reload(tell_parent=False)
++ if keep_locked_popup:
++ locked_popup = self.globals.get_locked_popup()
++ locked_group_id = locked_popup.group_r().identifier
++ else:
++ locked_group_id = None
++ self.dockbar.reload(tell_parent=False, locked_group=locked_group_id)
+ self.db_loaded = True
+ self.applets.find_applets()
+ self.theme.reload()
+
+From 8d5bf59e35381f8350b09443324f577402e4f858 Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sat, 15 Aug 2020 18:41:52 +0800
+Subject: [PATCH 04/25] fixed issue #143
+
+---
+ dockbarx/cairowidgets.py | 2 +-
+ dockx | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/dockbarx/cairowidgets.py b/dockbarx/cairowidgets.py
+index a4967af..ad39c39 100644
+--- a/dockbarx/cairowidgets.py
++++ b/dockbarx/cairowidgets.py
+@@ -487,7 +487,7 @@ def __init__(self, orient="down", no_arrow=False, type_="popup"):
+ gdk_screen = Gdk.Screen.get_default()
+ visual = gdk_screen.get_rgba_visual()
+ if visual is None:
+- visual = gdk_screen.get_rgb_visual()
++ visual = gdk_screen.get_system_visual()
+ self.set_visual(visual)
+ self.set_app_paintable(1)
+ self.globals = Globals()
+diff --git a/dockx b/dockx
+index 9c6f821..0c1d811 100755
+--- a/dockx
++++ b/dockx
+@@ -64,7 +64,7 @@ class CairoDockX(Gtk.Window):
+ gdk_screen = Gdk.Screen.get_default()
+ visual = gdk_screen.get_rgba_visual()
+ if visual is None:
+- visual = gdk_screen.get_rgb_visual()
++ visual = gdk_screen.get_system_visual()
+ self.set_visual(visual)
+ self.set_app_paintable(1)
+ self.compute_padding()
+
+From 57e59bd53954d76eb936b1e9acb49f058b96954d Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sat, 15 Aug 2020 19:20:30 +0800
+Subject: [PATCH 05/25] added existence checking
+
+---
+ dockx | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/dockx b/dockx
+index 0c1d811..af986a3 100755
+--- a/dockx
++++ b/dockx
+@@ -1294,7 +1294,10 @@ class DockX(CairoDockX):
+ self.db_loaded = False
+ if keep_locked_popup:
+ locked_popup = self.globals.get_locked_popup()
+- locked_group_id = locked_popup.group_r().identifier
++ if locked_popup:
++ locked_group_id = locked_popup.group_r().identifier
++ else:
++ locked_group_id = None
+ else:
+ locked_group_id = None
+ self.dockbar.reload(tell_parent=False, locked_group=locked_group_id)
+
+From 0e2712a8469552d91fdda9a0445599703f9543d9 Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sat, 15 Aug 2020 19:50:58 +0800
+Subject: [PATCH 06/25] updated README
+
+---
+ README.md | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/README.md b/README.md
+index fde8edd..d428506 100644
+--- a/README.md
++++ b/README.md
+@@ -6,8 +6,8 @@ The gtk3/python3 version of DockbarX is a lightweight taskbar / panel replacemen
+
+ DockbarX is free software and is licensed under GPL3.
+
+-## Install in Ubuntu 20.04+ from ppa
+-The main DockbarX ppa is not maintained for the moment. You can use Xu Zhen's unofficial DockbarX ppa instead. To add the ppa and install the application in Ubuntu (and derivatives), use the following commands:
++## Install in Ubuntu 18.04+ from PPA
++The main DockbarX PPA is not maintained for the moment. You can use Xu Zhen's unofficial DockbarX PPA instead. To add the PPA and install the application in Ubuntu (and derivatives), use the following commands:
+
+ ```
+ sudo add-apt-repository ppa:xuzhen666/dockbarx
+
+From 2fb1f4c618c6536f8b8028ae776a99bf79778222 Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sun, 23 Aug 2020 13:47:11 +0800
+Subject: [PATCH 07/25] fixed missing argument
+
+---
+ dockbarx/groupbutton.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/dockbarx/groupbutton.py b/dockbarx/groupbutton.py
+index 65249e4..ed05d60 100644
+--- a/dockbarx/groupbutton.py
++++ b/dockbarx/groupbutton.py
+@@ -783,7 +783,7 @@ def __menu_position(self, menu):
+ x -= w - a.width
+ return (x, y, False)
+
+- def __menu_closed(self, menushell):
++ def __menu_closed(self, menushell=None):
+ # Used only with the gtk menu
+ self.globals.gtkmenu = None
+ self.menu.delete_menu()
+
+From 0308d1bb368c4236ceebf82f2bc54b7a07a6d5a4 Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sun, 23 Aug 2020 20:10:29 +0800
+Subject: [PATCH 08/25] a fallback desktop file editor. fixed issue #146
+
+---
+ dockbarx/desktopfileeditor.py | 251 ++++++++++++++++++++++++++++++++++
+ dockbarx/dockbar.py | 20 ++-
+ dockbarx/groupbutton.py | 3 +
+ 3 files changed, 268 insertions(+), 6 deletions(-)
+ create mode 100644 dockbarx/desktopfileeditor.py
+
+diff --git a/dockbarx/desktopfileeditor.py b/dockbarx/desktopfileeditor.py
+new file mode 100644
+index 0000000..1f07531
+--- /dev/null
++++ b/dockbarx/desktopfileeditor.py
+@@ -0,0 +1,251 @@
++#!/usr/bin/python3
++
++# Desktop File Editor for Dockbarx
++#
++# Copyright 2020 Xu Zhen
++#
++# DockbarX 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.
++#
++# DockbarX 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 dockbar. If not, see <http://www.gnu.org/licenses/>
++
++import configparser
++import os
++import gi
++gi.require_version("Gtk", "3.0")
++from gi.repository import Gtk
++from gi.repository import GdkPixbuf
++from .log import logger
++from . import i18n
++_ = i18n.language.gettext
++
++
++class DesktopFileEditor(Gtk.Dialog):
++ def __init__(self):
++ Gtk.Dialog.__init__(self, _("Edit Launcher"))
++ self.config = configparser.ConfigParser(delimiters=('='))
++ self.config.optionxform = str
++ self.langs = os.environ.get("LANGUAGE")
++ self.keymap = {}
++ self.icon = None
++ self.file = None
++ if self.langs is not None:
++ self.langs = self.langs.split(":")
++ else:
++ self.langs = []
++
++ grid = Gtk.Grid()
++ grid.set_row_spacing(5)
++ grid.set_column_spacing(5)
++ grid.set_margin_start(10)
++ grid.set_margin_end(10)
++ row = 0
++ label = Gtk.Label.new(_("Name:"))
++ label.set_halign(Gtk.Align.END)
++ self.name_entry = Gtk.Entry()
++ self.name_entry.set_hexpand(True)
++ grid.attach(label, 0, row, 1, 1)
++ grid.attach(self.name_entry, 1, row, 1, 1)
++ row += 1
++ label = Gtk.Label.new(_("Comment:"))
++ label.set_halign(Gtk.Align.END)
++ self.comment_entry = Gtk.Entry()
++ self.comment_entry.set_hexpand(True)
++ grid.attach(label, 0, row, 1, 1)
++ grid.attach(self.comment_entry, 1, row, 1, 1)
++ row += 1
++ label = Gtk.Label.new(_("Command:"))
++ label.set_halign(Gtk.Align.END)
++ self.command_entry = Gtk.Entry()
++ self.command_entry.set_hexpand(True)
++ self.command_entry.set_margin_end(5)
++ self.command_button = Gtk.Button()
++ image = Gtk.Image.new_from_icon_name("document-open", Gtk.IconSize.SMALL_TOOLBAR)
++ self.command_button.set_image(image)
++ self.command_button.set_hexpand(True)
++ box = Gtk.HBox()
++ box.pack_start(self.command_entry, True, True, 0)
++ box.pack_start(self.command_button, False, True, 0)
++ grid.attach(label, 0, row, 1, 1)
++ grid.attach(box, 1, row, 1, 1)
++ row += 1
++ label = Gtk.Label.new(_("Working Directory:"))
++ label.set_halign(Gtk.Align.END)
++ self.workdir_entry = Gtk.Entry()
++ self.workdir_entry.set_hexpand(True)
++ self.workdir_entry.set_margin_end(5)
++ self.workdir_button = Gtk.Button()
++ image = Gtk.Image.new_from_icon_name("folder", Gtk.IconSize.SMALL_TOOLBAR)
++ self.workdir_button.set_image(image)
++ box = Gtk.HBox()
++ box.pack_start(self.workdir_entry, True, True, 0)
++ box.pack_start(self.workdir_button, False, True, 0)
++ grid.attach(label, 0, row, 1, 1)
++ grid.attach(box, 1, row, 1, 1)
++ row += 1
++ label = Gtk.Label.new(_("Icon:"))
++ label.set_halign(Gtk.Align.END)
++ self.icon_button = Gtk.Button()
++ self.icon_button.set_size_request(64, 64)
++ box = Gtk.HBox()
++ box.pack_start(self.icon_button, False, True, 0)
++ grid.attach(label, 0, row, 1, 1)
++ grid.attach(box, 1, row, 1, 1)
++ row += 1
++ label = Gtk.Label.new(_("Options:"))
++ label.set_halign(Gtk.Align.END)
++ self.notification_check = Gtk.CheckButton.new_with_label(_("Use startup notification"))
++ grid.attach(label, 0, row, 1, 1)
++ grid.attach(self.notification_check, 1, row, 1, 1)
++ row += 1
++ self.terminal_check = Gtk.CheckButton.new_with_label(_("Run in terminal"))
++ grid.attach(self.terminal_check, 1, row, 1, 1)
++
++ self.add_button(_("Cancel"), Gtk.ResponseType.CANCEL)
++ self.add_button(_("Save"), Gtk.ResponseType.OK)
++
++ self.command_dialog = Gtk.FileChooserDialog()
++ self.command_dialog.set_title(_("Select an application"))
++ self.command_dialog.add_buttons(_("_Cancel"), Gtk.ResponseType.CANCEL, _("_OK"), Gtk.ResponseType.OK)
++ self.workdir_dialog = Gtk.FileChooserDialog()
++ self.workdir_dialog.set_title(_("Select a working directory"))
++ self.workdir_dialog.add_buttons(_("_Cancel"), Gtk.ResponseType.CANCEL, _("_OK"), Gtk.ResponseType.OK)
++ self.workdir_dialog.set_action(Gtk.FileChooserAction.SELECT_FOLDER)
++ self.icon_dialog = Gtk.FileChooserDialog()
++ self.icon_dialog.set_title(_("Select an icon"))
++ self.icon_dialog.add_buttons(_("_Cancel"), Gtk.ResponseType.CANCEL, _("_OK"), Gtk.ResponseType.OK)
++ icon_filter = Gtk.FileFilter()
++ icon_filter.add_pixbuf_formats()
++ self.icon_dialog.set_filter(icon_filter)
++ self.previewer = Gtk.Image()
++ self.icon_dialog.set_preview_widget(self.previewer)
++ self.icon_dialog.connect("update-preview", self.update_preview)
++
++ self.command_button.connect("clicked", self.select_file, "command")
++ self.workdir_button.connect("clicked", self.select_file, "workdir")
++ self.icon_button.connect("clicked", self.select_image)
++
++ self.set_resizable(False)
++ box = self.get_content_area()
++ box.pack_start(grid, True, True, 10)
++
++ def select_file(self, button, name):
++ dialog = getattr(self, name + "_dialog")
++ entry = getattr(self, name + "_entry")
++ action = dialog.run()
++ if action == Gtk.ResponseType.OK:
++ entry.set_text(dialog.get_filename())
++ dialog.hide()
++
++ def select_image(self, button):
++ action = self.icon_dialog.run()
++ if action == Gtk.ResponseType.OK:
++ filename = self.icon_dialog.get_filename()
++ pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(filename, 64, 64)
++ if pixbuf is None:
++ return
++ image = Gtk.Image.new_from_pixbuf(pixbuf)
++ self.icon_button.set_image(image)
++ self.icon = filename
++ self.icon_dialog.hide()
++
++ def update_preview(self, chooser):
++ filename = chooser.get_preview_filename()
++ if filename is None or not os.path.isfile(filename):
++ chooser.set_preview_widget_active(False)
++ return
++ pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(filename, 256, 256)
++ if pixbuf is not None:
++ image = chooser.get_preview_widget()
++ image.set_from_pixbuf(pixbuf)
++ chooser.set_preview_widget_active(pixbuf is not None)
++
++ def load(self, filename):
++ self.file = None
++ if not os.path.exists(filename):
++ return False
++ try:
++ self.config.read(filename)
++ except:
++ return False
++ if self.get_item("Type") != "Application":
++ return False
++ self.file = filename
++ self.name_entry.set_text(self.get_item("Name"))
++ self.comment_entry.set_text(self.get_item("Comment"))
++ self.command_entry.set_text(self.get_item("Exec"))
++ self.workdir_entry.set_text(self.get_item("Path"))
++ self.notification_check.set_active(self.get_item("StartupNotify") == "true")
++ self.terminal_check.set_active(self.get_item("Terminal") == "true")
++ icon = self.get_item("Icon")
++ if icon.startswith("/"):
++ pixbuf = GdkPixbuf.new_from_file_at_size(icon, 64, 64)
++ else:
++ icon_theme = Gtk.IconTheme.get_default()
++ pixbuf = icon_theme.load_icon(icon, 64, 0)
++ if pixbuf is not None:
++ self.icon = icon
++ image = Gtk.Image.new_from_pixbuf(pixbuf)
++ self.icon_button.set_image(image)
++ return True
++
++ def save(self, filename=None):
++ if filename is None:
++ filename = self.file
++ if "Desktop Entry" not in self.config.sections():
++ self.config["Desktop Entry"] = {}
++ self.set_item("Name", self.name_entry.get_text())
++ self.set_item("Comment", self.comment_entry.get_text())
++ self.set_item("Exec", self.command_entry.get_text())
++ self.set_item("Path", self.workdir_entry.get_text())
++ self.set_item("StartupNotify", self.notification_check.get_active())
++ self.set_item("Terminal", self.terminal_check.get_active())
++ self.set_item("Icon", self.icon)
++ try:
++ f = open(filename, "w", encoding="utf-8")
++ self.config.write(f, space_around_delimiters=False)
++ f.close()
++ return True
++ except:
++ logger.error("failed to save launcher: %s" % filename)
++ return False
++
++ def get_item(self, name):
++ if name in ("Name", "Comment", "GenericName"):
++ keys = [ name + "[%s]" % l for l in self.langs ]
++ keys.append(name)
++ else:
++ keys = [ name ]
++ for key in keys:
++ try:
++ text = self.config.get("Desktop Entry", key, raw=True)
++ except:
++ pass
++ else:
++ self.keymap[name] = key
++ return text
++ return ""
++
++ def set_item(self, name, value):
++ if name in self.keymap:
++ name = self.keymap[name]
++ try:
++ if type(value) == bool:
++ if value:
++ value = "true"
++ else:
++ value = "false"
++ if value is None or value == "":
++ self.config.remove_option("Desktop Entry", name)
++ else:
++ text = self.config.set("Desktop Entry", name, value)
++ except:
++ pass
+diff --git a/dockbarx/dockbar.py b/dockbarx/dockbar.py
+index 104476a..f6ec793 100644
+--- a/dockbarx/dockbar.py
++++ b/dockbarx/dockbar.py
+@@ -493,6 +493,8 @@ def load(self):
+ from .dbx_dbus import DockbarDBus
+ global UnityWatcher
+ from .unity import UnityWatcher
++ global DesktopFileEditor
++ from .desktopfileeditor import DesktopFileEditor
+ global shlex
+ import shlex
+
+@@ -1412,13 +1414,19 @@ def edit_launcher(self, path, identifier):
+ "mate-desktop-item-edit", "exo-desktop-item-edit")
+ for program in programs:
+ if check_program(program):
++ process = subprocess.Popen([program, new_path], env=os.environ)
++ GLib.timeout_add(100, self.__wait_for_launcher_editor,
++ process, path, new_path, identifier)
+ break
+ else:
+- logger.warning("Error: Found no program for editing .desktop files.")
+- return
+- process = subprocess.Popen([program, new_path], env=os.environ)
+- GLib.timeout_add(100, self.__wait_for_launcher_editor,
+- process, path, new_path, identifier)
++ editor = DesktopFileEditor()
++ editor.load(path)
++ editor.show_all()
++ action = editor.run()
++ if action == Gtk.ResponseType.OK:
++ editor.save(new_path)
++ self.__wait_for_launcher_editor(None, path, new_path, identifier)
++ editor.destroy()
+
+ def update_pinned_apps_list(self, arg=None):
+ # Saves pinned_apps_list
+@@ -1591,7 +1599,7 @@ def __identifier_dialog(self, identifier=None):
+
+ def __wait_for_launcher_editor(self, process,
+ old_path, new_path, identifier):
+- if process.poll() != None:
++ if process is None or process.poll() != None:
+ # Launcher editor closed.
+ if os.path.isfile(new_path):
+ # Update desktop_entry.
+diff --git a/dockbarx/groupbutton.py b/dockbarx/groupbutton.py
+index ed05d60..4aa5ce9 100644
+--- a/dockbarx/groupbutton.py
++++ b/dockbarx/groupbutton.py
+@@ -813,6 +813,9 @@ def __menu_edit_launcher(self, widget=None, event=None):
+ path = ""
+ self.dockbar_r().edit_launcher(path, self.identifier)
+ self.popup.hide()
++ if self.globals.gtkmenu:
++ # the modal DesktopFileEditor dialog prevented us from receiving selection-done signal
++ self.__menu_closed()
+
+ def __menu_pin(self, widget=None, event=None):
+ self.pinned = True
+
+From 1d31bdad9fe66119077d567fd09d376e3dfc285f Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sun, 23 Aug 2020 20:42:28 +0800
+Subject: [PATCH 09/25] avoid calling method on None
+
+---
+ dockbarx/groupbutton.py | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/dockbarx/groupbutton.py b/dockbarx/groupbutton.py
+index 4aa5ce9..5a3f48c 100644
+--- a/dockbarx/groupbutton.py
++++ b/dockbarx/groupbutton.py
+@@ -785,9 +785,10 @@ def __menu_position(self, menu):
+
+ def __menu_closed(self, menushell=None):
+ # Used only with the gtk menu
+- self.globals.gtkmenu = None
+- self.menu.delete_menu()
+- self.menu = None
++ if self.globals.gtkmenu:
++ self.globals.gtkmenu = None
++ self.menu.delete_menu()
++ self.menu = None
+
+ def __menu_unminimize_all_windows(self, widget=None, event=None):
+ if event:
+
+From 1dd377a77868b7100145e84001beb885d4867fd2 Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sun, 23 Aug 2020 22:42:26 +0800
+Subject: [PATCH 10/25] fixed method
+
+---
+ dockbarx/desktopfileeditor.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/dockbarx/desktopfileeditor.py b/dockbarx/desktopfileeditor.py
+index 1f07531..3b11714 100644
+--- a/dockbarx/desktopfileeditor.py
++++ b/dockbarx/desktopfileeditor.py
+@@ -187,7 +187,7 @@ def load(self, filename):
+ self.terminal_check.set_active(self.get_item("Terminal") == "true")
+ icon = self.get_item("Icon")
+ if icon.startswith("/"):
+- pixbuf = GdkPixbuf.new_from_file_at_size(icon, 64, 64)
++ pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(icon, 64, 64)
+ else:
+ icon_theme = Gtk.IconTheme.get_default()
+ pixbuf = icon_theme.load_icon(icon, 64, 0)
+
+From fba486ec131e3356ff0236724eba92f57fb8c48e Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sun, 23 Aug 2020 22:46:52 +0800
+Subject: [PATCH 11/25] fixed unhidden popup menu
+
+---
+ dockbarx/groupbutton.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/dockbarx/groupbutton.py b/dockbarx/groupbutton.py
+index 5a3f48c..259c0b9 100644
+--- a/dockbarx/groupbutton.py
++++ b/dockbarx/groupbutton.py
+@@ -812,8 +812,8 @@ def __menu_edit_launcher(self, widget=None, event=None):
+ path = self.desktop_entry.getFileName()
+ else:
+ path = ""
+- self.dockbar_r().edit_launcher(path, self.identifier)
+ self.popup.hide()
++ self.dockbar_r().edit_launcher(path, self.identifier)
+ if self.globals.gtkmenu:
+ # the modal DesktopFileEditor dialog prevented us from receiving selection-done signal
+ self.__menu_closed()
+
+From 087436756a59c96c46b2b661d01fe1a7c12d7ba9 Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sat, 29 Aug 2020 00:31:50 +0800
+Subject: [PATCH 12/25] free pixmap to avoid memory leak. fixed issue #147
+
+---
+ dockbarx/windowbutton.py | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/dockbarx/windowbutton.py b/dockbarx/windowbutton.py
+index 751a15a..4226008 100644
+--- a/dockbarx/windowbutton.py
++++ b/dockbarx/windowbutton.py
+@@ -522,6 +522,7 @@ def set_preview_image(self):
+ geo = xwin.get_geometry()
+ pixmap = xwin.composite_name_window_pixmap()
+ image_object = pixmap.get_image(0, 0, geo.width, geo.height, Xlib.X.ZPixmap, 0xffffffff)
++ pixmap.free()
+ xwin.composite_unredirect_window(Xlib.ext.composite.RedirectAutomatic)
+ except:
+ self.preview.set_from_pixbuf(window.wnck.get_icon())
+
+From 89c39a570559d2cc974ece26023a8c9f8d9b7ee1 Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sat, 29 Aug 2020 12:18:11 +0800
+Subject: [PATCH 13/25] fixed Spacer width
+
+---
+ dockx | 7 -------
+ 1 file changed, 7 deletions(-)
+
+diff --git a/dockx b/dockx
+index af986a3..0349867 100755
+--- a/dockx
++++ b/dockx
+@@ -741,13 +741,6 @@ class DockX(CairoDockX):
+ self.box.pack_start(boxes[0], True, True, 0)
+ self.box.pack_start(boxes[1], False, False, 0)
+ self.box.pack_start(boxes[2], True, True, 0)
+- # Use hugh size requests so that the boxes get equally large.
+- if pos in ("left", "right"):
+- boxes[0].set_size_request(-1, 3000)
+- boxes[2].set_size_request(-1, 3000)
+- else:
+- boxes[0].set_size_request(3000, -1)
+- boxes[2].set_size_request(3000, -1)
+ else:
+ boxes = None
+ return boxes
+
+From 5281e9e3932cb67a05c2a5ee0386340f70af0757 Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sat, 29 Aug 2020 12:52:52 +0800
+Subject: [PATCH 14/25] catch exceptions during applet initialization
+
+---
+ dockx | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/dockx b/dockx
+index 0349867..97b79b7 100755
+--- a/dockx
++++ b/dockx
+@@ -715,7 +715,11 @@ class DockX(CairoDockX):
+ if appletscr is None:
+ continue
+ applet_id = self.applets.get_id(name)
+- applet = appletscr.get_dbx_applet({"name":name, "id":applet_id, "dock":self})
++ try:
++ applet = appletscr.get_dbx_applet({"name":name, "id":applet_id, "dock":self})
++ except:
++ logger.warning("Failed to load applet %s. If this applet was installed after DockbarX started, please restart DockbarX" % name)
++ continue
+ expand = applet.get_expand() and panel
+ applet.finish_init()
+ if expand:
+
+From d2044ece46db849d29061980367876acfb21abef Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sat, 29 Aug 2020 13:42:46 +0800
+Subject: [PATCH 15/25] update AUR urls
+
+---
+ README.md | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/README.md b/README.md
+index d428506..696241c 100644
+--- a/README.md
++++ b/README.md
+@@ -33,13 +33,13 @@ sudo apt-get install dockbarx-themes-extra
+ ```
+
+ ## Install in archlinux
+-There is an aur for DockbarX
++There is an AUR for DockbarX
+
+-https://aur.archlinux.org/packages/dockbarx-gtk3-git/
++https://aur.archlinux.org/packages/dockbarx/
+
+ And there is also one for xfce4-dockbarx-plugin
+
+-https://aur.archlinux.org/packages/xfce4-dockbarx-plugin-gtk3-git/
++https://aur.archlinux.org/packages/xfce4-dockbarx-plugin/
+
+
+ ## Manual Installation
+
+From c7d87cf7c5be785441f7b43cc88e0b12ac0ceb6b Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sat, 5 Sep 2020 01:29:59 +0800
+Subject: [PATCH 16/25] fixed a constant
+
+---
+ dockbarx/groupbutton.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/dockbarx/groupbutton.py b/dockbarx/groupbutton.py
+index 259c0b9..e615ade 100644
+--- a/dockbarx/groupbutton.py
++++ b/dockbarx/groupbutton.py
+@@ -51,7 +51,7 @@
+ X = None
+
+ try:
+- WNCK_WINDOW_ACTION_MAXIMIZE = Wnck.WindowType.ACTION_MAXIMIZE
++ WNCK_WINDOW_ACTION_MAXIMIZE = Wnck.WindowActions.MAXIMIZE
+ except:
+ WNCK_WINDOW_ACTION_MAXIMIZE = 1 << 14
+
+
+From e61886994937e412fc112add644a1805aadd83d7 Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sat, 5 Sep 2020 01:37:56 +0800
+Subject: [PATCH 17/25] fixed maximize menu status
+
+---
+ dockbarx/groupbutton.py | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/dockbarx/groupbutton.py b/dockbarx/groupbutton.py
+index e615ade..e5b4440 100644
+--- a/dockbarx/groupbutton.py
++++ b/dockbarx/groupbutton.py
+@@ -613,6 +613,7 @@ def __menu_build(self):
+ if not window.wnck.is_maximized() \
+ and window.wnck.get_actions() & WNCK_WINDOW_ACTION_MAXIMIZE:
+ maximize = True
++ break
+ else:
+ maximize = False
+ minimize = self.get_unminimized_count() > 0
+
+From aec0ab5f463e8fa2f25d15ef0325cd95fddb413a Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sun, 6 Sep 2020 12:20:52 +0800
+Subject: [PATCH 18/25] update dockx colors immediately when setting changed
+
+---
+ dockbarx/common.py | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/dockbarx/common.py b/dockbarx/common.py
+index 2686795..cd60d07 100644
+--- a/dockbarx/common.py
++++ b/dockbarx/common.py
+@@ -922,6 +922,7 @@ def __on_theme_gsettings_changed(self, settings, gkey, data=None):
+ def __on_dock_theme_gsettings_changed(self, settings, gkey, data=None):
+ colors = self.dock_theme_gsettings.get_value("colors").unpack()
+ self.__update_dock_colors(colors)
++ self.emit("dock-theme-changed")
+
+ def __get_settings(self, default):
+ settings = default.copy()
+
+From dbbae3a3ccea230819f5903954792ee64ed8cf88 Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sun, 6 Sep 2020 12:31:19 +0800
+Subject: [PATCH 19/25] fixed the initial visibility of some widgets
+
+---
+ dbx_preference | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/dbx_preference b/dbx_preference
+index cdeb1b2..2356bac 100755
+--- a/dbx_preference
++++ b/dbx_preference
+@@ -1433,9 +1433,9 @@ class PrefDialog():
+ notebook.append_page(plugins_box, Gtk.Label(label=_("Plugins")))
+ notebook.append_page(advanced_box, Gtk.Label(label=_("Advanced")))
+ ca.pack_start(notebook, True, True, 0)
+- self.__update()
+ self.dialog.add_button(_("_Close"), Gtk.ResponseType.CLOSE)
+ self.dialog.show_all()
++ self.__update()
+ dock = False
+ for name in dbus.SessionBus().list_names():
+ if str(name).startswith("org.dockbar.DockX"):
+
+From 32b36016a6a3019c6694893cfe0b2d19bed65774 Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sun, 6 Sep 2020 19:55:38 +0800
+Subject: [PATCH 20/25] fixed drag & drop bugs
+
+---
+ dockbarx/dockbar.py | 71 ++++++++++++++--------
+ dockbarx/groupbutton.py | 130 ++++++++++++++++++++--------------------
+ dockx | 61 ++++++++++++-------
+ 3 files changed, 152 insertions(+), 110 deletions(-)
+
+diff --git a/dockbarx/dockbar.py b/dockbarx/dockbar.py
+index f6ec793..600df50 100644
+--- a/dockbarx/dockbar.py
++++ b/dockbarx/dockbar.py
+@@ -92,6 +92,8 @@ def __init__(self, dockbar):
+
+ self.drag_dest_set(0, [], 0)
+ self.drag_entered = False
++ self.drag_launcher = False
++ self.on_drop = False
+ self.connect("button-release-event", self.on_button_release_event)
+ self.connect("drag-motion", self.on_drag_motion)
+ self.connect("drag-leave", self.on_drag_leave)
+@@ -106,49 +108,65 @@ def on_button_release_event(self, widget, event):
+ def on_drag_drop(self, widget, drag_context, x, y, t):
+ targets = [target.name() for target in drag_context.list_targets()]
+ if "text/groupbutton_name" in targets:
+- self.drag_get_data(drag_context, "text/groupbutton_name", t)
+- drag_context.finish(True, False, t)
++ self.drag_get_data(drag_context, Gdk.Atom.intern("text/groupbutton_name", False), t)
+ elif "text/uri-list" in targets:
+- self.drag_get_data(drag_context, "text/uri-list", t)
+- drag_context.finish(True, False, t)
++ self.on_drop = True
++ self.drag_get_data(drag_context, Gdk.Atom.intern("text/uri-list", False), t)
+ else:
+- drag_context.finish(False, False, t)
++ return False
+ return True
+
+- def on_drag_data_received(self, widget, context, x, y, selection, targetType, t):
++ def on_drag_data_received(self, widget, drag_context, x, y, selection, targetType, t):
+ selection_target = selection.get_target().name()
+ if selection_target == "text/groupbutton_name":
+- self.dockbar_r().groupbutton_moved(selection.get_data(),
+- "after")
++ name = selection.get_data().decode()
++ self.dockbar_r().groupbutton_moved(name, "after")
++ drag_context.finish(True, False, t)
+ elif selection_target == "text/uri-list":
+- if ".desktop" in selection.get_data():
+- # .desktop file! This is a potential launcher.
+- #remove "file://" and "/n" from the URI
+- path = selection.get_data()
+- path = path.replace('\000', '') # for spacefm
+- if path.startswith("file://"):
+- path = path[7:]
+- else:
+- # No support for other kind of uris.
+- return
+- path = path.rstrip()
+- path = urllib.parse.unquote(path)
+- self.dockbar_r().launcher_dropped(path, "after")
++ dropped = False;
++ for uri in selection.get_uris():
++ uri = uri.replace('\000', '') # for spacefm
++ if uri.startswith("file://") and uri.endswith(".desktop"):
++ # .desktop file! This is a potential launcher.
++ if self.on_drop:
++ #remove "file://" from the URI
++ path = uri[7:]
++ path = urllib.parse.unquote(path)
++ self.dockbar_r().launcher_dropped(path, "after")
++ dropped = True
++ else:
++ self.drag_launcher = True
++ break
++ if self.on_drop:
++ drag_context.finish(dropped, False, t)
++ else:
++ self.on_drag_motion(widget, drag_context, x, y, t)
+
+ def on_drag_motion(self, widget, drag_context, x, y, t):
+ if not self.drag_entered:
+ self.on_drag_enter(widget, drag_context, x, y, t)
++ return True
+ targets = [target.name() for target in drag_context.list_targets()]
+ if "text/groupbutton_name" in targets:
+ Gdk.drag_status(drag_context, Gdk.DragAction.MOVE, t)
+- elif "text/uri-list" in targets():
+- Gdk.drag_status(drag_context, Gdk.DragAction.COPY, t)
++ elif "text/uri-list" in targets:
++ if self.drag_launcher:
++ Gdk.drag_status(drag_context, Gdk.DragAction.COPY, t)
++ else:
++ Gdk.drag_status(drag_context, Gdk.DragAction.PRIVATE, t)
+ else:
+ Gdk.drag_status(drag_context, Gdk.DragAction.PRIVATE, t)
+ return True
+
+ def on_drag_enter(self, widget, drag_context, x, y, t):
+ self.drag_entered = True
++ targets = [target.name() for target in drag_context.list_targets()]
++ if "text/groupbutton_name" in targets:
++ pass
++ elif "text/uri-list" in targets:
++ self.on_drop = False
++ self.drag_launcher = False
++ self.drag_get_data(drag_context, Gdk.Atom.intern("text/uri-list", False), t)
+
+ def on_drag_leave(self, widget, drag_context, t):
+ self.drag_entered = False
+@@ -1059,8 +1077,11 @@ def __add_window(self, window):
+ return
+
+ def __remove_window(self, window):
+- identifier = self.windows[window]
+- group = self.groups[identifier]
++ try:
++ identifier = self.windows[window]
++ group = self.groups[identifier]
++ except KeyError:
++ return
+ group.del_window(window)
+ if not len(group) and not group.pinned:
+ self.remove_groupbutton(group)
+diff --git a/dockbarx/groupbutton.py b/dockbarx/groupbutton.py
+index e5b4440..2cb5ad0 100644
+--- a/dockbarx/groupbutton.py
++++ b/dockbarx/groupbutton.py
+@@ -1391,10 +1391,10 @@ def __init__(self, group, size):
+ # raised.
+ self.drag_dest_set(0, [], 0)
+ self.drag_entered = False
+- self.launcher_drag = False
++ self.dnd_has_launcher = False
++ self.dnd_on_drop = False
+ self.dnd_position = "end"
+ self.dnd_show_popup = None
+- self.dd_uri = None
+
+ #Make buttons drag-able
+ self.drag_source_set(Gdk.ModifierType.BUTTON1_MASK,[], Gdk.DragAction.MOVE)
+@@ -1486,13 +1486,13 @@ def update_state(self, force_update=False):
+ state_type = state_type | IconFactory.MOUSE_BUTTON_DOWN
+
+ if self.mouse_over or \
+- (self.drag_entered and not self.launcher_drag):
++ (self.drag_entered and not self.dnd_has_launcher):
+ state_type = state_type | IconFactory.MOUSE_OVER
+
+ if self.launch_effect:
+ state_type = state_type | IconFactory.LAUNCH_EFFECT
+
+- if self.launcher_drag:
++ if self.dnd_has_launcher:
+ if self.dnd_position == "start":
+ state_type = state_type | IconFactory.DRAG_DROPP_START
+ else:
+@@ -1720,6 +1720,7 @@ def on_drag_data_get(self, widget, context, selection, targetType, eventTime):
+
+ def on_drag_end(self, widget, drag_context, result=None):
+ self.is_current_drag_source = False
++ self.globals.dragging = False
+ #~ # A delay is needed to make sure the button is
+ #~ # shown after on_drag_end has hidden it and
+ #~ # not the other way around.
+@@ -1727,63 +1728,50 @@ def on_drag_end(self, widget, drag_context, result=None):
+
+ #### DnD (target)
+ def on_drag_drop(self, widget, drag_context, x, y, t):
+- group = self.group_r()
+ targets = [target.name() for target in drag_context.list_targets()]
+ if "text/groupbutton_name" in targets:
+ target_atom = Gdk.Atom.intern("text/groupbutton_name", False)
+ self.drag_get_data(drag_context, target_atom, t)
+- drag_context.finish(True, False, t)
+ elif "text/uri-list" in targets:
+- #Drag data should already be stored in self.dd_uri
+- if ".desktop" in self.dd_uri:
+- # .desktop file! This is a potential launcher.
+- #remove "file://" and "/n" from the URI
+- path = self.dd_uri
+- if path.startswith("file://"):
+- path = path.replace('\000', '') # for spacefm
+- path = path[7:]
+- else:
+- # No support for other kind of uris.
+- return
+- path = path.rstrip()
+- path = urllib.parse.unquote(path)
+- self.dockbar_r().launcher_dropped(path, group,
+- self.dnd_position)
+- else:
+- uri = self.dd_uri
+- # Remove the new line at the end
+- uri = uri.rstrip()
+- group.launch(None, None, uri)
+- drag_context.finish(True, False, t)
+- else:
+- drag_context.finish(False, False, t)
+- self.dd_uri = None
++ self.dnd_on_drop = True
++ target_atom = Gdk.Atom.intern("text/uri-list", False)
++ self.drag_get_data(drag_context, target_atom, t)
++ else:
++ return False
+ return True
+
+ def on_drag_data_received(self, widget, context, x, y, selection, targetType, t):
+ group = self.group_r()
+- name = group.identifier or group.desktop_entry.getFileName()
+ selection_target = selection.get_target().name()
+ if selection_target == "text/groupbutton_name":
++ name = group.identifier or group.desktop_entry.getFileName()
+ # Selection data is in bytes we need to decode it to a string.
+ data = selection.get_data().decode()
+ if data != name:
+ self.dockbar_r().groupbutton_moved(data, group, self.dnd_position)
++ context.finish(True, False, t)
+ elif selection_target == "text/uri-list":
+- # Uri lists are tested on first motion instead on drop
+- # to check if it's a launcher.
+- # The data is saved in self.dd_uri to be used again
+- # if the file is dropped.
+- self.dd_uri = selection.get_data().decode()
+- if ".desktop" in selection.get_data().decode():
+- # .desktop file! This is a potential launcher.
+- self.launcher_drag = True
+- self.update_state()
++ for uri in selection.get_uris():
++ uri = uri.replace('\000', '') # for spacefm
++ if uri.startswith("file://") and uri.endswith(".desktop"):
++ # .desktop file! This is a potential launcher.
++ if self.dnd_on_drop:
++ #remove "file://" from the URI
++ path = uri[7:]
++ path = urllib.parse.unquote(path)
++ self.dockbar_r().launcher_dropped(path, group,
++ self.dnd_position)
++ else:
++ self.dnd_has_launcher = True
++ break
++ elif self.dnd_on_drop:
++ group.launch(None, None, uri)
++ if self.dnd_on_drop:
++ context.finish(True, False, t)
++ else:
++ self.__update_dragging_status(x, y)
+
+ def on_drag_motion(self, widget, drag_context, x, y, t):
+- group = self.group_r()
+- if not self.drag_entered:
+- self.on_drag_enter(widget, drag_context, x, y, t)
+ targets = [target.name() for target in drag_context.list_targets()]
+ if "text/groupbutton_name" in targets and \
+ not self.is_current_drag_source:
+@@ -1792,38 +1780,26 @@ def on_drag_motion(self, widget, drag_context, x, y, t):
+ Gdk.drag_status(drag_context, Gdk.DragAction.COPY, t)
+ else:
+ Gdk.drag_status(drag_context, Gdk.DragAction.PRIVATE, t)
+- if self.launcher_drag:
+- dnd_position = "end"
+- if self.dockbar_r().orient in ("left", "right"):
+- if y <= self.get_allocation().height // 2:
+- dnd_position = "start"
+- else:
+- if x <= self.get_allocation().width // 2:
+- dnd_position = "start"
+- if dnd_position != self.dnd_position:
+- self.dnd_position = dnd_position
+- self.update_state()
++
++ if not self.drag_entered:
++ self.on_drag_enter(widget, drag_context, x, y, t)
++ return True
++
++ self.__update_dragging_status(x, y)
+ return True
+
+ def on_drag_enter(self, widget, drag_context, x, y, t):
+ group = self.group_r()
+ self.drag_entered = True
+ targets = [target.name() for target in drag_context.list_targets()]
+- if not "text/groupbutton_name" in targets:
+- win_nr = group.get_count()
+- if win_nr == 1:
+- group[0].select_after_delay(600)
+- elif win_nr > 1:
+- delay = self.globals.settings["popup_delay"]
+- self.dnd_show_popup = GLib.timeout_add(delay,
+- group.popup.show)
+ if "text/groupbutton_name" in targets:
+ if not self.is_current_drag_source:
+- self.launcher_drag = True
++ self.dnd_has_launcher = True
+ self.update_state()
+ elif "text/uri-list" in targets:
+ # We have to get the data to find out if this
+ # is a launcher or something else.
++ self.dnd_on_drop = False
+ target_atom = Gdk.Atom.intern("text/uri-list", False)
+ self.drag_get_data(drag_context, target_atom, t)
+ # No update_state() here!
+@@ -1832,7 +1808,7 @@ def on_drag_enter(self, widget, drag_context, x, y, t):
+
+ def on_drag_leave(self, widget, drag_context, t):
+ group = self.group_r()
+- self.launcher_drag = False
++ self.dnd_has_launcher = False
+ self.drag_entered = False
+ self.update_state()
+ group.popup.hide_if_not_hovered(100)
+@@ -1849,6 +1825,28 @@ def on_drag_leave(self, widget, drag_context, t):
+ #~ # the drop is completed.
+ #~ GLib.timeout_add(20, self.hide)
+
++ def __update_dragging_status(self, x, y):
++ if self.dnd_has_launcher:
++ dnd_position = "end"
++ if self.dockbar_r().orient in ("left", "right"):
++ if y <= self.get_allocation().height // 2:
++ dnd_position = "start"
++ else:
++ if x <= self.get_allocation().width // 2:
++ dnd_position = "start"
++ if dnd_position != self.dnd_position:
++ self.dnd_position = dnd_position
++ self.update_state()
++ else:
++ group = self.group_r()
++ win_nr = group.get_count()
++ if win_nr == 1:
++ group[0].select_after_delay(600)
++ elif win_nr > 1:
++ delay = self.globals.settings["popup_delay"]
++ self.dnd_show_popup = GLib.timeout_add(delay, group.popup.show)
++ self.update_state()
++
+
+ #### Events
+ def on_size_allocate(self, widget, allocation):
+@@ -1919,7 +1917,9 @@ def on_enter_notify_event(self, widget, event):
+ self.opacify(delay)
+
+ def on_leave_notify_event(self, widget, event):
+- self.leave_notify_sid = None
++ if self.leave_notify_sid is not None:
++ GLib.source_remove(self.leave_notify_sid)
++ self.leave_notify_sid = None
+ group = self.group_r()
+ if group is None:
+ return
+diff --git a/dockx b/dockx
+index 97b79b7..2610b2c 100755
+--- a/dockx
++++ b/dockx
+@@ -46,6 +46,7 @@ import weakref
+ import time
+ import dbus
+ import dbus.service
++import urllib.parse
+
+ WNCK_WINDOW_STATE_MINIMIZED = 1
+
+@@ -1361,6 +1362,8 @@ class EventPadding(Gtk.EventBox):
+ self.position = None
+ self.drag_dest_set(0, [], 0)
+ self.drag_entered = False
++ self.drag_launcher = False
++ self.on_drop = False
+ self.connect("button-release-event", self.on_button_release_event)
+ self.connect("drag-motion", self.on_drag_motion)
+ self.connect("drag-leave", self.on_drag_leave)
+@@ -1403,13 +1406,12 @@ class EventPadding(Gtk.EventBox):
+ def on_drag_drop(self, widget, drag_context, x, y, t):
+ targets = [target.name() for target in drag_context.list_targets()]
+ if "text/groupbutton_name" in targets:
+- self.drag_get_data(drag_context, "text/groupbutton_name", t)
+- drag_context.finish(True, False, t)
++ self.drag_get_data(drag_context, Gdk.Atom.intern("text/groupbutton_name", False), t)
+ elif "text/uri-list" in targets:
+- self.drag_get_data(drag_context, "text/uri-list", t)
+- drag_context.finish(True, False, t)
++ self.on_drop = True
++ self.drag_get_data(drag_context, Gdk.Atom.intern("text/uri-list", False), t)
+ else:
+- drag_context.finish(False, False, t)
++ return False
+ return True
+
+ def on_drag_data_received(self, widget, context, x, y, selection, targetType, t):
+@@ -1417,38 +1419,57 @@ class EventPadding(Gtk.EventBox):
+ return
+ selection_target = selection.get_target().name()
+ if selection_target == "text/groupbutton_name":
+- self.dock_r().dockbar.groupbutton_moved(selection.get_data(),
+- self.position)
++ data = selection.get_data().decode()
++ self.dock_r().dockbar.groupbutton_moved(data, self.position)
++ context.finish(True, False, t)
+ elif selection_target == "text/uri-list":
+- if ".desktop" in selection.get_data():
+- # .desktop file! This is a potential launcher.
+- #remove "file://" and "/n" from the URI
+- path = selection.get_data()
+- if path.startswith("file://"):
+- path = path[7:]
+- else:
+- # No support for other kind of uris.
+- return
+- path = path.rstrip()
+- path = path.replace("%20"," ")
+- self.dock_r().dockbar.launcher_dropped(path, self.position)
++ dropped = False
++ for uri in selection.get_uris():
++ uri = uri.replace('\000', '') # for spacefm
++ if uri.startswith("file://") and uri.endswith(".desktop"):
++ # .desktop file! This is a potential launcher.
++ if self.on_drop:
++ #remove "file://" from the URI
++ path = uri[7:]
++ path = urllib.parse.unquote(path)
++ self.dock_r().dockbar.launcher_dropped(path, self.position)
++ dropped = True
++ else:
++ self.drag_launcher = True
++ break
++ if self.on_drop:
++ context.finish(dropped, False, t)
++ else:
++ self.on_drag_motion(widget, context, x, y, t)
+
+ def on_drag_motion(self, widget, drag_context, x, y, t):
+ if not self.position in ("before", "after"):
+ return True
+ if not self.drag_entered:
+ self.on_drag_enter(widget, drag_context, x, y, t)
++ return True
+ targets = [target.name() for target in drag_context.list_targets()]
+ if "text/groupbutton_name" in targets:
+ Gdk.drag_status(drag_context, Gdk.DragAction.MOVE, t)
+ elif "text/uri-list" in targets:
+- Gdk.drag_status(drag_context, Gdk.DragAction.COPY, t)
++ if self.drag_launcher:
++ Gdk.drag_status(drag_context, Gdk.DragAction.COPY, t)
++ else:
++ Gdk.drag_status(drag_context, Gdk.DragAction.PRIVATE, t)
+ else:
+ Gdk.drag_status(drag_context, Gdk.DragAction.PRIVATE, t)
+ return True
+
+ def on_drag_enter(self, widget, drag_context, x, y, t):
+ self.drag_entered = True
++ targets = [target.name() for target in drag_context.list_targets()]
++ if "text/groupbutton_name" in targets:
++ pass
++ elif "text/uri-list" in targets:
++ self.on_drop = False
++ self.drag_launcher = False
++ self.drag_get_data(drag_context, Gdk.Atom.intern("text/uri-list", False), t)
++
+
+ def on_drag_leave(self, widget, drag_context, t):
+ self.drag_entered = False
+
+From 663001e6859f3181295845da45ac6b0c262a459d Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sun, 6 Sep 2020 20:11:29 +0800
+Subject: [PATCH 21/25] sync settings
+
+---
+ dockbarx/common.py | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/dockbarx/common.py b/dockbarx/common.py
+index cd60d07..bf22b39 100644
+--- a/dockbarx/common.py
++++ b/dockbarx/common.py
+@@ -999,8 +999,10 @@ def update_colors(self, theme_name, theme_colors={}, theme_alphas={}):
+ alpha = self.theme_gsettings.get_value(a)
+ if self.theme_gsettings.get_user_value(c) is None:
+ self.theme_gsettings.set_value(c, color)
++ self.theme_gsettings.sync()
+ if self.theme_gsettings.get_user_value(a) is None:
+ self.theme_gsettings.set_value(a, alpha)
++ self.theme_gsettings.sync()
+
+ def update_popup_style(self, default_style):
+ # Runs when the theme has changed.
+@@ -1061,6 +1063,7 @@ def __update_dock_colors(self, colors):
+ if update_needed:
+ self.old_dock_gs_colors = colors
+ self.dock_theme_gsettings.set_value("colors", GLib.Variant("a{ss}", colors))
++ self.dock_theme_gsettings.sync()
+ self.emit("preference-update")
+
+
+@@ -1071,6 +1074,7 @@ def get_pinned_apps_list(self):
+
+ def set_pinned_apps_list(self, pinned_apps):
+ self.gsettings.set_value("launchers", GLib.Variant("as", pinned_apps))
++ self.gsettings.sync()
+
+ def set_shown_popup(self, popup):
+ if popup is None:
+@@ -1101,6 +1105,7 @@ def get_compiz_version(self):
+
+ def set_applets_enabled_list(self, applets_list):
+ self.applets_gsettings.set_value("enabled-list", GLib.Variant("as", applets_list))
++ self.applets_gsettings.sync()
+
+
+ __connector = Connector()
+
+From 56933975e108d7afc66ca48b6f45464bdbedb2d5 Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sat, 17 Oct 2020 00:14:44 +0800
+Subject: [PATCH 22/25] improved CEF apps compatibility. fixed issue #151
+
+---
+ dockbarx/dockbar.py | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+diff --git a/dockbarx/dockbar.py b/dockbarx/dockbar.py
+index 600df50..b440ea8 100644
+--- a/dockbarx/dockbar.py
++++ b/dockbarx/dockbar.py
+@@ -1024,6 +1024,14 @@ def __add_window(self, window):
+ if self.globals.settings["separate_ooo_apps"]:
+ connect(window, "name-changed",
+ self.__on_ooo_window_name_changed)
++ if res_class == '' and res_name == 'untitled window':
++ win_name = window.get_name().lower()
++ if win_name == 'untitled window':
++ connect(window, "name-changed",
++ self.__on_cef_window_name_changed)
++ else:
++ identifier = win_name
++
+ self.windows[window] = identifier
+ if identifier in self.groups.get_identifiers():
+ self.groups[identifier].add_window(window)
+@@ -1242,6 +1250,13 @@ def __on_ooo_window_name_changed(self, window):
+ if window == self.screen.get_active_window():
+ self.__on_active_window_changed(self.screen, None)
+
++ def __on_cef_window_name_changed(self, window):
++ if window.get_name().lower() != 'untitled window':
++ self.__remove_window(window)
++ self.__add_window(window)
++ if window == self.screen.get_active_window():
++ self.__on_active_window_changed(self.screen, None)
++
+ def __set_group_identifier(self, group, identifier):
+ group.set_identifier(identifier)
+ for window in group:
+
+From 21e86e3f0d3198eb6a2682afec0377ee28fc5fd5 Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sun, 18 Oct 2020 14:19:15 +0800
+Subject: [PATCH 23/25] add choices
+
+---
+ org.dockbar.dockbarx.gschema.xml | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+diff --git a/org.dockbar.dockbarx.gschema.xml b/org.dockbar.dockbarx.gschema.xml
+index 201a216..e729b03 100644
+--- a/org.dockbar.dockbarx.gschema.xml
++++ b/org.dockbar.dockbarx.gschema.xml
+@@ -783,6 +783,12 @@
+
+ <key name='position' type='s'>
+ <default>"left"</default>
++ <choices>
++ <choice value='left'/>
++ <choice value='right'/>
++ <choice value='top'/>
++ <choice value='bottom'/>
++ </choices>
+ <summary>The position of the dock</summary>
+ <description>
+ Position the dock at left, right, top or bottom.
+@@ -808,6 +814,11 @@
+
+ <key name='mode' type='s'>
+ <default>"centered"</default>
++ <choices>
++ <choice value='panel'/>
++ <choice value='corner'/>
++ <choice value='centered'/>
++ </choices>
+ <summary>The mode of the dock</summary>
+ <description>
+ The dock has three modes:
+@@ -819,6 +830,13 @@
+
+ <key name='behavior' type='s'>
+ <default>"panel"</default>
++ <choices>
++ <choice value='panel'/>
++ <choice value='standard'/>
++ <choice value='dodge windows'/>
++ <choice value='dodge active window'/>
++ <choice value='always autohide'/>
++ </choices>
+ <summary>The behavior of the dock</summary>
+ <description>
+ Description needed
+
+From ca39d247dd265c01666987cfd76f062f4d1d0ca6 Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sun, 18 Oct 2020 15:34:39 +0800
+Subject: [PATCH 24/25] fix autohide behavior
+
+---
+ dockx | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/dockx b/dockx
+index 2610b2c..3defb8f 100755
+--- a/dockx
++++ b/dockx
+@@ -941,6 +941,7 @@ class DockX(CairoDockX):
+
+ def show_dock(self):
+ self.show()
++ self.position_dock()
+ if self.globals.settings["dock/mode"] != "centered":
+ self.move(self.old_x, self.old_y)
+ self.dockbar.dockbar_moved()
+@@ -1305,6 +1306,7 @@ class DockX(CairoDockX):
+ # self.position_dock(rebuild=True) # already in __on_dock_theme_reloaded
+ if self.autohide:
+ self.__compute_should_autohide()
++ self.show_dock()
+
+ def on_destroy(self, *args):
+ Gtk.main_quit()
+
+From f772075ae4fc410a8bd24460ce29117ef25efc62 Mon Sep 17 00:00:00 2001
+From: xuzhen <xuzhen@users.noreply.github.com>
+Date: Sun, 18 Oct 2020 16:08:59 +0800
+Subject: [PATCH 25/25] add an option to act as a normal window. fixed issue
+ #152
+
+---
+ dbx_migrate_settings | 2 ++
+ dbx_preference | 29 +++++++++++++++++++++++++++--
+ dockbarx/common.py | 6 +++++-
+ dockbarx/dockbar.py | 3 +++
+ dockx | 10 ++++++----
+ org.dockbar.dockbarx.gschema.xml | 15 +++++++++++++--
+ 6 files changed, 56 insertions(+), 9 deletions(-)
+
+diff --git a/dbx_migrate_settings b/dbx_migrate_settings
+index 6100b64..e930486 100755
+--- a/dbx_migrate_settings
++++ b/dbx_migrate_settings
+@@ -114,6 +114,8 @@ for option in options:
+ elif key.startswith("dock/"):
+ gsettings, gschema = dockx_gsettings
+ key = key[len("dock/"):]
++ if key == "behavior" and value == "panel":
++ value = "standard"
+ elif key.startswith("themes/"):
+ path = key[:key.rindex("/")].lower()
+ gsettings, gschema = get_gsettings("dockbarx.theme", "dockbarx/" + path)
+diff --git a/dbx_preference b/dbx_preference
+index 2356bac..8a5850e 100755
+--- a/dbx_preference
++++ b/dbx_preference
+@@ -1298,6 +1298,19 @@ class PrefDialog():
+ hbox.set_border_width(5)
+ label = Gtk.Label(label=_("Type"))
+ hbox.pack_start(label, False, False, 0)
++ self.dock_type_box = Gtk.ComboBoxText()
++ for pos in (_("dock"), _("normal window")):
++ self.dock_type_box.append_text(pos)
++ self.dock_type_box.connect("changed", self.__cb_changed)
++ hbox.pack_start(self.dock_type_box, False, True, 0)
++ label = Gtk.Label(label=_("(Need to restart dockx to take effect)"))
++ hbox.pack_start(label, False, False, 0)
++ dock_box.pack_start(hbox, False, True, 0)
++
++ hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 5)
++ hbox.set_border_width(5)
++ label = Gtk.Label(label=_("Mode"))
++ hbox.pack_start(label, False, False, 0)
+ self.dock_mode_box = Gtk.ComboBoxText()
+ for pos in (_("panel"), _("corner"), _("centered")):
+ self.dock_mode_box.append_text(pos)
+@@ -1325,7 +1338,7 @@ class PrefDialog():
+ label = Gtk.Label(label=_("Behavior"))
+ hbox.pack_start(label, False, False, 0)
+ self.dock_behavior_box = Gtk.ComboBoxText()
+- for pos in (_("panel"), _("standard"), _("dodge windows"),
++ for pos in (_("standard"), _("dodge windows"),
+ _("dodge active window"), _("always autohide")):
+ self.dock_behavior_box.append_text(pos)
+ self.dock_behavior_box.connect("changed", self.__cb_changed)
+@@ -1818,6 +1831,12 @@ class PrefDialog():
+ self.dock_offset_spin.set_sensitive(False)
+ else:
+ self.dock_offset_spin.set_sensitive(True)
++ model = self.dock_type_box.get_model()
++ for i in range(len(model)):
++ mode = self.globals.settings["dock/type"].lower()
++ if model[i][0].lower() == _(mode):
++ self.dock_type_box.set_active(i)
++ break
+ model = self.dock_mode_box.get_model()
+ for i in range(len(model)):
+ mode = self.globals.settings["dock/mode"].lower()
+@@ -2013,6 +2032,12 @@ class PrefDialog():
+ if value == _(p) and p != self.globals.settings["dock/size"]:
+ self.dock_gsettings.set_string("position", p)
+
++ if combobox == self.dock_type_box:
++ pos = ("dock", "normal window")
++ for p in pos:
++ if value == _(p) and p != self.globals.settings["dock/type"]:
++ self.dock_gsettings.set_string("type", p)
++
+ if combobox == self.dock_mode_box:
+ pos = ("panel", "centered", "corner")
+ for p in pos:
+@@ -2020,7 +2045,7 @@ class PrefDialog():
+ self.dock_gsettings.set_string("mode", p)
+
+ if combobox == self.dock_behavior_box:
+- pos = ("panel", "standard", "dodge windows",
++ pos = ("standard", "dodge windows",
+ "dodge active window", "always autohide")
+ for p in pos:
+ if value == _(p) and \
+diff --git a/dockbarx/common.py b/dockbarx/common.py
+index bf22b39..0ab2b1f 100644
+--- a/dockbarx/common.py
++++ b/dockbarx/common.py
+@@ -588,6 +588,7 @@ class Globals(GObject.GObject):
+ "dock-position-changed": (GObject.SignalFlags.RUN_FIRST,
+ None,()),
+ "dock-mode-changed": (GObject.SignalFlags.RUN_FIRST, None,()),
++ "dock-type-changed": (GObject.SignalFlags.RUN_FIRST, None,()),
+ "dock-offset-changed": (GObject.SignalFlags.RUN_FIRST,
+ None,()),
+ "dock-overlap-changed": (GObject.SignalFlags.RUN_FIRST,
+@@ -714,7 +715,8 @@ class Globals(GObject.GObject):
+ "dock/size": 42,
+ "dock/offset":0,
+ "dock/mode": "centered",
+- "dock/behavior": "panel",
++ "dock/type": "dock",
++ "dock/behavior": "standard",
+ "dock/end_decorations": False,
+
+ "applets/enabled_list": ["DockbarX"]}
+@@ -890,6 +892,8 @@ def __on_dock_gsettings_changed(self, settings, gkey, data=None):
+ self.emit("dock-end-decorations-changed")
+ elif "theme-file" == gkey:
+ self.emit("dock-theme-changed")
++ elif "type" == gkey:
++ self.emit("dock-type-changed")
+ self.emit("preference-update")
+
+ def __on_applets_gsettings_changed(self, settings, gkey, data=None):
+diff --git a/dockbarx/dockbar.py b/dockbarx/dockbar.py
+index b440ea8..a685fb7 100644
+--- a/dockbarx/dockbar.py
++++ b/dockbarx/dockbar.py
+@@ -984,6 +984,9 @@ def __add_window(self, window):
+ window = Wnck.Window.get(window.get_xid())
+ res_class = window.get_class_group().get_res_class().lower()
+ res_name = window.get_class_group().get_name().lower()
++ if self.globals.settings["dock/type"] == "normal window":
++ if res_class == "dockx" and res_name == "dockx":
++ return
+ identifier = res_class or res_name
+ pid = window.get_pid()
+ if not identifier:
+diff --git a/dockx b/dockx
+index 3defb8f..9e6feb1 100755
+--- a/dockx
++++ b/dockx
+@@ -57,7 +57,8 @@ class CairoDockX(Gtk.Window):
+ GObject.GObject.__init__(self)
+ self.globals = Globals()
+ self.theme = DockTheme()
+- self.set_type_hint(Gdk.WindowTypeHint.DOCK)
++ if self.globals.settings["dock/type"] == "dock":
++ self.set_type_hint(Gdk.WindowTypeHint.DOCK)
+ self.stick()
+ self.set_accept_focus(False)
+ self.set_decorated(False)
+@@ -499,7 +500,7 @@ class DockX(CairoDockX):
+ "top": 1000, "bottom":1000}
+ self.geometry_time = 0
+ self.last_geometry_window = lambda: None
+- if self.globals.settings["dock/behavior"] in ("panel", "standard"):
++ if self.globals.settings["dock/behavior"] == "standard":
+ self.autohide = False
+ else:
+ self.autohide = True
+@@ -794,7 +795,8 @@ class DockX(CairoDockX):
+ def __set_dock_strut(self, x, y, w, h):
+ if not self.get_window():
+ return
+- set_strut = self.globals.settings["dock/behavior"] == "panel"
++ set_strut = self.globals.settings["dock/type"] == "dock" and \
++ self.globals.settings["dock/behavior"] == "standard"
+ if not set_strut:
+ topw = XDisplay.create_resource_object('window',
+ self.get_toplevel().get_window().get_xid())
+@@ -1137,7 +1139,7 @@ class DockX(CairoDockX):
+ self.position_dock()
+
+ def __on_behavior_changed(self, *args):
+- if self.globals.settings["dock/behavior"] in ("panel", "standard"):
++ if self.globals.settings["dock/behavior"] == "standard":
+ if self.autohide:
+ self.autohide = False
+ if self.autohide_sid is not None:
+diff --git a/org.dockbar.dockbarx.gschema.xml b/org.dockbar.dockbarx.gschema.xml
+index e729b03..ebd07d4 100644
+--- a/org.dockbar.dockbarx.gschema.xml
++++ b/org.dockbar.dockbarx.gschema.xml
+@@ -829,9 +829,8 @@
+ </key>
+
+ <key name='behavior' type='s'>
+- <default>"panel"</default>
++ <default>"standard"</default>
+ <choices>
+- <choice value='panel'/>
+ <choice value='standard'/>
+ <choice value='dodge windows'/>
+ <choice value='dodge active window'/>
+@@ -851,6 +850,18 @@
+ </description>
+ </key>
+
++ <key name='type' type='s'>
++ <default>"dock"</default>
++ <choices>
++ <choice value='dock'/>
++ <choice value='normal window'/>
++ </choices>
++ <summary>The window type of dockx</summary>
++ <description>
++ The window type of dockx
++ </description>
++ </key>
++
+ </schema>
+
+