diff --git a/README.md b/README.md
index 6817368..11f6d2d 100644
--- a/README.md
+++ b/README.md
@@ -1,35 +1,32 @@
# DockbarX
-### Version 1.0-beta2
+### Version 1.0-beta4
## About DockbarX
-The gtk3/python3 version of DockbarX is a lightweight taskbar / panel replacement for Linux which works as a stand-alone dock (called DockX), as a Xfce4 panel applet[^1] or a matepanel applet. DockbarX is a fork of dockbar made by Aleksey Shaferov. DockbarX branch is developed by Matias Särs.
+The gtk3/python3 version of DockbarX is a lightweight taskbar / panel replacement for Linux which works as a stand-alone dock (called DockX), as a [Xfce4 panel plugin](https://github.com/xuzhen/xfce4-dockbarx-plugin), as a [MATE panel applet](https://github.com/xuzhen/dockbarx-mate-applet) or as a [LXQt panel plugin](https://github.com/xuzhen/dockbarx-lxqt-plugin). DockbarX is a fork of dockbar made by Aleksey Shaferov. DockbarX branch is developed by Matias Särs.
DockbarX is free software and is licensed under GPL3.
-## Install in Ubuntu 18.04+ from PPA
+## Install in Ubuntu 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
-sudo apt-get update
-sudo apt-get install dockbarx
+sudo apt update
+sudo apt install dockbarx
```
-If you want to use dockbarx as a Xfce panel applet you also need this command
+If you want to use dockbarx as a panel plugin you also need these commands
```
-sudo apt-get install xfce4-dockbarx-plugin
-```
-
-If you want to use dockbarx as a MATE panel applet you also need this command
-```
-sudo apt-get install dockbarx-mate-panel-applet
+sudo apt install xfce4-dockbarx-plugin # for Xfce4 panel
+sudo apt install dockbarx-mate-panel-applet # for MATE panel
+sudo apt install dockbarx-lxqt-plugin # for LXQt panel
```
To get more themes for DockbarX and DockX use this command
```
-sudo apt-get install dockbarx-themes-extra
+sudo apt install dockbarx-themes-extra
```
## Install in archlinux
@@ -45,12 +42,17 @@ https://aur.archlinux.org/packages/xfce4-dockbarx-plugin/
## Manual Installation
1. Following dependencies needs to be installed (many of them might be installed already on your system):
- - gir1.2-gtk-3.0 (>= 3.22), gir1.2-glib-2.0 (>= 1.40), gir1.2-keybinder-3.0, gir1.2-pango-1.0, gir1.2-wnck-3.0, python3-cairo (>= 1.11.0), python3-dbus, python3-distutils, python3-gi, python3-gi-cairo, python3-pil, python3-polib, python3-xdg and python3-xlib.
+ - gir1.2-gtk-3.0 (>= 3.22), gir1.2-glib-2.0 (>= 1.40), gir1.2-keybinder-3.0, gir1.2-pango-1.0, gir1.2-wnck-3.0, python3 (>= 3.5), python3-cairo (>= 1.11.0), python3-dbus, python3-gi, python3-gi-cairo, python3-pil, python3-xdg and python3-xlib.
+ - (Only for build) python3-pip, python3-polib, python3-setuptools
- (Optional) gir1.2-zeitgeist-2.0 and zeitgeist, to access latest and most used documents.
- (Optional) indicator-application or ayatana-indicator-application, to use the appindicator applet with DockX
- (Optional) python3-pyudev (>= 0.15), to use the battery status applet with DockX
- (Optional) python3-lxml, to use the settings migrating tool
-2. Extract dockbarx. Change directory to where you extracted dockbarx and run the setup.py install `$ sudo ./setup.py install`
+2. Extract dockbarx. Change directory to where you extracted dockbarx and run the following commands:
+```
+sudo pip install .
+sudo glib-compile-schemas /usr/local/share/glib-2.0/schemas/
+```
## Usage
To run DockbarX as a stand alone dock use the command `dockx`.
@@ -118,5 +120,3 @@ A: Backup: ```dconf dump /org/dockbarx/ > dockbarx.xml```\
A: Dump the preferences from GConf database ```gconftool --dump /apps/dockbarx > dockbarx.xml```, run the migrating tool ```dbx_migrate_settings dockbarx.xml```, and move the application folder from ~/.dockbarx to ~/.local/share/dockbarx
-[^1]: Using [xfce-dockbarx-plugin] (https://github.com/M7S/xfce4-dockbarx-plugin/tree/pygi-python3)
-
diff --git a/applets/Creating applets b/applets/Creating applets
new file mode 100644
index 0000000..b763ea0
--- /dev/null
+++ b/applets/Creating applets
@@ -0,0 +1,3 @@
+An applet for Dockbarx stand alone dock needs to be a text file named %name%.applet, where %name% can be whatever you want. It doesn't have to be the same as the name of the directory you specify within the file.
+
+See hello_world for a simple example or clock for a more complex one.
diff --git a/dockx_applets/appindicator.applet b/applets/appindicator/appindicator.applet
similarity index 100%
rename from dockx_applets/appindicator.applet
rename to applets/appindicator/appindicator.applet
diff --git a/dockx_applets/appindicator.py b/applets/appindicator/appindicator.py
similarity index 100%
rename from dockx_applets/appindicator.py
rename to applets/appindicator/appindicator.py
diff --git a/dockx_applets/battery_status.applet b/applets/batterystatus/battery_status.applet
similarity index 100%
rename from dockx_applets/battery_status.applet
rename to applets/batterystatus/battery_status.applet
diff --git a/dockx_applets/battery_status.py b/applets/batterystatus/battery_status.py
similarity index 98%
rename from dockx_applets/battery_status.py
rename to applets/batterystatus/battery_status.py
index c130814..471f0ee 100644
--- a/dockx_applets/battery_status.py
+++ b/applets/batterystatus/battery_status.py
@@ -43,6 +43,7 @@
from gi.repository import Pango
import dbus
import pyudev # >= 0.15
+from xml.etree import ElementTree
from dockbarx.applets import DockXApplet, DockXAppletDialog
import dockbarx.i18n
_ = dockbarx.i18n.language.gettext
@@ -404,8 +405,11 @@ class SystemdUtils(GObject.GObject):
BUS_NAME = "org.freedesktop.login1"
LOGIN_PATH = "/org/freedesktop/login1"
LOGIN_IFNAME = "org.freedesktop.login1.Manager"
- SESSION_PATH = "/org/freedesktop/login1/session/self"
+ SESSION_ROOT_PATH = "/org/freedesktop/login1/session"
+ SESSION_PATH_NEW = "/org/freedesktop/login1/session/auto"
+ SESSION_PATH_OLD = "/org/freedesktop/login1/session/self"
SESSION_IFNAME = "org.freedesktop.login1.Session"
+ INTROSPECTABLE_IFNAME = "org.freedesktop.DBus.Introspectable"
__gsignals__ = {
"error": (GObject.SignalFlags.RUN_FIRST, None, (str,))
@@ -425,7 +429,18 @@ def __init__(self):
self.__session_iface = None
try:
if self.__bus is not None:
- session_object = self.__bus.get_object(self.BUS_NAME, self.SESSION_PATH)
+ use_old_path = True
+ session_object = self.__bus.get_object(self.BUS_NAME, self.SESSION_ROOT_PATH)
+ iface = dbus.Interface(session_object, self.INTROSPECTABLE_IFNAME)
+ xml_string = iface.Introspect()
+ for child in ElementTree.fromstring(xml_string):
+ if child.tag != "node":
+ continue
+ child_path = "/".join((self.SESSION_ROOT_PATH, child.attrib["name"]))
+ if child_path == self.SESSION_PATH_NEW:
+ use_old_path = False
+ break
+ session_object = self.__bus.get_object(self.BUS_NAME, self.SESSION_PATH_OLD if use_old_path else self.SESSION_PATH_NEW)
self.__session_iface = dbus.Interface(session_object, self.SESSION_IFNAME)
except dbus.exceptions.DBusException as exception:
self.emit("error", exception.__str__())
diff --git a/dockx_applets/battery_status_helper.sh b/applets/batterystatus/battery_status_helper.sh
similarity index 100%
rename from dockx_applets/battery_status_helper.sh
rename to applets/batterystatus/battery_status_helper.sh
diff --git a/dockx_applets/org.dockbar.applets.batterystatus.gschema.xml b/applets/batterystatus/org.dockbarx.applets.batterystatus.gschema.xml
similarity index 100%
rename from dockx_applets/org.dockbar.applets.batterystatus.gschema.xml
rename to applets/batterystatus/org.dockbarx.applets.batterystatus.gschema.xml
diff --git a/dockx_applets/clock.applet b/applets/clock/clock.applet
similarity index 100%
rename from dockx_applets/clock.applet
rename to applets/clock/clock.applet
diff --git a/dockx_applets/clock.py b/applets/clock/clock.py
similarity index 100%
rename from dockx_applets/clock.py
rename to applets/clock/clock.py
diff --git a/dockx_applets/org.dockbar.applets.clock.gschema.xml b/applets/clock/org.dockbarx.applets.clock.gschema.xml
similarity index 100%
rename from dockx_applets/org.dockbar.applets.clock.gschema.xml
rename to applets/clock/org.dockbarx.applets.clock.gschema.xml
diff --git a/dockx_applets/hello_world.applet b/applets/hello_world/hello_world.applet
similarity index 100%
rename from dockx_applets/hello_world.applet
rename to applets/hello_world/hello_world.applet
diff --git a/dockx_applets/hello_world.py b/applets/hello_world/hello_world.py
similarity index 100%
rename from dockx_applets/hello_world.py
rename to applets/hello_world/hello_world.py
diff --git a/dockx_applets/org.dockbar.applets.hello-world.gschema.xml b/applets/hello_world/org.dockbarx.applets.hello-world.gschema.xml
similarity index 100%
rename from dockx_applets/org.dockbar.applets.hello-world.gschema.xml
rename to applets/hello_world/org.dockbarx.applets.hello-world.gschema.xml
diff --git a/dockx_applets/namebar_common.py b/applets/namebar/namebar_common.py
similarity index 97%
rename from dockx_applets/namebar_common.py
rename to applets/namebar/namebar_common.py
index 58a18cb..68077a9 100644
--- a/dockx_applets/namebar_common.py
+++ b/applets/namebar/namebar_common.py
@@ -37,12 +37,9 @@ def get_namebar_homedir():
global namebar_appdir
if namebar_appdir is not None:
return namebar_appdir
- homedir = os.environ['HOME']
- default = os.path.join(homedir, '.local', 'share')
- namebar_appdir = os.path.join(
- os.getenv('XDG_DATA_HOME', default),
- 'namebar'
- )
+ homedir = os.environ.get("XDG_DATA_HOME", os.environ.get("HOME", os.path.expanduser('~')))
+ namebar_appdir = os.path.join(homedir, '.local', 'share', "namebar")
+
"""
Migration Path
From "$HOME/.namebar" to "${XDG_DATA_HOME:-$HOME/.local/share}/namebar"
@@ -52,9 +49,7 @@ def get_namebar_homedir():
try:
os.rename(old_appdir, namebar_appdir)
except OSError:
- sys.stderr.write('Could not move dir "%s" to "%s". \
- Move the contents of "%s" to "%s" manually \
- and then remove the first location.'
+ sys.stderr.write('Could not move dir "%s" to "%s". Move the contents of "%s" to "%s" manually and then remove the first location.\n'
% (old_appdir, namebar_appdir, old_appdir, namebar_appdir))
"""
End Migration Path
@@ -453,7 +448,7 @@ def find_themes(self):
# a theme can be loaded
themes = {}
theme_paths = []
- dirs = [os.path.join(os.path.dirname(__file__), "namebar_themes"),
+ dirs = [os.path.join(os.path.dirname(__file__), "themes"),
os.path.join(get_namebar_homedir(), "themes")]
for dir in dirs:
if os.path.exists(dir):
diff --git a/dockx_applets/namebar_window_buttons.applet b/applets/namebar/namebar_window_buttons.applet
similarity index 100%
rename from dockx_applets/namebar_window_buttons.applet
rename to applets/namebar/namebar_window_buttons.applet
diff --git a/dockx_applets/namebar_window_buttons.py b/applets/namebar/namebar_window_buttons.py
similarity index 98%
rename from dockx_applets/namebar_window_buttons.py
rename to applets/namebar/namebar_window_buttons.py
index 4a86493..5c3010e 100644
--- a/dockx_applets/namebar_window_buttons.py
+++ b/applets/namebar/namebar_window_buttons.py
@@ -29,11 +29,8 @@
import dockbarx.i18n
_ = dockbarx.i18n.language.gettext
-from pathlib import Path
-file = Path(__file__).resolve()
-parent, root = file.parent, file.parents[1]
-sys.path.append(str(root))
-from applets.namebar_common import get_namebar_homedir, create_context_menu, PrefDialog, Theme
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+from namebar.namebar_common import get_namebar_homedir, create_context_menu, PrefDialog, Theme
try:
action_minimize = Wnck.WindowType.ACTION_MINIMIZE
@@ -149,7 +146,7 @@ def find_themes(self):
# a theme can be loaded
themes = {}
theme_paths = []
- dirs = [os.path.join(os.path.dirname(__file__), "namebar_themes"),
+ dirs = [os.path.join(os.path.dirname(__file__), "themes"),
os.path.join(get_namebar_homedir(), "themes")]
for dir in dirs:
if os.path.exists(dir):
diff --git a/dockx_applets/namebar_window_title.applet b/applets/namebar/namebar_window_title.applet
similarity index 100%
rename from dockx_applets/namebar_window_title.applet
rename to applets/namebar/namebar_window_title.applet
diff --git a/dockx_applets/namebar_window_title.py b/applets/namebar/namebar_window_title.py
similarity index 98%
rename from dockx_applets/namebar_window_title.py
rename to applets/namebar/namebar_window_title.py
index c8580d6..6cd5530 100644
--- a/dockx_applets/namebar_window_title.py
+++ b/applets/namebar/namebar_window_title.py
@@ -29,11 +29,8 @@
from tarfile import open as taropen
from dockbarx.applets import DockXApplet
-from pathlib import Path
-file = Path(__file__).resolve()
-parent, root = file.parent, file.parents[1]
-sys.path.append(str(root))
-from applets.namebar_common import create_context_menu, PrefDialog
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+from namebar.namebar_common import create_context_menu, PrefDialog
class WindowTitleApplet(DockXApplet):
def __init__(self, dbx_dict):
diff --git a/dockx_applets/org.dockbar.applets.namebar.gschema.xml b/applets/namebar/org.dockbarx.applets.namebar.gschema.xml
similarity index 100%
rename from dockx_applets/org.dockbar.applets.namebar.gschema.xml
rename to applets/namebar/org.dockbarx.applets.namebar.gschema.xml
diff --git a/dockx_applets/namebar_themes/Dust-ish.tar.gz b/applets/namebar/themes/Dust-ish.tar.gz
similarity index 100%
rename from dockx_applets/namebar_themes/Dust-ish.tar.gz
rename to applets/namebar/themes/Dust-ish.tar.gz
diff --git a/dockx_applets/namebar_themes/Human-ish.tar.gz b/applets/namebar/themes/Human-ish.tar.gz
similarity index 100%
rename from dockx_applets/namebar_themes/Human-ish.tar.gz
rename to applets/namebar/themes/Human-ish.tar.gz
diff --git a/dockx_applets/namebar_themes/New Wave-ish.tar.gz b/applets/namebar/themes/New Wave-ish.tar.gz
similarity index 100%
rename from dockx_applets/namebar_themes/New Wave-ish.tar.gz
rename to applets/namebar/themes/New Wave-ish.tar.gz
diff --git a/DockX.desktop b/data/DockX.desktop
similarity index 100%
rename from DockX.desktop
rename to data/DockX.desktop
diff --git a/dbx_preference.desktop b/data/dbx_preference.desktop
similarity index 100%
rename from dbx_preference.desktop
rename to data/dbx_preference.desktop
diff --git a/icons/hicolor/128x128/apps/dockbarx.png b/data/icons/hicolor/128x128/apps/dockbarx.png
similarity index 100%
rename from icons/hicolor/128x128/apps/dockbarx.png
rename to data/icons/hicolor/128x128/apps/dockbarx.png
diff --git a/icons/hicolor/16x16/apps/dockbarx.png b/data/icons/hicolor/16x16/apps/dockbarx.png
similarity index 100%
rename from icons/hicolor/16x16/apps/dockbarx.png
rename to data/icons/hicolor/16x16/apps/dockbarx.png
diff --git a/icons/hicolor/22x22/apps/dockbarx.png b/data/icons/hicolor/22x22/apps/dockbarx.png
similarity index 100%
rename from icons/hicolor/22x22/apps/dockbarx.png
rename to data/icons/hicolor/22x22/apps/dockbarx.png
diff --git a/icons/hicolor/24x24/apps/dockbarx.png b/data/icons/hicolor/24x24/apps/dockbarx.png
similarity index 100%
rename from icons/hicolor/24x24/apps/dockbarx.png
rename to data/icons/hicolor/24x24/apps/dockbarx.png
diff --git a/icons/hicolor/36x36/apps/dockbarx.png b/data/icons/hicolor/36x36/apps/dockbarx.png
similarity index 100%
rename from icons/hicolor/36x36/apps/dockbarx.png
rename to data/icons/hicolor/36x36/apps/dockbarx.png
diff --git a/icons/hicolor/48x48/apps/dockbarx.png b/data/icons/hicolor/48x48/apps/dockbarx.png
similarity index 100%
rename from icons/hicolor/48x48/apps/dockbarx.png
rename to data/icons/hicolor/48x48/apps/dockbarx.png
diff --git a/icons/hicolor/64x64/apps/dockbarx.png b/data/icons/hicolor/64x64/apps/dockbarx.png
similarity index 100%
rename from icons/hicolor/64x64/apps/dockbarx.png
rename to data/icons/hicolor/64x64/apps/dockbarx.png
diff --git a/icons/hicolor/72x72/apps/dockbarx.png b/data/icons/hicolor/72x72/apps/dockbarx.png
similarity index 100%
rename from icons/hicolor/72x72/apps/dockbarx.png
rename to data/icons/hicolor/72x72/apps/dockbarx.png
diff --git a/icons/hicolor/96x96/apps/dockbarx.png b/data/icons/hicolor/96x96/apps/dockbarx.png
similarity index 100%
rename from icons/hicolor/96x96/apps/dockbarx.png
rename to data/icons/hicolor/96x96/apps/dockbarx.png
diff --git a/org.dockbar.dockbarx.gschema.xml b/data/org.dockbarx.dockbarx.gschema.xml
similarity index 100%
rename from org.dockbar.dockbarx.gschema.xml
rename to data/org.dockbarx.dockbarx.gschema.xml
diff --git a/po-themes/bg.po b/data/po-themes/bg.po
similarity index 100%
rename from po-themes/bg.po
rename to data/po-themes/bg.po
diff --git a/po-themes/cs.po b/data/po-themes/cs.po
similarity index 100%
rename from po-themes/cs.po
rename to data/po-themes/cs.po
diff --git a/po-themes/da.po b/data/po-themes/da.po
similarity index 100%
rename from po-themes/da.po
rename to data/po-themes/da.po
diff --git a/po-themes/de.po b/data/po-themes/de.po
similarity index 100%
rename from po-themes/de.po
rename to data/po-themes/de.po
diff --git a/po-themes/dockbarx-themes.pot b/data/po-themes/dockbarx-themes.pot
similarity index 100%
rename from po-themes/dockbarx-themes.pot
rename to data/po-themes/dockbarx-themes.pot
diff --git a/po-themes/el.po b/data/po-themes/el.po
similarity index 100%
rename from po-themes/el.po
rename to data/po-themes/el.po
diff --git a/po-themes/en_GB.po b/data/po-themes/en_GB.po
similarity index 100%
rename from po-themes/en_GB.po
rename to data/po-themes/en_GB.po
diff --git a/po-themes/es.po b/data/po-themes/es.po
similarity index 100%
rename from po-themes/es.po
rename to data/po-themes/es.po
diff --git a/po-themes/eu.po b/data/po-themes/eu.po
similarity index 100%
rename from po-themes/eu.po
rename to data/po-themes/eu.po
diff --git a/po-themes/fi.po b/data/po-themes/fi.po
similarity index 100%
rename from po-themes/fi.po
rename to data/po-themes/fi.po
diff --git a/po-themes/fr.po b/data/po-themes/fr.po
similarity index 100%
rename from po-themes/fr.po
rename to data/po-themes/fr.po
diff --git a/po-themes/hu.po b/data/po-themes/hu.po
similarity index 100%
rename from po-themes/hu.po
rename to data/po-themes/hu.po
diff --git a/po-themes/it.po b/data/po-themes/it.po
similarity index 100%
rename from po-themes/it.po
rename to data/po-themes/it.po
diff --git a/po-themes/ja.po b/data/po-themes/ja.po
similarity index 100%
rename from po-themes/ja.po
rename to data/po-themes/ja.po
diff --git a/po-themes/ko.po b/data/po-themes/ko.po
similarity index 100%
rename from po-themes/ko.po
rename to data/po-themes/ko.po
diff --git a/po-themes/lt.po b/data/po-themes/lt.po
similarity index 100%
rename from po-themes/lt.po
rename to data/po-themes/lt.po
diff --git a/po-themes/nl.po b/data/po-themes/nl.po
similarity index 100%
rename from po-themes/nl.po
rename to data/po-themes/nl.po
diff --git a/po-themes/pl.po b/data/po-themes/pl.po
similarity index 100%
rename from po-themes/pl.po
rename to data/po-themes/pl.po
diff --git a/po-themes/pt_BR.po b/data/po-themes/pt_BR.po
similarity index 100%
rename from po-themes/pt_BR.po
rename to data/po-themes/pt_BR.po
diff --git a/po-themes/ro.po b/data/po-themes/ro.po
similarity index 100%
rename from po-themes/ro.po
rename to data/po-themes/ro.po
diff --git a/po-themes/ru.po b/data/po-themes/ru.po
similarity index 100%
rename from po-themes/ru.po
rename to data/po-themes/ru.po
diff --git a/po-themes/sk.po b/data/po-themes/sk.po
similarity index 100%
rename from po-themes/sk.po
rename to data/po-themes/sk.po
diff --git a/po-themes/sv.po b/data/po-themes/sv.po
similarity index 100%
rename from po-themes/sv.po
rename to data/po-themes/sv.po
diff --git a/po-themes/th.po b/data/po-themes/th.po
similarity index 100%
rename from po-themes/th.po
rename to data/po-themes/th.po
diff --git a/po-themes/uk.po b/data/po-themes/uk.po
similarity index 100%
rename from po-themes/uk.po
rename to data/po-themes/uk.po
diff --git a/po-themes/zh_CN.po b/data/po-themes/zh_CN.po
similarity index 100%
rename from po-themes/zh_CN.po
rename to data/po-themes/zh_CN.po
diff --git a/po-themes/zh_TW.po b/data/po-themes/zh_TW.po
similarity index 100%
rename from po-themes/zh_TW.po
rename to data/po-themes/zh_TW.po
diff --git a/po/ar.po b/data/po/ar.po
similarity index 100%
rename from po/ar.po
rename to data/po/ar.po
diff --git a/po/bg.po b/data/po/bg.po
similarity index 100%
rename from po/bg.po
rename to data/po/bg.po
diff --git a/po/cs.po b/data/po/cs.po
similarity index 100%
rename from po/cs.po
rename to data/po/cs.po
diff --git a/po/da.po b/data/po/da.po
similarity index 100%
rename from po/da.po
rename to data/po/da.po
diff --git a/po/de.po b/data/po/de.po
similarity index 100%
rename from po/de.po
rename to data/po/de.po
diff --git a/po/dockbarx.pot b/data/po/dockbarx.pot
similarity index 100%
rename from po/dockbarx.pot
rename to data/po/dockbarx.pot
diff --git a/po/el.po b/data/po/el.po
similarity index 100%
rename from po/el.po
rename to data/po/el.po
diff --git a/po/en_GB.po b/data/po/en_GB.po
similarity index 100%
rename from po/en_GB.po
rename to data/po/en_GB.po
diff --git a/po/es.po b/data/po/es.po
similarity index 100%
rename from po/es.po
rename to data/po/es.po
diff --git a/po/eu.po b/data/po/eu.po
similarity index 100%
rename from po/eu.po
rename to data/po/eu.po
diff --git a/po/fi.po b/data/po/fi.po
similarity index 100%
rename from po/fi.po
rename to data/po/fi.po
diff --git a/po/fr.po b/data/po/fr.po
similarity index 100%
rename from po/fr.po
rename to data/po/fr.po
diff --git a/po/hu.po b/data/po/hu.po
similarity index 100%
rename from po/hu.po
rename to data/po/hu.po
diff --git a/po/id.po b/data/po/id.po
similarity index 100%
rename from po/id.po
rename to data/po/id.po
diff --git a/po/it.po b/data/po/it.po
similarity index 100%
rename from po/it.po
rename to data/po/it.po
diff --git a/po/ja.po b/data/po/ja.po
similarity index 100%
rename from po/ja.po
rename to data/po/ja.po
diff --git a/po/ko.po b/data/po/ko.po
similarity index 100%
rename from po/ko.po
rename to data/po/ko.po
diff --git a/po/lt.po b/data/po/lt.po
similarity index 100%
rename from po/lt.po
rename to data/po/lt.po
diff --git a/po/nl.po b/data/po/nl.po
similarity index 100%
rename from po/nl.po
rename to data/po/nl.po
diff --git a/po/pl.po b/data/po/pl.po
similarity index 100%
rename from po/pl.po
rename to data/po/pl.po
diff --git a/po/pt.po b/data/po/pt.po
similarity index 100%
rename from po/pt.po
rename to data/po/pt.po
diff --git a/po/pt_BR.po b/data/po/pt_BR.po
similarity index 100%
rename from po/pt_BR.po
rename to data/po/pt_BR.po
diff --git a/po/ro.po b/data/po/ro.po
similarity index 100%
rename from po/ro.po
rename to data/po/ro.po
diff --git a/po/ru.po b/data/po/ru.po
similarity index 100%
rename from po/ru.po
rename to data/po/ru.po
diff --git a/po/sk.po b/data/po/sk.po
similarity index 100%
rename from po/sk.po
rename to data/po/sk.po
diff --git a/po/sv.po b/data/po/sv.po
similarity index 100%
rename from po/sv.po
rename to data/po/sv.po
diff --git a/po/tr.po b/data/po/tr.po
similarity index 100%
rename from po/tr.po
rename to data/po/tr.po
diff --git a/po/uk.po b/data/po/uk.po
similarity index 100%
rename from po/uk.po
rename to data/po/uk.po
diff --git a/po/zh_CN.po b/data/po/zh_CN.po
similarity index 100%
rename from po/zh_CN.po
rename to data/po/zh_CN.po
diff --git a/po/zh_TW.po b/data/po/zh_TW.po
similarity index 100%
rename from po/zh_TW.po
rename to data/po/zh_TW.po
diff --git a/themes/Colors.tar.gz b/data/themes/Colors.tar.gz
similarity index 100%
rename from themes/Colors.tar.gz
rename to data/themes/Colors.tar.gz
diff --git a/themes/Deep.tar.gz b/data/themes/Deep.tar.gz
similarity index 100%
rename from themes/Deep.tar.gz
rename to data/themes/Deep.tar.gz
diff --git a/themes/Glass_DMD.tar.gz b/data/themes/Glass_DMD.tar.gz
similarity index 100%
rename from themes/Glass_DMD.tar.gz
rename to data/themes/Glass_DMD.tar.gz
diff --git a/themes/Magic_trans.tar.gz b/data/themes/Magic_trans.tar.gz
similarity index 100%
rename from themes/Magic_trans.tar.gz
rename to data/themes/Magic_trans.tar.gz
diff --git a/themes/dock/Colors.tar.gz b/data/themes/dock/Colors.tar.gz
similarity index 100%
rename from themes/dock/Colors.tar.gz
rename to data/themes/dock/Colors.tar.gz
diff --git a/themes/dock/Glass_DMD.tar.gz b/data/themes/dock/Glass_DMD.tar.gz
similarity index 100%
rename from themes/dock/Glass_DMD.tar.gz
rename to data/themes/dock/Glass_DMD.tar.gz
diff --git a/themes/dock/Magic_trans.tar.gz b/data/themes/dock/Magic_trans.tar.gz
similarity index 100%
rename from themes/dock/Magic_trans.tar.gz
rename to data/themes/dock/Magic_trans.tar.gz
diff --git a/themes/dock/dbx.tar.gz b/data/themes/dock/dbx.tar.gz
similarity index 100%
rename from themes/dock/dbx.tar.gz
rename to data/themes/dock/dbx.tar.gz
diff --git a/themes/dock/deep.tar.gz b/data/themes/dock/deep.tar.gz
similarity index 100%
rename from themes/dock/deep.tar.gz
rename to data/themes/dock/deep.tar.gz
diff --git a/themes/dock/folded.tar.gz b/data/themes/dock/folded.tar.gz
similarity index 100%
rename from themes/dock/folded.tar.gz
rename to data/themes/dock/folded.tar.gz
diff --git a/themes/dock/glass-dock.tar.gz b/data/themes/dock/glass-dock.tar.gz
similarity index 100%
rename from themes/dock/glass-dock.tar.gz
rename to data/themes/dock/glass-dock.tar.gz
diff --git a/themes/dock/invisible.tar.gz b/data/themes/dock/invisible.tar.gz
similarity index 100%
rename from themes/dock/invisible.tar.gz
rename to data/themes/dock/invisible.tar.gz
diff --git a/themes/dockxyz.tar.gz b/data/themes/dockxyz.tar.gz
similarity index 100%
rename from themes/dockxyz.tar.gz
rename to data/themes/dockxyz.tar.gz
diff --git a/themes/glassified.tar.gz b/data/themes/glassified.tar.gz
similarity index 100%
rename from themes/glassified.tar.gz
rename to data/themes/glassified.tar.gz
diff --git a/themes/popup_styles/Colors.tar.gz b/data/themes/popup_styles/Colors.tar.gz
similarity index 100%
rename from themes/popup_styles/Colors.tar.gz
rename to data/themes/popup_styles/Colors.tar.gz
diff --git a/themes/popup_styles/DMD_Glass.tar.gz b/data/themes/popup_styles/DMD_Glass.tar.gz
similarity index 100%
rename from themes/popup_styles/DMD_Glass.tar.gz
rename to data/themes/popup_styles/DMD_Glass.tar.gz
diff --git a/themes/popup_styles/Elegance.tar.gz b/data/themes/popup_styles/Elegance.tar.gz
similarity index 100%
rename from themes/popup_styles/Elegance.tar.gz
rename to data/themes/popup_styles/Elegance.tar.gz
diff --git a/themes/popup_styles/Magic_trans.tar.gz b/data/themes/popup_styles/Magic_trans.tar.gz
similarity index 100%
rename from themes/popup_styles/Magic_trans.tar.gz
rename to data/themes/popup_styles/Magic_trans.tar.gz
diff --git a/themes/popup_styles/Radiance.tar.gz b/data/themes/popup_styles/Radiance.tar.gz
similarity index 100%
rename from themes/popup_styles/Radiance.tar.gz
rename to data/themes/popup_styles/Radiance.tar.gz
diff --git a/themes/popup_styles/dbx.tar.gz b/data/themes/popup_styles/dbx.tar.gz
similarity index 100%
rename from themes/popup_styles/dbx.tar.gz
rename to data/themes/popup_styles/dbx.tar.gz
diff --git a/themes/popup_styles/deep.tar.gz b/data/themes/popup_styles/deep.tar.gz
similarity index 100%
rename from themes/popup_styles/deep.tar.gz
rename to data/themes/popup_styles/deep.tar.gz
diff --git a/themes/popup_styles/gradent.tar.gz b/data/themes/popup_styles/gradent.tar.gz
similarity index 100%
rename from themes/popup_styles/gradent.tar.gz
rename to data/themes/popup_styles/gradent.tar.gz
diff --git a/themes/popup_styles/old.tar.gz b/data/themes/popup_styles/old.tar.gz
similarity index 100%
rename from themes/popup_styles/old.tar.gz
rename to data/themes/popup_styles/old.tar.gz
diff --git a/themes/popup_styles/square.tar.gz b/data/themes/popup_styles/square.tar.gz
similarity index 100%
rename from themes/popup_styles/square.tar.gz
rename to data/themes/popup_styles/square.tar.gz
diff --git a/themes/sunny-c.tar.gz b/data/themes/sunny-c.tar.gz
similarity index 100%
rename from themes/sunny-c.tar.gz
rename to data/themes/sunny-c.tar.gz
diff --git a/dockbarx/__init__.py b/dockbarx/__init__.py
index 3d2cd56..ecf7241 100644
--- a/dockbarx/__init__.py
+++ b/dockbarx/__init__.py
@@ -1,13 +1,2 @@
#!/usr/bin/python3
-__all__ = ["dockbar",
- "groupbutton",
- "windowbutton",
- "cairowidgets",
- "icon_factory",
- "theme",
- "common",
- "i18n",
- "log",
- "mediabuttons",
- "zg"]
diff --git a/dockbarx/applets.py b/dockbarx/applets.py
index 881a35a..d6680de 100644
--- a/dockbarx/applets.py
+++ b/dockbarx/applets.py
@@ -22,7 +22,7 @@
from gi.repository import Gtk
from gi.repository import Gdk
import os
-import imp
+import importlib.util
import dbus
import weakref
from gi.repository import GObject
@@ -30,7 +30,8 @@
from gi.repository import GLib
from dbus.mainloop.glib import DBusGMainLoop
from .log import logger
-from .common import get_app_homedir, Globals
+from .common import Globals
+from .dirutils import get_app_dirs
from . import i18n
_ = i18n.language.gettext
@@ -128,29 +129,30 @@ def __init__(self):
self.globals = Globals()
def find_applets(self):
- # Reads the applets from /usr/share/dockbarx/applets and
- # ${XDG_DATA_HOME:-$HOME/.local/share}/dockbarx/applets
+ # Reads the applets from DATA_DIRS/dockbarx/applets
# and returns a dict of the applets file names and paths so that a
# applet can be loaded.
self.applets = {}
- home_folder = os.path.expanduser("~")
- applets_folder = os.path.join(get_app_homedir(), "applets")
- dirs = ["/usr/share/dockbarx/applets", applets_folder]
- for dir in dirs:
- if not(os.path.exists(dir) and os.path.isdir(dir)):
- continue
- for f in os.listdir(dir):
- name, ext = os.path.splitext(os.path.split(f)[-1])
- if not(ext.lower() == ".applet"):
- continue
- path = os.path.join(dir, f)
- applet, err = self.read_applet_file(path)
- if err is not None:
- logger.debug("Error: Did not load applet from %s: %s" % (path, err))
- continue
- name = applet["name"]
- applet["dir"] = dir
- self.applets[name] = applet
+ app_dirs = get_app_dirs()
+ applets_dirs = [ os.path.join(d, "applets") for d in app_dirs ]
+ for d in applets_dirs:
+ num_sep = d.count(os.path.sep)
+ for root, dirs, files in os.walk(d):
+ for f in files:
+ name, ext = os.path.splitext(os.path.basename(f))
+ if ext.lower() != ".applet":
+ continue
+ path = os.path.join(root, f)
+ applet, err = self.read_applet_file(path)
+ if err is not None:
+ logger.debug("Error: Did not load applet from %s: %s" % (path, err))
+ continue
+ name = applet["name"]
+ if name not in self.applets:
+ applet["dir"] = root
+ self.applets[name] = applet
+ if num_sep + 1 <= root.count(os.path.sep):
+ del dirs[:]
def read_applet_file(self, path):
try:
@@ -213,7 +215,9 @@ def get(self, name):
iname, ext = os.path.splitext(os.path.split(e)[-1])
path = os.path.join(self.applets[name]["dir"], e)
try:
- applet = imp.load_source(iname, path)
+ spec = importlib.util.spec_from_file_location(iname, path)
+ applet = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(applet)
except:
message = "Error: Could not load applet from %s. " % path
message += "Could not import the script."
diff --git a/dockbarx/cairowidgets.py b/dockbarx/cairowidgets.py
index 3f7147c..78b9dd5 100644
--- a/dockbarx/cairowidgets.py
+++ b/dockbarx/cairowidgets.py
@@ -22,6 +22,7 @@
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GdkX11
+from gi.repository import GdkPixbuf
from math import pi, tan
from xml.sax.saxutils import escape
from gi.repository import GObject
@@ -30,8 +31,10 @@
from gi.repository import PangoCairo
from gi.repository import Pango
import cairo
+import weakref
from .common import Globals, connect, disconnect
+from .common import XWindowPixbuf
from .theme import PopupStyle
from .log import logger
@@ -39,9 +42,11 @@
class CairoAppButton(Gtk.EventBox):
def __init__(self, surface=None):
- GObject.GObject.__init__(self)
+ super().__init__()
self.set_visible_window(False)
+ self.set_app_paintable(True)
self.area = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
+ self.area.set_app_paintable(True)
self.add(self.area)
self.area.show()
self.globals = Globals()
@@ -58,20 +63,22 @@ def __init__(self, surface=None):
self.connect("size_allocate", self.on_size_allocate)
def update(self, surface=None):
- a = self.area.get_allocation()
if surface is not None:
self.surface = surface
self.queue_draw()
def on_draw(self, widget, ctx):
if self.surface is not None:
- a = self.get_allocation()
+ sf = self.get_scale_factor()
+ ctx.save()
+ ctx.scale(1 / sf, 1 / sf)
ctx.set_source_surface(self.surface, 0, 0)
ctx.paint()
for surface in (self.badge, self.progress_bar):
if surface is not None:
ctx.set_source_surface(surface, 0, 0)
ctx.paint()
+ ctx.restore()
def on_size_allocate(self, widget, allocation):
if self.badge:
@@ -86,6 +93,9 @@ def make_badge(self, text):
return
self.badge_text = text
a = self.area.get_allocation()
+ sf = self.get_scale_factor()
+ a.width *= sf
+ a.height *= sf
self.badge = cairo.ImageSurface(cairo.FORMAT_ARGB32, a.width, a.height)
ctx = cairo.Context(self.badge)
layout = PangoCairo.create_layout(ctx)
@@ -150,6 +160,9 @@ def make_progress_bar(self, progress):
return
self.progress = progress
a = self.area.get_allocation()
+ sf = self.get_scale_factor()
+ a.width *= sf
+ a.height *= sf
x = max(0.1 * a.width, 2)
y = max(0.15 * a.height, 3)
w = min(max (0.60 * a.width, 20), a.width - 2 * x)
@@ -234,8 +247,8 @@ def pointer_is_inside(self):
class CairoSmallButton(Gtk.Button):
__gsignals__={"draw": "override"}
def __init__(self, width, height=None):
- GObject.GObject.__init__(self)
- self.set_app_paintable(1)
+ super().__init__()
+ self.set_app_paintable(True)
if height is None:
height = width
self.set_size_request(width, height)
@@ -246,6 +259,7 @@ def __init__(self, width, height=None):
self.connect("button-press-event", self.on_button_press_event)
self.connect("button-release-event", self.on_button_release_event)
self.connect("draw", self.on_draw)
+ self.connect("notify::scale-factor", self.on_scale_changed)
def on_enter_notify_event(self, *args):
@@ -262,7 +276,15 @@ def on_button_release_event(self, *args):
def on_draw(self, widget, ctx):
a = self.get_allocation()
- self.draw_button(ctx, 0, 0, a.width, a.height)
+ sf = self.get_scale_factor()
+ ctx.save()
+ ctx.scale(1 / sf, 1 / sf)
+ self.draw_button(ctx, 0, 0, a.width * sf, a.height * sf)
+ ctx.restore()
+
+ def on_scale_changed(self, *args):
+ if self.is_visible():
+ self.queue_draw()
def do_draw(self, ctx):
# This function does nothing and by doing that
@@ -313,19 +335,20 @@ def draw_button(self, ctx, x, y, w, h):
xc = self.popup_style.get("close_button_x_color",
"#FFFFF")
xa = self.popup_style.get("close_button_x_alpha", 0)
+ sf = self.get_scale_factor()
+ r = int(self.popup_style.get("close_button_roundness", 5)) * sf
if button_source is None:
- button_source = self.__make_button_surface(self.size, self.size, bgc, bga, xc, xa)
+ button_source = self.__make_button_surface(self.size * sf, self.size * sf, bgc, bga, xc, xa, r)
if (self.size < w):
- x = (w - self.size) // 2
+ x = (w - self.size * sf) // 2
if (self.size < h):
- y = (h - self.size) // 2
+ y = (h - self.size * sf) // 2
ctx.set_source_surface(button_source, x, y)
ctx.paint()
- def __make_button_surface(self, w, h, bgc, bga, xc, xa):
+ def __make_button_surface(self, w, h, bgc, bga, xc, xa, r):
button_source = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
bctx = cairo.Context(button_source)
- r = int(self.popup_style.get("close_button_roundness", 5))
make_path(bctx, 0, 0, w, h, r)
red, green, blue = parse_color(bgc)
alpha = min(max(float(bga) / 100, 0), 1)
@@ -488,7 +511,7 @@ def __init__(self, orient="down", no_arrow=False, type_="popup"):
if visual is None:
visual = gdk_screen.get_system_visual()
self.set_visual(visual)
- self.set_app_paintable(1)
+ self.set_app_paintable(True)
self.globals = Globals()
self.popup_style = PopupStyle()
self.popup_type = type_
@@ -510,6 +533,7 @@ def __init__(self, orient="down", no_arrow=False, type_="popup"):
self.connect("draw", self.on_draw)
self.connect("enter-notify-event", self.on_enter_notify_event)
self.connect("leave-notify-event", self.on_leave_notify_event)
+ self.connect("notify::scale-factor", self.on_scale_changed)
self.popup_reloaded_sid = self.popup_style.connect(
"popup-style-reloaded",
self.__on_popup_style_reloaded)
@@ -562,6 +586,13 @@ def on_draw(self, widget, ctx):
ctx.set_operator(cairo.OPERATOR_OVER)
self.draw_frame(ctx, w, h)
+ def on_scale_changed(self, *args):
+ # from 1x to 2x, some ctx became broken
+ # self.queue_draw() did not help
+ if self.get_realized():
+ self.unrealize()
+ self.realize()
+
def update_shape(self):
if self.globals.settings["shape_mask"]:
self.set_shape_mask()
@@ -794,7 +825,7 @@ class CairoButton(Gtk.EventBox):
__gsignals__ = {"clicked": (GObject.SIGNAL_RUN_FIRST, None,(int, int, )),}
def __init__(self, label=None, button_type="window_item"):
- GObject.GObject.__init__(self)
+ super().__init__()
self.set_visible_window(False)
self.set_above_child(False)
self.area = CairoArea(label, button_type)
@@ -808,11 +839,13 @@ def __init__(self, label=None, button_type="window_item"):
self.connect("button-press-event", self.on_button_press_event)
def on_leave_notify_event(self, *args):
+ self.area.mouseover = False
if self.mousedown:
self.area.set_pressed_down(False)
self.area.queue_draw()
def on_enter_notify_event(self, *args):
+ self.area.mouseover = True
if self.mousedown:
self.area.set_pressed_down(True)
self.area.queue_draw()
@@ -865,23 +898,23 @@ def pointer_is_inside(self):
class CairoArea(Gtk.Bin):
def __init__(self, text=None, area_type="window_item"):
+ super().__init__()
self.type = area_type
self.text = text
- Gtk.Bin.__init__(self)
- GObject.GObject.__init__(self)
self.popup_style = PopupStyle()
lrp = int(self.popup_style.get("%s_lr_padding" % self.type,
5))
tdp = int(self.popup_style.get("%s_td_padding" % self.type,
5))
self.set_padding(tdp, tdp, lrp, lrp)
- self.set_app_paintable(1)
+ self.set_app_paintable(True)
self.globals = Globals()
self.highlighted = False
self.pressed_down = False
self.active_window = False
self.needs_attention = False
self.minimized = False
+ self.mouseover = False
if text:
self.label = Gtk.Label()
self.add(self.label)
@@ -926,14 +959,11 @@ def set_label_color(self, color):
def on_draw(self, widget, ctx):
a = self.get_allocation()
- mx , my = self.get_pointer()
- highlighted = self.highlighted or \
- (mx >= 0 and mx < a.width and my >= 0 and my < a.height)
if self.needs_attention:
self.draw_type_frame(ctx, 0, 0, a.width, a.height, "needs_attention_item")
if self.active_window:
self.draw_type_frame(ctx, 0, 0, a.width, a.height, "active_item")
- if highlighted:
+ if self.mouseover or self.highlighted:
self.draw_frame(ctx, 0, 0, a.width, a.height)
return
@@ -1023,8 +1053,7 @@ def pointer_is_inside(self):
mx,my = self.get_pointer()
a = self.get_allocation()
- if mx >= 0 and mx < a.width \
- and my >= 0 and my < a.height:
+ if self.mouseover:
# Mouse pointer is inside the "rectangle"
# but check if it's still outside the rounded corners
x = None
@@ -1089,7 +1118,6 @@ class CairoToggleMenu(Gtk.Box):
def __init__(self, label=None, show_menu=False):
super().__init__(orientation=Gtk.Orientation.VERTICAL)
- GObject.GObject.__init__(self)
self.globals = Globals()
self.set_spacing(0)
@@ -1140,8 +1168,7 @@ class CairoVBox(Gtk.Box):
def __init__(self, label=None, show_menu=False):
super().__init__(orientation=Gtk.Orientation.VERTICAL)
- GObject.GObject.__init__(self)
- self.set_app_paintable(1)
+ self.set_app_paintable(True)
self.globals = Globals()
self.popup_style = PopupStyle()
self.connect("draw", self.on_draw)
@@ -1163,30 +1190,129 @@ def draw_frame(self, ctx, x, y, w, h):
ctx.stroke()
class CairoPreview(Gtk.Image):
- def __init__(self):
- GObject.GObject.__init__(self)
- GLib.timeout_add(100, self.draw)
- self.connect("visibility-notify-event", self.on_visibility_notify_event)
+ def __init__(self, window):
+ super().__init__()
+ self.window_r = weakref.ref(window)
+ self.globals = Globals()
+ self.last_snapshot = None
+ self.set_app_paintable(True)
self.connect("draw", self.on_draw)
+ self.connect("notify::scale-factor", self.on_scale_changed)
+ self.connect("notify::visible", self.on_visibility_changed)
- def draw(self):
- if self.get_window() is None:
- return True
- a = self.get_allocation()
- ctx = self.get_window().cairo_create()
- ctx.rectangle(a.x, a.y, a.width, a.height)
- ctx.clip()
- ctx.set_operator(cairo.OPERATOR_SOURCE)
- ctx.set_source_rgba(1,1,1,0)
+ def on_draw(self, widget, ctx):
+ window = self.window_r()
+ w, h = self.get_size_request()
+ sf = self.get_scale_factor()
+ w, h = w * sf, h * sf
+ pixbuf = self.get_pixbuf(window, w, h)
+ draw_icon = False
+ if pixbuf is None:
+ if self.globals.settings["preview_keep"] and self.last_snapshot is not None:
+ pixbuf = self.last_snapshot
+ if pixbuf.get_width() != w or pixbuf.get_height() != h:
+ pixbuf = pixbuf.scale_simple(w, h, GdkPixbuf.InterpType.BILINEAR)
+ else:
+ pixbuf = self.get_icon_pixbuf(window, sf)
+ draw_icon = True
+ pixbuf = CairoMiniIcon.graying_icon(pixbuf)
+ ctx.save()
+ ctx.scale(1 / sf, 1 / sf)
+ if not draw_icon:
+ Gdk.cairo_set_source_pixbuf(ctx, pixbuf, 0, 0)
+ else:
+ Gdk.cairo_set_source_pixbuf(ctx, pixbuf, (w - pixbuf.get_width()) // 2, (h - pixbuf.get_height()) // 2)
ctx.paint()
+ ctx.restore()
+ return True
+
+ def on_scale_changed(self, *args):
+ if self.is_visible():
+ self.queue_draw()
+
+ def on_visibility_changed(self, *args):
+ if self.is_visible():
+ self.queue_draw()
+
+ def get_pixbuf(self, window, w, h):
+ p = XWindowPixbuf(window.xid)
+ pixbuf = p.get_snapshot();
+ if pixbuf is None:
+ return None
+ pixbuf = pixbuf.scale_simple(w, h, GdkPixbuf.InterpType.BILINEAR)
+ if pixbuf is not None and self.globals.settings["preview_keep"]:
+ self.last_snapshot = pixbuf
+ return pixbuf
+
+ def get_icon_pixbuf(self, window, sf):
+ pixbuf = window.wnck.get_icon()
+ if sf > 1:
+ w = pixbuf.get_width()
+ h = pixbuf.get_height()
+ p = XWindowPixbuf(window.xid).get_icon(max(w, h) * sf)
+ if p is not None:
+ pixbuf = p.scale_simple(w * sf, h * sf, GdkPixbuf.InterpType.BILINEAR)
+ return pixbuf
+
+
+class CairoMiniIcon(Gtk.Image):
+ def __init__(self, window):
+ super().__init__()
+ self.window_r = weakref.ref(window)
+ self.set_app_paintable(True)
+ self.size = 16
+ self.connect("draw", self.on_draw)
+ self.connect("notify::scale-factor", self.on_scale_changed)
+ self.connect("notify::visible", self.on_visibility_changed)
def on_draw(self, widget, ctx):
- ctx.set_operator(cairo.OPERATOR_SOURCE)
- ctx.set_source_rgba(1,1,1,0)
+ window = self.window_r()
+ sf = self.get_scale_factor()
+ pixbuf = self._get_icon_pixbuf(window, sf)
+ pb_w = pixbuf.get_width()
+ pb_h = pixbuf.get_height()
+ self.size = max(pb_w, pb_h) // sf
+ if window.wnck.is_minimized():
+ pixbuf = self.graying_icon(pixbuf)
+ ctx.save()
+ ctx.scale(1 / sf, 1 / sf)
+ ah = self.get_allocation().height
+ Gdk.cairo_set_source_pixbuf(ctx, pixbuf, 0, (ah * sf - pb_h) // 2)
ctx.paint()
+ ctx.restore()
+ return True
+
+ def on_scale_changed(self, *args):
+ if self.is_visible():
+ self.queue_draw()
+
+ def on_visibility_changed(self, *args):
+ if self.is_visible():
+ self.queue_draw()
+
+ def get_pixel_size(self):
+ return self.size
+
+ def _get_icon_pixbuf(self, window, sf):
+ pixbuf = window.wnck.get_mini_icon()
+ if sf > 1:
+ w = pixbuf.get_width()
+ h = pixbuf.get_height()
+ p = XWindowPixbuf(window.xid).get_icon(max(w, h) * sf)
+ if p is not None:
+ pixbuf = p.scale_simple(w * sf, h * sf, GdkPixbuf.InterpType.BILINEAR)
+ return pixbuf
+
+ @staticmethod
+ def graying_icon(icon):
+ pixbuf = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, True, 8, icon.get_width(), icon.get_height())
+ pixbuf.fill(0x00000000)
+ grayed_icon = pixbuf.copy()
+ icon.composite(pixbuf, 0, 0, pixbuf.get_width(), pixbuf.get_height(),
+ 0, 0, 1, 1, GdkPixbuf.InterpType.BILINEAR, 190)
+ pixbuf.saturate_and_pixelate(grayed_icon, 0.12, False)
+ return grayed_icon
- def on_visibility_notify_event(self, *args):
- self.draw()
def make_path(ctx, x=0, y=0, w=0, h=0, r=6, b=0.5,
arrow_size=0, arrow_direction=None, arrow_position=0):
diff --git a/dockbarx/common.py b/dockbarx/common.py
index b0caf73..cf29ae0 100644
--- a/dockbarx/common.py
+++ b/dockbarx/common.py
@@ -24,6 +24,7 @@
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from gi.repository import Gdk
+from gi.repository import GdkPixbuf
from gi.repository import GObject
from gi.repository import Gio
from gi.repository import GLib
@@ -35,7 +36,9 @@
from .log import logger
import sys
import struct
+import Xlib
from Xlib import display, X
+from PIL import Image
import _thread
from Xlib.ext import record
@@ -97,35 +100,6 @@ def check_program(name):
prog = os.path.join(dir, name)
if os.path.exists(prog): return prog
-appdir = None
-def get_app_homedir():
- global appdir
- if appdir is not None:
- return appdir
- homedir = os.environ['HOME']
- default = os.path.join(homedir, '.local', 'share')
- appdir = os.path.join(
- os.getenv('XDG_DATA_HOME', default),
- 'dockbarx'
- )
- """
- Migration Path
- From "$HOME/.dockbarx" to "${XDG_DATA_HOME:-$HOME/.local/share}/dockbarx"
- """
- old_appdir = os.path.join(homedir, '.dockbarx')
- if os.path.exists(old_appdir) and os.path.isdir(old_appdir):
- try:
- os.rename(old_appdir, appdir)
- except OSError:
- sys.stderr.write(
- "Could not move dir '%s' to '%s'. Move the contents of '%s' to '%s' manually and then remove the first location.\n"
- % (old_appdir, appdir, old_appdir, appdir)
- )
- """
- End Migration Path
- """
- return appdir
-
class Connector():
"""A class to simplify disconnecting of signals"""
@@ -322,7 +296,7 @@ def launch(self, uri=None, command=None):
if file.startswith("file://"):
file = file[7:]
- file = file.replace("%20","\ ")
+ file = file.replace("%20","\\ ")
file = unquote(file)
files.append(file)
@@ -444,6 +418,58 @@ def _notify(self, reply):
# emit signals in main thread
GLib.idle_add(lambda: self.emit("window-update", event.window.id))
+
+class XWindowPixbuf():
+ def __init__(self, xid):
+ self.xid = xid
+
+ def get_snapshot(self):
+ try:
+ xwin = XDisplay.create_resource_object('window', self.xid)
+ # Wnck.Window.is_minimized() may not work with some wine program windows
+ if xwin.get_wm_state().state != Xlib.Xutil.NormalState:
+ return None
+ xwin.composite_redirect_window(Xlib.ext.composite.RedirectAutomatic)
+ pixmap = xwin.composite_name_window_pixmap()
+ xwin.composite_unredirect_window(Xlib.ext.composite.RedirectAutomatic)
+ geo = xwin.get_geometry()
+ image_object = pixmap.get_image(0, 0, geo.width, geo.height, Xlib.X.ZPixmap, 0xffffffff)
+ pixmap.free()
+ except:
+ return None
+ return self._pixmap2pixbuf(image_object.data, (geo.width, geo.height))
+
+ def get_icon(self, size):
+ xatom = XDisplay.get_atom('_NET_WM_ICON')
+ win = XDisplay.create_resource_object('window', self.xid)
+ prop = win.get_full_property(xatom, Xlib.Xatom.CARDINAL)
+ if prop and prop.format == 32:
+ v = prop.value
+ i = 0
+ l = len(v)
+ min_d = None
+ data = None
+ actual_size = None
+ while i < l:
+ w = v[i]
+ h = v[i + 1]
+ d = abs(w * h - size * size)
+ if min_d is None or d < min_d or (d == min_d and w * h > size * size):
+ min_d = d
+ data = v[i + 2 : i + 2 + w * h]
+ actual_size = (w, h)
+ i += 2 + w * h
+ if data is not None:
+ return self._pixmap2pixbuf(data, actual_size)
+ return None
+
+ def _pixmap2pixbuf(self, data, size):
+ im = Image.frombuffer("RGBA", size, data, "raw", "BGRA")
+ data = GLib.Bytes.new(im.tobytes())
+ return GdkPixbuf.Pixbuf.new_from_bytes(data, GdkPixbuf.Colorspace.RGB, True, 8, size[0], size[1], size[0] * 4)
+
+
+
class Opacify():
def __init__(self):
self.opacifier = None
diff --git a/dockbarx/dirutils.py b/dockbarx/dirutils.py
new file mode 100644
index 0000000..c6835be
--- /dev/null
+++ b/dockbarx/dirutils.py
@@ -0,0 +1,70 @@
+#!/usr/bin/python3
+
+# dirutils.py
+#
+# Copyright 2023 Xu Zhen
+#
+# DockBar 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.
+#
+# DockBar 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 .
+
+import os
+import sys
+from collections import OrderedDict
+
+appdir = None
+def get_app_homedir():
+ global appdir
+ if appdir is not None:
+ return appdir
+ home_dir = os.environ.get("HOME", os.path.expanduser('~'))
+ user_dir = os.environ.get("XDG_DATA_HOME", os.path.join(home_dir, '.local', 'share'))
+ appdir = os.path.join(user_dir, "dockbarx")
+
+ """
+ Migration Path
+ From "$HOME/.dockbarx" to "${XDG_DATA_HOME:-$HOME/.local/share}/dockbarx"
+ """
+ old_appdir = os.path.join(home_dir, '.dockbarx')
+ if os.path.exists(old_appdir) and os.path.isdir(old_appdir):
+ try:
+ os.rename(old_appdir, appdir)
+ except OSError:
+ sys.stderr.write(
+ "Could not move dir '%s' to '%s'. Move the contents of '%s' to '%s' manually and then remove the first location.\n"
+ % (old_appdir, appdir, old_appdir, appdir)
+ )
+ """
+ End Migration Path
+ """
+ return appdir
+
+def get_app_dirs():
+ data_dirs = get_data_dirs()
+ app_dirs = [ os.path.join(d, "dockbarx") for d in data_dirs ]
+ return app_dirs
+
+def get_data_dirs(user_first=True):
+ data_dirs = []
+ home_dir = os.environ.get("HOME", os.path.expanduser('~'))
+ user_dir = os.environ.get("XDG_DATA_HOME", os.path.join(home_dir, '.local', 'share'))
+ if user_first:
+ data_dirs.append(user_dir)
+ env = os.environ.get("XDG_DATA_DIRS")
+ if env:
+ data_dirs.extend(env.split(":"))
+ if not user_first:
+ data_dirs.append(user_dir)
+ data_dirs.extend( ( "/usr/local/share", "/usr/share" ) )
+ # dict.fromkeys can be used instead for python 3.7+
+ return list(OrderedDict.fromkeys(data_dirs))
+
diff --git a/dockbarx/dockbar.py b/dockbarx/dockbar.py
index 8344859..44cb611 100644
--- a/dockbarx/dockbar.py
+++ b/dockbarx/dockbar.py
@@ -27,6 +27,7 @@
from gi.repository import GLib
import sys
import os
+import shutil
import dbus
from . import cairowidgets
import weakref
@@ -36,13 +37,14 @@
from .common import *
+from .dirutils import get_app_homedir, get_data_dirs
from .log import logger
from .key_listener import KeyListener
from . import i18n
_ = i18n.language.gettext
-VERSION = "1.0-beta2"
+VERSION = "1.0-beta4"
SPECIAL_RES_CLASSES = {
@@ -446,10 +448,12 @@ def on_size_allocate(self, widget, allocation):
if allocation.height > size:
allocation.height = size
widget.size_allocate(allocation)
+ return
else:
if allocation.width > size:
allocation.width = size
widget.size_allocate(allocation)
+ return
self.calculate_button_size()
self.manage_size_overflow()
@@ -993,6 +997,12 @@ def group_unpinned(self, identifier):
desktop_entry = self.__get_desktop_entry_for_id(app.get_id())
group.set_desktop_entry(desktop_entry)
group.update_name()
+ custom_launcher = os.path.join(get_app_homedir(), "launchers", "%s.desktop"%identifier)
+ if os.path.isfile(custom_launcher):
+ try:
+ os.remove(custom_launcher)
+ except:
+ pass
def __make_groupbutton(self, identifier=None, desktop_entry=None,
pinned=False, index=None, window=None):
@@ -1498,16 +1508,20 @@ def edit_launcher(self, path, identifier):
logger.warning("Error: file %s doesn't exist."%path)
new_path = os.path.join(launcher_dir, os.path.basename(path))
if new_path != path:
- os.system("cp %s %s"%(path, new_path))
+ shutil.copy(path, new_path)
else:
new_path = os.path.join(launcher_dir, "%s.desktop"%identifier)
+ try:
+ mtime = os.path.getmtime(new_path)
+ except OSError:
+ mtime = None
programs = ("gnome-desktop-item-edit",
"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)
+ process, path, new_path, mtime, identifier)
break
else:
editor = DesktopFileEditor()
@@ -1516,7 +1530,7 @@ def edit_launcher(self, path, identifier):
action = editor.run()
if action == Gtk.ResponseType.OK:
editor.save(new_path)
- self.__wait_for_launcher_editor(None, path, new_path, identifier)
+ self.__wait_for_launcher_editor(None, path, new_path, mtime, identifier)
editor.destroy()
def update_pinned_apps_list(self, arg=None):
@@ -1613,15 +1627,9 @@ def __remove_desktop_entry_id_from_list(self, id):
break
def __get_desktop_entry_for_id(self, id):
- # Search for the desktop id first in ~/.local/share/applications
- # and then in XDG_DATA_DIRS/applications
- user_folder = os.environ.get("XDG_DATA_HOME",
- os.path.join(os.path.expanduser("~"),
- ".local", "share"))
- data_folders = os.environ.get("XDG_DATA_DIRS",
- "/usr/local/share/:/usr/share/")
- folders = "%s:%s"%(user_folder, data_folders)
- for folder in folders.split(":"):
+ # Search for the desktop id in DATA_DIRS/applications
+ folders = get_data_dirs()
+ for folder in folders:
dirname = os.path.join(folder, "applications")
basename = id
run = True
@@ -1688,10 +1696,18 @@ def __identifier_dialog(self, identifier=None):
return text
def __wait_for_launcher_editor(self, process,
- old_path, new_path, identifier):
+ old_path, new_path, mtime, identifier):
if process is None or process.poll() != None:
# Launcher editor closed.
if os.path.isfile(new_path):
+ try:
+ if os.path.getmtime(new_path) == mtime:
+ # No modifications
+ if old_path != new_path:
+ os.remove(new_path)
+ return
+ except:
+ return
# Update desktop_entry.
desktop_entry = DesktopEntry(new_path)
if identifier:
diff --git a/dockbarx/groupbutton.py b/dockbarx/groupbutton.py
index 3f7dd87..80ae672 100644
--- a/dockbarx/groupbutton.py
+++ b/dockbarx/groupbutton.py
@@ -478,6 +478,31 @@ def group_icon_changed(self, class_group):
self.button.update_state(force_update=True)
self.button.drag_source_set_icon_pixbuf(self.button.icon_factory.get_icon(32))
+ def update_window_title_ellipsize_mode(self, title_label):
+ labels = [ win.item.label for win in self.get_windows() ]
+ if title_label not in labels:
+ labels.append(title_label)
+ if len(labels) <= 1:
+ return
+ titles = []
+ titles_r = []
+ for label in labels:
+ title = label.get_text()
+ titles.append(title);
+ titles_r.append(title[::-1]);
+ common_end = len(os.path.commonprefix(titles_r))
+ common_start = len(os.path.commonprefix(titles))
+ if common_end > 3 and common_end >= common_start:
+ mode = Pango.EllipsizeMode.END
+ elif common_start > 3:
+ mode = Pango.EllipsizeMode.START
+ else:
+ mode = Pango.EllipsizeMode.MIDDLE
+ for label in labels:
+ label.set_ellipsize(mode)
+
+ def get_scale_factor(self):
+ return self.monitor.get_scale_factor()
#### Opacify
def opacify(self, delay=0):
@@ -920,17 +945,18 @@ def action_select_or_minimize_group(self, widget, event, minimize=True):
unminimized = False
for window in minimized_windows:
ignored = False
+ window_workspace = window.wnck.get_workspace()
# Check if the window is on another workspace
if not window.wnck.is_pinned() \
- and window.wnck.get_workspace() is not None \
- and window.wnck.get_workspace() != active_workspace:
+ and window_workspace is not None \
+ and window_workspace != active_workspace:
if mode == "move":
- ws = screen.get_active_workspace()
- window.wnck.move_to_workspace(ws)
+ window.wnck.move_to_workspace(active_workspace)
else: # mode == "ignore" or "switch"
ignored = True
# Check if the window is on another viewport
- if not window.wnck.is_in_viewport(active_workspace):
+ if active_workspace is not None \
+ and not window.wnck.is_in_viewport(active_workspace):
if mode == "move":
wx, wy, ww, wh = window.wnck.get_geometry()
window.wnck.set_geometry(0,3,
@@ -961,17 +987,18 @@ def action_select_or_minimize_group(self, widget, event, minimize=True):
grtop = False
continue
ignored = False
+ window_workspace = wnck_window.get_workspace()
if not wnck_window.is_pinned() \
- and wnck_window.get_workspace() is not None \
- and active_workspace != wnck_window.get_workspace():
+ and window_workspace is not None \
+ and active_workspace != window_workspace:
if mode == "move":
- ws = screen.get_active_workspace()
- wnck_window.move_to_workspace(ws)
+ wnck_window.move_to_workspace(active_workspace)
moved = True
else: # mode == "ignore" or "switch"
ignored = True
ignorelist.append(self[wnck_window])
- if not wnck_window.is_in_viewport(screen.get_active_workspace()):
+ if active_workspace is not None \
+ and not wnck_window.is_in_viewport(active_workspace):
if mode == "move":
wx, wy, ww, wh = wnck_window.get_geometry()
wnck_window.set_geometry(0,3,wx%screen.get_width(),
@@ -1429,6 +1456,7 @@ def __init__(self, group, size):
self.connect("button-press-event", self.on_button_press_event)
self.connect("scroll-event", self.on_scroll_event)
self.connect("size-allocate", self.on_size_allocate)
+ self.connect("notify::scale-factor", self.on_scale_changed)
self.connect("drag-motion", self.on_drag_motion)
self.connect("drag-leave", self.on_drag_leave)
self.connect("drag-drop", self.on_drag_drop)
@@ -1519,8 +1547,9 @@ def update_state(self, force_update=False):
surface = self.icon_factory.surface_update(state_type, force_update=force_update)
self.state_type = state_type
# Set the button size to the size of the surface
- width = surface.get_width()
- height = surface.get_height()
+ sf = self.get_scale_factor()
+ width = surface.get_width() // sf
+ height = surface.get_height() // sf
if self.get_allocation().width != width or \
self.get_allocation().height != height:
self.set_size_request(width, height)
@@ -1891,7 +1920,7 @@ def on_size_allocate(self, widget, allocation):
# Sends the new size to icon_factory so that a new icon in the right
# size can be found. The icon is then updated.
CairoAppButton.on_size_allocate(self, widget, allocation)
- if self.old_alloc == self.get_allocation():
+ if self.old_alloc == allocation:
return
if not self.manual_size:
# Let's update the size of the icons
@@ -1901,6 +1930,11 @@ def on_size_allocate(self, widget, allocation):
# Update icon geometry
self.set_icongeo()
+ def on_scale_changed(self, *args):
+ # button size should be recalculated
+ self.icon_factory.reset_surfaces()
+ self.update_state(force_update=True)
+
def __reset_locked_popup_position(self):
group = self.group_r()
# If there is a locked popup on a dockbar at bottom, it needs to be
@@ -2180,7 +2214,7 @@ def show_after_delay(self, delay=0, force=False):
# Prepare window preview so they are ready when the popup is shown.
if self.get_child_() == group.window_list and group.window_list.show_previews:
for window in group:
- GLib.idle_add(window.item.set_preview_image)
+ GLib.idle_add(window.item.update_preview_image)
if not delay:
# No delay, show it now.
self.__show(force)
@@ -2194,7 +2228,7 @@ def show(self, force=False):
group = self.group_r()
if self.get_child_() == group.window_list and group.window_list.show_previews:
for window in group:
- GLib.idle_add(window.item.set_preview_image)
+ GLib.idle_add(window.item.update_preview_image)
self.__show(force)
def __show(self, force=False):
@@ -2334,6 +2368,7 @@ def __init__(self, group):
self.overlap_sid = self.globals.connect("locked-list-overlap-changed", self.__set_own_strut)
self.size_allocate_sid = self.connect("size-allocate", self.on_size_allocate)
self.connect("realize", self.__on_realized)
+ self.connect("notify::scale-factor", self.__on_scale_changed)
def show(self):
CairoPopup.show_all(self)
@@ -2444,6 +2479,12 @@ def __on_realized(self, widget):
self.get_window().set_override_redirect(False)
self.__set_own_strut()
+ def __on_scale_changed(self, *args):
+ if self.is_visible():
+ # relocate
+ self.hide()
+ self.show()
+
def destroy(self):
group = self.group_r()
group.locked_popup = None
diff --git a/dockbarx/i18n.py b/dockbarx/i18n.py
index 27168e9..deb57d2 100644
--- a/dockbarx/i18n.py
+++ b/dockbarx/i18n.py
@@ -1,37 +1,34 @@
#!/usr/bin/python3
-import os, sys
+import os
+import sysconfig
import locale
import gettext
+from .dirutils import get_data_dirs
+APP_DOMAIN = "dockbarx"
+THEME_DOMAIN = "dockbarx-themes"
+def find_mo_location(domain):
+ data_dirs = get_data_dirs()
+ for d in data_dirs:
+ locale_dir = os.path.join(d, "locale")
+ if gettext.find(domain, locale_dir):
+ return locale_dir
+ return os.path.join(sysconfig.get_path("data"), "share")
-APP_NAME = "dockbarx"
-
-
-APP_DIR = os.path.join (sys.prefix,
- "share")
-LOCALE_DIR = os.path.join(APP_DIR, "locale")
-
-mo_location = LOCALE_DIR
-
-
-gettext.install (True)
-
-gettext.bindtextdomain (APP_NAME,
- mo_location)
-gettext.textdomain (APP_NAME)
-language = gettext.translation (APP_NAME,
- mo_location,
- fallback = True)
+gettext.install(True)
+app_mo_location = find_mo_location(APP_DOMAIN)
+gettext.bindtextdomain(APP_DOMAIN, app_mo_location)
+gettext.textdomain(APP_DOMAIN)
+language = gettext.translation(APP_DOMAIN, app_mo_location, fallback = True)
theme = None
def load_theme_translation():
global theme
- gettext.bindtextdomain("dockbarx-themes", mo_location)
- theme = gettext.translation("dockbarx-themes",
- mo_location,
- fallback = True)
+ theme_mo_location = find_mo_location(THEME_DOMAIN)
+ gettext.bindtextdomain(THEME_DOMAIN, theme_mo_location)
+ theme = gettext.translation(THEME_DOMAIN, theme_mo_location, fallback = True)
diff --git a/dockbarx/iconfactory.py b/dockbarx/iconfactory.py
index f796819..5591f17 100644
--- a/dockbarx/iconfactory.py
+++ b/dockbarx/iconfactory.py
@@ -34,6 +34,7 @@
from .theme import Theme
from .common import Globals
+from .dirutils import get_data_dirs
from .log import logger
from . import i18n
@@ -71,6 +72,7 @@ class IconFactory():
def __init__(self, group, class_group=None,
desktop_entry=None, identifier=None, size=None):
self.dockbar_r = weakref.ref(group.dockbar_r())
+ self.group_r = weakref.ref(group)
self.theme = Theme()
self.globals = Globals()
self.globals_event = self.globals.connect("color-changed", self.reset_surfaces)
@@ -171,7 +173,6 @@ def surface_update(self, type_ = 0, force_update=False):
del self.temp
gc.collect()
if dnd:
- print
surface = self.__dd_highlight(surface, is_vertical, dnd)
gc.collect()
return surface
@@ -445,12 +446,13 @@ def __command_pixmap_from_self(self, surface, name, content=None):
return surface
def __command_pixmap(self, surface, name, content=None, size=None):
+ sf = self.group_r().get_scale_factor()
if size is not None:
# TODO: Fix for different height and width
w = h = int(round(self.__get_use_size() + \
- self.__process_size(size)))
+ self.__process_size(size))) * sf
elif surface is None:
- w = h = int(round(self.__get_use_size()))
+ w = h = int(round(self.__get_use_size())) * sf
else:
w = surface.get_width()
h = surface.get_height()
@@ -471,7 +473,8 @@ def __command_pixmap(self, surface, name, content=None, size=None):
#### Get icon
def __command_get_icon(self,surface=None, size="0"):
- size = int(self.__get_use_size() + self.__process_size(size))
+ sf = self.group_r().get_scale_factor()
+ size = int(self.__get_use_size() + self.__process_size(size)) * sf
if size <= 0:
# To avoid crashes.
size = 15
@@ -495,7 +498,7 @@ def __command_get_icon(self,surface=None, size="0"):
pbs = pb.scale_simple(w, h, GdkPixbuf.InterpType.BILINEAR)
woffset = round((size - w) / 2.0)
hoffset = round((size - h) / 2.0)
- Gdk.cairo_set_source_pixbuf(ctx, pb, woffset, hoffset)
+ Gdk.cairo_set_source_pixbuf(ctx, pbs, woffset, hoffset)
ctx.paint()
del pb
del pbs
@@ -584,13 +587,8 @@ def __icon_from_file_name(self, icon_name, icon_size = -1):
return None
def __icon_search_in_data_path(self, icon_name, icon_size):
- data_folders = os.environ.get("XDG_DATA_HOME",
- os.path.join(os.path.expanduser("~"),
- ".local/share"))
- data_folders += ":" + os.environ.get("XDG_DATA_DIRS",
- "/usr/local/share/:/usr/share/")
-
- for data_folder in data_folders.split(":"):
+ data_folders = get_data_dirs()
+ for data_folder in data_folders:
if data_folder == "":
continue
paths = (os.path.join(data_folder, "pixmaps", icon_name),
@@ -607,13 +605,14 @@ def __icon_search_in_data_path(self, icon_name, icon_size):
#### Other commands
def __command_clear(self, surface):
+ sf = self.group_r().get_scale_factor()
if self.dockbar_r().orient in ("left", "right"):
w = self.size
h = int(self.size * self.ar)
else:
w = int(self.size * self.ar)
h = self.size
- new = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
+ new = cairo.ImageSurface(cairo.FORMAT_ARGB32, w * sf, h * sf)
ctx = cairo.Context(new)
ctx.set_source_rgba(0, 0, 0)
ctx.set_operator(cairo.OPERATOR_SOURCE)
@@ -622,12 +621,15 @@ def __command_clear(self, surface):
def __command_get_pixmap(self, surface, name):
if surface is None:
+ sf = self.group_r().get_scale_factor()
if self.dockbar_r().orient in ("left", "right"):
width = self.size
height = int(self.size * self.ar)
else:
width = int(self.size * self.ar)
height = self.size
+ width = width * sf
+ height = height * sf
else:
width = surface.get_width()
height = surface.get_height()
@@ -836,10 +838,13 @@ def __command_correct_size(self, surface):
else:
width = int(self.size * self.ar)
height = self.size
+ sf = self.group_r().get_scale_factor()
+ width = width * sf
+ height = height * sf
if surface.get_width() == width and surface.get_height() == height:
return surface
- woffset = round((width - surface.get_width()) / 2.0)
- hoffset = round((height - surface.get_height()) / 2.0)
+ woffset = (width - surface.get_width()) / 2.0
+ hoffset = (height - surface.get_height()) / 2.0
new = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
ctx = cairo.Context(new)
ctx.set_source_surface(surface, woffset, hoffset)
@@ -1017,7 +1022,12 @@ def __get_from_set(self, setname):
def __resize_surface(self, surface, w, h):
im = self.__surface2pil(surface)
- im = im.resize((w, h), Image.ANTIALIAS)
+ try:
+ algo = Image.LANCZOS
+ except AttributeError:
+ # removed in python-pillow 10.0.0
+ algo = Image.ANTIALIAS
+ im = im.resize((w, h), algo)
return self.__pil2surface(im)
def __command_print_size(self, surface):
diff --git a/dockbarx/log.py b/dockbarx/log.py
index 20e59b0..360f82e 100644
--- a/dockbarx/log.py
+++ b/dockbarx/log.py
@@ -21,6 +21,7 @@
import logging.handlers
import os
import sys
+from .dirutils import get_app_homedir
# To avoid infinite recursion in some cases, e.g. out of disk space
logging.raiseExceptions = False
@@ -28,37 +29,7 @@
logging.basicConfig(format="%(message)s", level=logging.DEBUG)
logger = logging.getLogger("DockbarX")
file_handler = None
-
-appdir = None
-def get_app_homedir():
- global appdir
- if appdir is not None:
- return appdir
- homedir = os.environ['HOME']
- default = os.path.join(homedir, '.local', 'share')
- appdir = os.path.join(
- os.getenv('XDG_DATA_HOME', default),
- 'dockbarx'
- )
- """
- Migration Path
- From "$HOME/.dockbarx" to "${XDG_DATA_HOME:-$HOME/.local/share}/dockbarx"
- """
- old_appdir = os.path.join(homedir, '.dockbarx')
- if os.path.exists(old_appdir) and os.path.isdir(old_appdir):
- try:
- os.rename(old_appdir, appdir)
- except OSError:
- sys.stderr.write(
- "Could not move dir '%s' to '%s'. Move the contents of '%s' to '%s' manually and then remove the first location.\n"
- % (old_appdir, appdir, old_appdir, appdir)
- )
- """
- End Migration Path
- """
- return appdir
-
-
+
def log_to_file():
log_dir = os.path.join(get_app_homedir(), "log")
if not os.path.exists(log_dir):
diff --git a/dockbarx/theme.py b/dockbarx/theme.py
index fb087b3..61fdeef 100644
--- a/dockbarx/theme.py
+++ b/dockbarx/theme.py
@@ -29,7 +29,7 @@
import array
from .common import ODict
from .common import Globals
-from .common import get_app_homedir
+from .dirutils import get_app_dirs
from .log import logger
from PIL import Image
@@ -149,18 +149,13 @@ def on_theme_changed(self, arg=None):
self.reload()
def find_themes(self):
- # Reads the themes from $XDG_DATA_DIRS/dockbarx/themes and
- # ${XDG_DATA_HOME:-$HOME/.local/share}/dockbarx/themes
+ # Reads the themes from APP_DIRS/themes
# and returns a dict of the theme names and paths so
# that a theme can be loaded.
themes = {}
theme_paths = []
- theme_folder = os.path.join(get_app_homedir(), "themes")
- data_dirs = os.environ.get("XDG_DATA_DIRS",
- "/usr/local/share/:/usr/share/")
- data_dirs = data_dirs.split(":")
- dirs = [os.path.join(d, "dockbarx/themes") for d in data_dirs]
- dirs.append(theme_folder)
+ app_dirs = get_app_dirs()
+ dirs = [os.path.join(d, "themes") for d in app_dirs]
for dir in dirs:
if os.path.exists(dir) and os.path.isdir(dir):
for f in os.listdir(dir):
@@ -179,10 +174,10 @@ def find_themes(self):
md = Gtk.MessageDialog(None,
Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE,
- _("No working themes found in /usr/share/dockbarx/themes or ~/.local/share/dockbarx/themes"))
+ _("No working themes found in any of these locations:\n\n%s") % "\n".join(dirs))
md.run()
md.destroy()
- raise NoThemesError("No working themes found in /usr/share/dockbarx/themes or ${XDG_DATA_HOME:-$HOME/.local/share}/.dockbarx/themes")
+ raise NoThemesError("No working themes found")
return themes
def reload(self):
@@ -412,18 +407,12 @@ def get(self, key, default=None):
return self.settings.get(key, default)
def find_styles(self):
- # Reads the styles from $XDG_DATA_DIRS/dockbarx/themes/popup_styles and
- # ${XDG_DATA_HOME:-$HOME/.local/share}/dockbarx/themes/popup_styles
+ # Reads the styles from APP_DIRS/themes/popup_styles
# and returns a dict of the style file names and paths so that a
# style can be loaded
styles = {}
- style_paths = []
- style_folder = os.path.join(get_app_homedir(), "themes", "popup_styles")
- data_dirs = os.environ.get("XDG_DATA_DIRS",
- "/usr/local/share/:/usr/share/")
- data_dirs = data_dirs.split(":")
- dirs = [os.path.join(d, "dockbarx/themes/popup_styles") for d in data_dirs]
- dirs.append(style_folder)
+ app_dirs = get_app_dirs()
+ dirs = [os.path.join(d, "themes", "popup_styles") for d in app_dirs]
for dir in dirs:
if os.path.exists(dir) and os.path.isdir(dir):
for f in os.listdir(dir):
@@ -533,12 +522,8 @@ def get_styles(self, theme_name=None):
# For DockbarX preference. This function makes a dict of the names and
# file names of the styles for all styles that can be opened correctly.
styles = {}
- style_folder = os.path.join(get_app_homedir(), "themes", "popup_styles")
- data_dirs = os.environ.get("XDG_DATA_DIRS",
- "/usr/local/share/:/usr/share/")
- data_dirs = data_dirs.split(":")
- dirs = [os.path.join(d, "dockbarx/themes/popup_styles") for d in data_dirs]
- dirs.append(style_folder)
+ app_dirs = get_app_dirs()
+ dirs = [os.path.join(d, "themes", "popup_styles") for d in app_dirs]
for dir in dirs:
if os.path.exists(dir) and os.path.isdir(dir):
for f in os.listdir(dir):
@@ -627,18 +612,12 @@ def get_bg(self, bar, size=None):
return self.resized_bg[bar]
def find_themes(self):
- # Reads the themes from $XDG_DATA_DIRS/dockbarx/themes/dock and
- # ${XDG_DATA_HOME:-$HOME/.local/share}/dockbarx/themes/dock
+ # Reads the themes from APP_DIRS/themes/dock
# and returns a dict of the theme names and paths so
# that a theme can be loaded.
themes = {}
- theme_paths = []
- theme_folder = os.path.join(get_app_homedir(), "themes/dock")
- data_dirs = os.environ.get("XDG_DATA_DIRS",
- "/usr/local/share/:/usr/share/")
- data_dirs = data_dirs.split(":")
- dirs = [os.path.join(d, "dockbarx/themes/dock") for d in data_dirs]
- dirs.append(theme_folder)
+ app_dirs = get_app_dirs()
+ dirs = [os.path.join(d, "themes", "dock") for d in app_dirs]
for dir in dirs:
if os.path.exists(dir) and os.path.isdir(dir):
for f in os.listdir(dir):
@@ -754,12 +733,8 @@ def get_themes(self):
# For DockbarX preference. This function makes a dict of the names and
# file names of the themes for all themes that can be opened correctly.
themes = {}
- theme_folder = os.path.join(get_app_homedir(), "themes", "dock")
- data_dirs = os.environ.get("XDG_DATA_DIRS",
- "/usr/local/share/:/usr/share/")
- data_dirs = data_dirs.split(":")
- dirs = [os.path.join(d, "dockbarx/themes/dock") for d in data_dirs]
- dirs.append(theme_folder)
+ app_dirs = get_app_dirs()
+ dirs = [os.path.join(d, "themes", "dock") for d in app_dirs]
for dir in dirs:
if os.path.exists(dir) and os.path.isdir(dir):
for f in os.listdir(dir):
@@ -840,5 +815,10 @@ def __pil2surface(self, im):
def __resize_surface(self, surface, w, h):
im = self.__surface2pil(surface)
- im = im.resize((w, h), Image.ANTIALIAS)
+ try:
+ algo = Image.LANCZOS
+ except AttributeError:
+ # removed in python-pillow 10.0.0
+ algo = Image.ANTIALIAS
+ im = im.resize((w, h), algo)
return self.__pil2surface(im)
diff --git a/dockbarx/windowbutton.py b/dockbarx/windowbutton.py
index bf4cd43..0defe8d 100644
--- a/dockbarx/windowbutton.py
+++ b/dockbarx/windowbutton.py
@@ -22,7 +22,6 @@
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
gi.require_version('Wnck', '3.0')
@@ -30,12 +29,9 @@
import weakref
import gc
gc.enable()
-import Xlib
-from PIL import Image
from .common import ODict, Globals, Opacify
from .common import opacify, deopacify
-from .common import XDisplay
from .cairowidgets import *
from .log import logger
@@ -109,10 +105,9 @@ def set_active(self, mode):
self.update_preview_later()
def is_on_current_desktop(self):
+ ws = self.wnck.get_workspace()
aws = self.screen.get_active_workspace()
- if (self.wnck.get_workspace() is None or \
- self.wnck.get_workspace() == aws) and \
- self.wnck.is_in_viewport(aws):
+ if ws is None or aws is None or (ws == aws and self.wnck.is_in_viewport(aws)):
return True
else:
return False
@@ -223,7 +218,7 @@ def update_preview_later(self, delay = 100):
def update_preview(self):
self.preview_sid = None
- self.item.take_preview()
+ self.item.update_preview_image()
#### Opacify
def opacify(self):
@@ -251,18 +246,20 @@ def action_select_or_minimize_window(self, widget=None,
t = event.time
else:
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)
- if not self.wnck.is_in_viewport(self.screen.get_active_workspace()):
- win_x,win_y,win_w,win_h = self.wnck.get_geometry()
- self.screen.move_viewport(win_x-(win_x%self.screen.get_width()),
- win_y-(win_y%self.screen.get_height()))
- # Hide popup since mouse movement won't
- # be tracked during compiz move effect
- # which means popup list can be left open.
- group = self.group_r()
- group.popup.hide()
+ ws = self.wnck.get_workspace()
+ aws = self.screen.get_active_workspace()
+ if ws is not None and aws is not None:
+ if aws != ws:
+ ws.activate(t)
+ if not self.wnck.is_in_viewport(aws):
+ win_x,win_y,win_w,win_h = self.wnck.get_geometry()
+ self.screen.move_viewport(win_x-(win_x%self.screen.get_width()),
+ win_y-(win_y%self.screen.get_height()))
+ # Hide popup since mouse movement won't
+ # be tracked during compiz move effect
+ # which means popup list can be left open.
+ group = self.group_r()
+ group.popup.hide()
if self.wnck.is_minimized():
self.wnck.unminimize(t)
elif self.wnck.is_active() and minimize:
@@ -348,13 +345,11 @@ def __init__(self, window, group):
self.close_button.show()
self.label = Gtk.Label()
- self.label.set_ellipsize(Pango.EllipsizeMode.END)
+ self.label.set_ellipsize(Pango.EllipsizeMode.MIDDLE)
self.label.set_halign(Gtk.Align.START)
self.label.set_valign(Gtk.Align.CENTER)
- icon = window.wnck.get_mini_icon()
- self.icon_image = Gtk.Image()
- self.icon_image.set_from_pixbuf(icon)
+ self.icon_image = CairoMiniIcon(window)
self.icon_image.set_margin_start(2)
self.header_box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 4)
@@ -364,7 +359,7 @@ def __init__(self, window, group):
vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
vbox.pack_start(self.header_box, False, False, 0)
- self.preview = Gtk.Image()
+ self.preview = CairoPreview(window)
self.preview.set_halign(Gtk.Align.CENTER)
self.preview.set_valign(Gtk.Align.CENTER)
self.preview.set_margin_top(4)
@@ -450,6 +445,7 @@ def __clear_saved_preview(self, *args):
def __update_label(self, arg=None):
"""Updates the style of the label according to window state."""
window = self.window_r()
+ group = self.group_r()
text = escape(str(window.wnck.get_name()))
if window.wnck.is_minimized():
color = self.globals.colors["color4"]
@@ -458,10 +454,12 @@ def __update_label(self, arg=None):
text = "" + text + ""
self.label.set_text(text)
self.label.set_use_markup(True)
- if self.globals.settings["preview"]:
- # The label should be 140px wide unless there are more room
- # because the preview takes up more.
- label_size = 140
+ self.__set_label_size()
+ group.update_window_title_ellipsize_mode(self.label)
+
+ def __set_label_size(self):
+ if self.preview.get_visible():
+ label_size = self.globals.settings["preview_size"]
else:
label_size = self.globals.settings["window_title_width"]
@@ -471,23 +469,8 @@ def __update_label(self, arg=None):
size += self.icon_image.get_margin_start()
self.header_box.set_size_request(size, -1)
- def __make_minimized_icon(self, icon):
- pixbuf = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, True, 8, icon.get_width(), icon.get_height())
- pixbuf.fill(0x00000000)
- minimized_icon = pixbuf.copy()
- icon.composite(pixbuf, 0, 0, pixbuf.get_width(), pixbuf.get_height(),
- 0, 0, 1, 1, GdkPixbuf.InterpType.BILINEAR, 190)
- pixbuf.saturate_and_pixelate(minimized_icon, 0.12, False)
- return minimized_icon
-
def __update_icon(self):
- window = self.window_r()
- icon = window.wnck.get_mini_icon()
- if window.wnck.is_minimized():
- pixbuf = self.__make_minimized_icon(icon)
- self.icon_image.set_from_pixbuf(pixbuf)
- else:
- self.icon_image.set_from_pixbuf(icon)
+ self.icon_image.queue_draw()
def minimized_changed(self):
window = self.window_r()
@@ -543,48 +526,12 @@ def update_preview_size(self, *args):
self.preview.set_size_request(width, height)
return width, height
- def take_preview(self):
- window = self.window_r()
- try:
- xwin = XDisplay.create_resource_object('window', window.xid)
- # window.wnck.is_minimized() may not work with some wine program windows
- if xwin.get_wm_state().state != Xlib.Xutil.NormalState:
- return None
- xwin.composite_redirect_window(Xlib.ext.composite.RedirectAutomatic)
- pixmap = xwin.composite_name_window_pixmap()
- xwin.composite_unredirect_window(Xlib.ext.composite.RedirectAutomatic)
- geo = xwin.get_geometry()
- image_object = pixmap.get_image(0, 0, geo.width, geo.height, Xlib.X.ZPixmap, 0xffffffff)
- pixmap.free()
- except:
- return None
- im = Image.frombuffer("RGBX", (geo.width, geo.height), image_object.data, "raw", "BGRX").convert("RGB")
- data = im.tobytes()
- data = GLib.Bytes.new(data)
- pixbuf = GdkPixbuf.Pixbuf.new_from_bytes(data, GdkPixbuf.Colorspace.RGB, False, 8, geo.width, geo.height, geo.width * 3)
- w, h = self.preview.get_size_request()
- pixbuf = pixbuf.scale_simple(w, h, GdkPixbuf.InterpType.BILINEAR)
- if self.globals.settings["preview_keep"]:
- self.last_preview = pixbuf
- return pixbuf
-
def set_show_preview(self, show_preview):
- if show_preview:
- if self.group_r().popup.popup_showing:
- self.set_preview_image()
- self.preview.show()
- else:
- self.preview.hide()
-
- def set_preview_image(self):
- pixbuf = self.take_preview()
- if pixbuf is not None:
- self.preview.set_from_pixbuf(pixbuf)
- elif self.globals.settings["preview_keep"] and (self.last_preview is not None):
- self.preview.set_from_pixbuf(self.last_preview)
- else:
- window = self.window_r()
- self.preview.set_from_pixbuf(window.wnck.get_icon())
+ self.preview.set_visible(show_preview)
+ self.__set_label_size()
+
+ def update_preview_image(self):
+ self.preview.queue_draw()
#### Events
def on_enter_notify_event(self, widget, event):
diff --git a/dockx_applets/Creating applets b/dockx_applets/Creating applets
deleted file mode 100644
index 4bf358e..0000000
--- a/dockx_applets/Creating applets
+++ /dev/null
@@ -1,3 +0,0 @@
-An applet for Dockbarx stand alone dock needs to be a python file named dbx_applet_%name%.py, where %name% can be whatever you want. It doesn't have to be the same as the name of the applet you specify within the file.
-
-See dbx_applet_hello_world.py for a simple example or dbx_applet_clock.py for a more complex one.
diff --git a/make_translate_template.sh b/make_translate_template.sh
deleted file mode 100755
index 9e9dce3..0000000
--- a/make_translate_template.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-# Run this to make a dockbarx.pot template file for translation of dockbarx.
-xgettext --language=Python --keyword=_ --output=po/dockbarx.pot --from-code=UTF-8 `find . -name "*.py" -not -path "./build/*"` dockx dbx_preference
diff --git a/mate_panel_applet/dockbarx-applet-menu.xml b/mate_panel_applet/dockbarx-applet-menu.xml
deleted file mode 100644
index 12f10c1..0000000
--- a/mate_panel_applet/dockbarx-applet-menu.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/mate_panel_applet/dockbarx_mate_applet b/mate_panel_applet/dockbarx_mate_applet
deleted file mode 100755
index 72d973f..0000000
--- a/mate_panel_applet/dockbarx_mate_applet
+++ /dev/null
@@ -1,140 +0,0 @@
-#!/usr/bin/python3
-
-# dockbarx_mate_applet
-#
-# Copyright 2008, 2009, 2010 Aleksey Shaferov and Matias Sars
-# Copyright 2017 Alexey Hohlov
-#
-# 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 .
-
-import gi
-gi.require_version("Gtk", "3.0")
-gi.require_version('MatePanelApplet', '4.0')
-from gi.repository import Gtk, GLib
-from gi.repository import MatePanelApplet
-
-try:
- import sys
- import dockbarx.dockbar
- from dockbarx.log import *
-except Exception as e:
- print(e)
- sys.exit(1)
-
-class DockbarMateApplet(object):
- def __init__(self, applet, iid):
- self.applet = applet
- self.size = None
- self.dockbar = dockbarx.dockbar.DockBar(self)
- applet.set_flags(MatePanelApplet.AppletFlags.HAS_HANDLE | \
- MatePanelApplet.AppletFlags.EXPAND_MINOR | \
- MatePanelApplet.AppletFlags.EXPAND_MAJOR)
- orients = {MatePanelApplet.AppletOrient.DOWN: "down",
- MatePanelApplet.AppletOrient.UP: "up",
- MatePanelApplet.AppletOrient.LEFT: "left",
- MatePanelApplet.AppletOrient.RIGHT: "right"}
- self.dockbar.set_orient(orients[applet.get_orient()])
- menu_actions = [("About", Gtk.STOCK_ABOUT, _("About"), None, None, self.dockbar.on_ppm_about),
- ("Pref", Gtk.STOCK_PREFERENCES, _("Preferences"), None, None, self.dockbar.on_ppm_pref),
- ("Reload", Gtk.STOCK_REFRESH, _("Refresh"), None, None, self.dockbar.reload)]
- actiongroup = Gtk.ActionGroup.new("DockBarXAppletActions")
- #actiongroup.set_translation_domain(dockbarx.defs.GETTEXT_PACKAGE)
- actiongroup.add_actions(menu_actions, None)
- applet.setup_menu_from_file("/usr/share/mate-panel/ui/dockbarx-applet-menu.xml", actiongroup)
-
- # Set the applet coordinates to be way ofscreen until they've
- # been set correctly by a size allocate call.
- self.applet_origin_x = -1000
- self.applet_origin_y = -1000
- applet.connect("delete-event", self.__cleanup)
- applet.show_all()
-
- # Most of initializion must happen after dockbarx is
- # realized since python mateapplets crash if it
- # takes too long to realize.
- GLib.idle_add(self.__load_on_realized)
-
- def __load_on_realized(self):
- # Wait while gtk events are pending.
- while Gtk.events_pending():
- Gtk.main_iteration()
- # Load DockbarX.
- self.dockbar.load()
- # Add it to the applet.
- self.applet.add(self.dockbar.get_container())
- self.applet.show_all()
- # Connect events.
- self.applet.connect("size-allocate", self.__on_applet_size_alloc)
- #self.applet.connect("change_background", self.__on_change_background)
- self.applet.connect("change-orient", self.__on_change_orient)
-
- def __on_applet_size_alloc(self, widget, allocation):
- if self.applet.get_orient() in (MatePanelApplet.AppletOrient.DOWN,
- MatePanelApplet.AppletOrient.UP):
- size = allocation.height
- else:
- size = allocation.width
- if self.size != size:
- self.dockbar.set_size(size)
- self.size = size
- if not widget.get_window():
- return
- #x,y = widget.get_window().get_origin()
- x,y = widget.get_window().get_root_coords(0, 0)
- if x == self.applet_origin_x or y == self.applet_origin_y:
- # Nothing moved.
- return
- # Applet and/or panel moved,
- # icon_geo needs to be updated.
- self.applet_origin_x = x
- self.applet_origin_y = y
- self.dockbar.dockbar_moved()
-
- def __on_change_orient(self, arg1, data):
- orients = {MatePanelApplet.AppletOrient.DOWN: "down",
- MatePanelApplet.AppletOrient.UP: "up",
- MatePanelApplet.AppletOrient.LEFT: "left",
- MatePanelApplet.AppletOrient.RIGHT: "right"}
- self.applet.remove(self.dockbar.get_container())
- self.dockbar.set_orient(orients[self.applet.get_orient()])
- self.applet.add(self.dockbar.get_container())
-
- def __on_change_background(self, applet, type, color, pixmap):
- applet.set_style(None)
- rc_style = Gtk.RcStyle()
- applet.modify_style(rc_style)
- if type == MatePanelApplet.AppletBackgroundType.COLOR_BACKGROUND:
- applet.modify_bg(Gtk.StateType.NORMAL, color)
- elif type == MatePanelApplet.AppletBackgroundType.PIXMAP_BACKGROUND:
- style = applet.style
- style.bg_pixmap[Gtk.StateType.NORMAL] = pixmap
- applet.set_style(style)
- return
-
- def readd_container (self, container):
- self.applet.add(container)
- container.show_all()
-
- def __cleanup(self,event):
- if hasattr(self.dockbar, "destroy"):
- self.dockbar.destroy()
-
-
-def applet_factory(applet, iid, data):
- DockbarMateApplet(applet, iid)
- return True
-
-MatePanelApplet.Applet.factory_main("DockbarXAppletFactory", True,
- MatePanelApplet.Applet.__gtype__,
- applet_factory, None)
diff --git a/mate_panel_applet/org.mate.panel.DockbarX.mate-panel-applet b/mate_panel_applet/org.mate.panel.DockbarX.mate-panel-applet
deleted file mode 100644
index 37b7dca..0000000
--- a/mate_panel_applet/org.mate.panel.DockbarX.mate-panel-applet
+++ /dev/null
@@ -1,14 +0,0 @@
-[Applet Factory]
-Id=DockbarXAppletFactory
-InProcess=false
-Location=/usr/lib/mate-panel/dockbarx_mate_applet
-Name=DockBarX Applet
-Description=DockBarX Applet
-
-[DockbarXApplet]
-Name=DockBarX Applet
-Description=DockBarX Applet
-Icon=dockbarx
-MateComponentId=OAFIID:MATE_DockbarXApplet;
-
-
diff --git a/mate_panel_applet/org.mate.panel.applet.DockbarXAppletFactory.service b/mate_panel_applet/org.mate.panel.applet.DockbarXAppletFactory.service
deleted file mode 100644
index fe77ff1..0000000
--- a/mate_panel_applet/org.mate.panel.applet.DockbarXAppletFactory.service
+++ /dev/null
@@ -1,3 +0,0 @@
-[D-BUS Service]
-Name=org.mate.panel.applet.DockbarXAppletFactory
-Exec=/usr/lib/mate-panel/dockbarx_mate_applet
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..0b3061d
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["setuptools", "polib"]
+build-backend = "setuptools.build_meta"
diff --git a/scripts/make_translate_template.sh b/scripts/make_translate_template.sh
new file mode 100755
index 0000000..4ec1e11
--- /dev/null
+++ b/scripts/make_translate_template.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+# Run this to make a dockbarx.pot template file for translation of dockbarx.
+xgettext --language=Python --keyword=_ --output="$(dirname "$0")/../data/po/dockbarx.pot" --from-code=UTF-8 `find "$(dirname "$0")/.." -name "*.py" -not -path "./build/*"` "$(dirname "$0")"/../utils/*
diff --git a/setup.py b/setup.py
index 64b0642..ee653ea 100755
--- a/setup.py
+++ b/setup.py
@@ -17,155 +17,88 @@
# You should have received a copy of the GNU General Public License
# along with dockbar. If not, see .
-from distutils.core import setup
-from distutils.core import setup
-from distutils import cmd
-from distutils.command.install_data import install_data as _install_data
-from distutils.command.build import build as _build
+from setuptools import setup
+from setuptools.command.install import install as _install
+from setuptools.command.build_py import build_py as _build_py
import polib
import os
import sys
-import stat
-VERSION = "1.0-beta2"
-
-class build_trans(cmd.Command):
- description = "Compile .po files into .mo files"
- def initialize_options(self):
- pass
-
- def finalize_options(self):
- pass
-
- def run(self):
+VERSION = "1.0-beta4"
+
+dbx_files = []
+
+def scan_path(file_list, dest, base_path, ext="", exclude_ext=None, fixed_dest=False):
+ files = []
+ for f in os.listdir(base_path):
+ if f == ".git":
+ continue
+ fpath = os.path.join(base_path, f)
+ if os.path.isdir(fpath):
+ instdir = dest if fixed_dest else os.path.join(dest, f)
+ scan_path(file_list, instdir, os.path.join(base_path, f), ext, exclude_ext, fixed_dest)
+ elif os.path.isfile(fpath) and fpath.endswith(ext) and (exclude_ext is None or not fpath.endswith(exclude_ext)):
+ files.append(fpath)
+ if files:
+ file_list.append( ( dest, files ) )
+
+scan_path(dbx_files, "bin", "utils")
+scan_path(dbx_files, "share/applications", "data", ext=".desktop")
+scan_path(dbx_files, "share/dockbarx/applets", "applets", exclude_ext=".gschema.xml")
+scan_path(dbx_files, "share/dockbarx/themes", "data/themes", ext=".tar.gz")
+scan_path(dbx_files, "share/glib-2.0/schemas", os.curdir, ext=".gschema.xml", fixed_dest=True)
+scan_path(dbx_files, "share/icons", "data/icons", ext=".png")
+
+class build_py(_build_py):
+
+ def build_package_data(self):
+ _build_py.build_package_data(self)
+ self.build_trans()
+
+ def build_trans(self):
po_dict = {
- "dockbarx": os.path.join(os.path.dirname(os.curdir), "po"),
- "dockbarx-themes": os.path.join(os.path.dirname(os.curdir), "po-themes")
- }
- for (mo_file, po_dir) in list(po_dict.items()):
- for path, names, filenames in os.walk(po_dir):
- for f in filenames:
- if f.endswith(".po"):
- lang = f[:len(f) - 3]
- src = os.path.join(path, f)
- dest_path = os.path.join("build", "locale", lang, "LC_MESSAGES")
- dest = os.path.join(dest_path, "%s.mo"%mo_file)
- if not os.path.exists(dest_path):
- os.makedirs(dest_path)
- if not os.path.exists(dest):
- print("Compiling %s for %s" % (src, mo_file))
- po = polib.pofile(src);
- po.save_as_mofile(dest)
- else:
- src_mtime = os.stat(src)[8]
- dest_mtime = os.stat(dest)[8]
- if src_mtime > dest_mtime:
- print("Compiling %s for %s" % (src, mo_file))
- po = polib.pofile(src);
- po.save_as_mofile(dest)
-
-class build(_build):
- sub_commands = _build.sub_commands + [("build_trans", None)]
- def run(self):
- _build.run(self)
-
-class install_data(_install_data):
+ os.path.join(os.path.dirname(os.curdir), "data/po") : "dockbarx",
+ os.path.join(os.path.dirname(os.curdir), "data/po-themes") : "dockbarx-themes"
+ }
+ for path in po_dict.keys():
+ for f in os.listdir(path):
+ if f.endswith(".po"):
+ lang = f[:len(f) - 3]
+ src = os.path.join(path, f)
+ domain = po_dict[path]
+ dest_path = os.path.join("build", "locale", lang, "LC_MESSAGES")
+ dest = os.path.join(dest_path, "%s.mo" % domain)
+ if not os.path.exists(dest_path):
+ os.makedirs(dest_path)
+ print("Compiling %s for %s" % (src, domain))
+ po = polib.pofile(src);
+ po.save_as_mofile(dest)
+ scan_path(dbx_files, "share/locale", "build/locale", ext=".mo")
+
+class install(_install):
def run(self):
- for lang in os.listdir("build/locale/"):
- lang_dir = os.path.join("/", "usr", "share",
- "locale", lang, "LC_MESSAGES")
- lang_files = []
- d_file = os.path.join("build", "locale", lang,
- "LC_MESSAGES", "dockbarx.mo")
- dt_file = os.path.join("build", "locale", lang,
- "LC_MESSAGES", "dockbarx-themes.mo")
- if os.path.exists(d_file):
- lang_files.append(d_file)
- if os.path.exists(dt_file):
- lang_files.append(dt_file)
- self.data_files.append( (lang_dir, lang_files) )
- # Scan folders for the right files
- self.scan_path("/usr/share/dockbarx/themes", "themes", ext=".tar.gz")
- self.scan_path("share/icons/", "icons", ext=".png")
- self.scan_path("share/dockbarx/applets/namebar_themes",
- "dockx_applets/namebar_themes",
- ext=".tar.gz")
- _install_data.run(self)
-
- def scan_path(self, install_path, base_path, path="", ext=""):
- files = []
- for f in os.listdir(os.path.join(base_path, path)):
- fpath = os.path.join(base_path, path, f)
- if os.path.isdir(fpath):
- self.scan_path(install_path, base_path,
- os.path.join(path, f), ext)
- elif os.path.isfile(fpath) and fpath.endswith(ext):
- files.append(fpath)
- if files:
- self.data_files.append((os.path.join(install_path, path), files))
+ if self.distribution.data_files is None:
+ self.distribution.data_files = dbx_files
+ else:
+ for d in dbx_files:
+ self.distribution.data_files.append(d)
+ _install.run(self)
cmdclass = {
- "build": build,
- "build_trans": build_trans,
- "install_data": install_data,
+ "build_py": build_py,
+ "install": install,
}
-data_files=[
- ("share/dockbarx/applets", ["dockx_applets/clock.py",
- "dockx_applets/clock.applet",
- "dockx_applets/appindicator.py",
- "dockx_applets/appindicator.applet",
- "dockx_applets/hello_world.py",
- "dockx_applets/hello_world.applet",
- "dockx_applets/battery_status.py",
- "dockx_applets/battery_status.applet",
- "dockx_applets/battery_status_helper.sh",
- "dockx_applets/namebar_common.py",
- "dockx_applets/namebar_window_buttons.applet",
- "dockx_applets/namebar_window_buttons.py",
- "dockx_applets/namebar_window_title.applet",
- "dockx_applets/namebar_window_title.py"]),
- ("bin", ["dbx_preference", "dbx_migrate_settings", "dockx"]),
- ("lib/mate-panel/", ["mate_panel_applet/dockbarx_mate_applet"]),
- ("share/applications/", ["dbx_preference.desktop"]),
- ("share/applications/", ["DockX.desktop"]),
- ("share/glib-2.0/schemas/", ["org.dockbar.dockbarx.gschema.xml",
- "dockx_applets/org.dockbar.applets.clock.gschema.xml",
- "dockx_applets/org.dockbar.applets.hello-world.gschema.xml",
- "dockx_applets/org.dockbar.applets.batterystatus.gschema.xml",
- "dockx_applets/org.dockbar.applets.namebar.gschema.xml"]),
- ("share/dbus-1/services/", ["mate_panel_applet/org.mate.panel.applet.DockbarXAppletFactory.service"]),
- ("share/mate-panel/applets/", ["mate_panel_applet/org.mate.panel.DockbarX.mate-panel-applet"]),
- ("share/mate-panel/ui/", ["mate_panel_applet/dockbarx-applet-menu.xml"]),
- ]
-
-s = setup(name="Dockbarx",
+setup(name="Dockbarx",
version=VERSION,
- description="A dock-ish gnome-applet",
+ description="A dock-ish applet",
author="Aleksey Shaferov and Matias Sars",
url="http://launchpad.net/dockbar/",
packages=["dockbarx"],
- data_files=data_files,
cmdclass=cmdclass
)
-
-if len(sys.argv) == 2 and sys.argv[1] == "install":
- install_data_path = s.command_obj['install'].install_data
- schema_path = os.path.join(install_data_path, "share/glib-2.0/schemas")
- os.system("glib-compile-schemas %s" % schema_path)
- # create sudo policy file for battery_status_helper.sh
- helper_file = os.path.join(install_data_path, 'share/dockbarx/applets/battery_status_helper.sh')
- sudo_policy_file = '/etc/sudoers.d/dockbarx-applet-battery-status-helper'
- try:
- f = open(sudo_policy_file, 'w')
- f.write('ALL ALL=(root) NOPASSWD:%s\n' % helper_file)
- f.close()
- os.chmod(sudo_policy_file, stat.S_IRUSR | stat.S_IRGRP)
- except:
- pass
-
diff --git a/dbx_migrate_settings b/utils/dbx_migrate_settings
similarity index 100%
rename from dbx_migrate_settings
rename to utils/dbx_migrate_settings
diff --git a/dbx_preference b/utils/dbx_preference
similarity index 99%
rename from dbx_preference
rename to utils/dbx_preference
index 59ecadb..a82f966 100755
--- a/dbx_preference
+++ b/utils/dbx_preference
@@ -32,6 +32,7 @@ import signal
from dockbarx.common import *
from dockbarx.applets import DockXApplets
from dockbarx.theme import PopupStyle, DockTheme
+from dockbarx.dirutils import get_app_dirs
import dockbarx.i18n
_ = dockbarx.i18n.language.gettext
@@ -859,21 +860,6 @@ class PrefDialog():
popup_box.pack_start(self.shape_mask_cb, False, True, 5)
- self.preview_size_spinbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
- self.preview_size_spinbox.set_border_width(5)
- spinlabel = Gtk.Label(label=_("Preview size"))
- spinlabel.set_xalign(0)
- spinlabel.set_yalign(0.5)
- adj = Gtk.Adjustment.new(200, 50, 800, 1, 50, 0)
- self.preview_size_spin = Gtk.SpinButton.new(adj, 0.5, 0)
- self.preview_size_spinbox.pack_start(spinlabel, False, True, 0)
- self.preview_size_spinbox.pack_start(self.preview_size_spin,
- False, True, 5)
- adj.connect("value_changed", self.__adjustment_changed, "preview_size")
- self.preview_size_spinbox.show_all()
- self.preview_size_spinbox.set_no_show_all(True)
- popup_box.pack_start(self.preview_size_spinbox, False, True, 5)
-
self.window_title_width_spinbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
self.window_title_width_spinbox.set_border_width(5)
spinlabel = Gtk.Label(label=_("Window title width"))
@@ -936,6 +922,22 @@ class PrefDialog():
vbox.pack_start(self.preview_keep_cb, False, True, 0)
popup_box.pack_start(vbox, False, True, 5)
+ self.preview_size_spinbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
+ self.preview_size_spinbox.set_border_width(5)
+ spinlabel = Gtk.Label(label=_("Preview size"))
+ spinlabel.set_xalign(0)
+ spinlabel.set_yalign(0.5)
+ adj = Gtk.Adjustment.new(200, 50, 800, 1, 50, 0)
+ self.preview_size_spin = Gtk.SpinButton.new(adj, 0.5, 0)
+ self.preview_size_spinbox.pack_start(spinlabel, False, True, 0)
+ self.preview_size_spinbox.pack_start(self.preview_size_spin,
+ False, True, 5)
+ adj.connect("value_changed", self.__adjustment_changed, "preview_size")
+ self.preview_size_spinbox.show_all()
+ self.preview_size_spinbox.set_no_show_all(True)
+ popup_box.pack_start(self.preview_size_spinbox, False, True, 5)
+
+
# Locked list
vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
label1 = Gtk.Label(label="%s"%_("Locked list"))
@@ -1497,18 +1499,13 @@ class PrefDialog():
self.globals.update_colors(None)
def __find_themes(self):
- # Reads the themes from $XDG_DATA_DIRS/dockbarx/themes and
- # ${XDG_DATA_HOME:-$HOME/.local/share}/dockbarx/themes
+ # Reads the themes from APP_DIRS/themes
# and returns a dict of the theme names and paths so
# that a theme can be loaded.
themes = {}
theme_paths = []
- theme_folder = os.path.join(get_app_homedir(), "themes")
- data_dirs = os.environ.get("XDG_DATA_DIRS",
- "/usr/local/share/:/usr/share/")
- data_dirs = data_dirs.split(":")
- dirs = [os.path.join(d, "dockbarx/themes") for d in data_dirs]
- dirs.append(theme_folder)
+ app_dirs = get_app_dirs()
+ dirs = [os.path.join(d, "themes") for d in app_dirs]
for dir in dirs:
if os.path.exists(dir) and os.path.isdir(dir):
for f in os.listdir(dir):
@@ -1525,7 +1522,7 @@ class PrefDialog():
name = str(name)
themes[name] = theme_path
if not themes:
- message = _("No working themes found in /usr/share/dockbarx/themes or ~/.local/share/dockbarx/themes")
+ message = _("No working themes found in any of these locations:\n\n%s") % "\n".join(dirs)
flags = Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT
md = Gtk.MessageDialog(self.dialog,
flags,
@@ -1769,14 +1766,9 @@ class PrefDialog():
self.preview_keep_cb.set_active(self.globals.settings["preview_keep"])
self.preview_keep_cb.set_sensitive(self.globals.settings["preview"])
self.preview_size_spin.set_value(self.globals.settings["preview_size"])
+ self.preview_size_spin.set_sensitive(self.globals.settings["preview"])
self.window_title_width_spin.set_value(
self.globals.settings["window_title_width"])
- if self.globals.settings["preview"]:
- self.preview_size_spinbox.show()
- self.window_title_width_spinbox.hide()
- else:
- self.preview_size_spinbox.hide()
- self.window_title_width_spinbox.show()
# Advanced page stuff
self.ignore_workspace_cb.set_active(
diff --git a/dockx b/utils/dockx
similarity index 96%
rename from dockx
rename to utils/dockx
index ac7dcbd..1acd2ee 100755
--- a/dockx
+++ b/utils/dockx
@@ -554,6 +554,7 @@ class DockX(CairoDockX):
screen = Gdk.Screen.get_default()
screen.connect("size-changed", self.__on_screen_size_changed)
self.connect("size-allocate", self.on_size_allocate)
+ self.connect("notify::scale-factor", self.on_scale_change)
if self.autohide:
self.show_dock()
self.__on_layer_changed()
@@ -583,17 +584,13 @@ class DockX(CairoDockX):
size = max(db_size, int(db_size * rel_size / 100))
o = self.globals.settings["dock/offset"]
if pos == "left":
- x,y, w, h = (l, t + o, size, sh - t - b - o)
- strut = [x + w, 0, 0, 0, y, y + h - 1, 0, 0, 0, 0, 0, 0]
+ x, y, w, h = (l, t + o, size, sh - t - b - o)
elif pos == "right":
- x,y, w, h = (sw - size - r, t + o, size, sh - t - b - o)
- strut = [0, sw - x, 0, 0, 0, 0, y, y + h - 1, 0, 0, 0, 0]
+ x, y, w, h = (sw - size - r, t + o, size, sh - t - b - o)
elif pos == "top":
- x,y, w, h = (l + o, t, sw - l - r - o, size)
- strut = [0, 0, y + h, 0, 0, 0, 0, 0, x, x + w - 1, 0, 0]
+ x, y, w, h = (l + o, t, sw - l - r - o, size)
else:
- x,y, w, h = (l + o, sh - size - b, sw - l - r - o, size)
- strut = [0, 0, 0, sh - y, 0, 0, 0, 0, 0, 0, x, x + w - 1]
+ x, y, w, h = (l + o, sh - size - b, sw - l - r - o, size)
if (centered or cornered) and pos in ("left", "right"):
self.set_size_request(w, -1)
elif (centered or cornered):
@@ -603,10 +600,10 @@ class DockX(CairoDockX):
self.__set_dock_strut(x, y, w, h)
if centered and pos in ("left", "right"):
a = self.get_allocation()
- self.move(x, my + mh // 2 - a.height // 2)
+ self.move(x, my + (mh - a.height) // 2)
elif centered:
a = self.get_allocation()
- self.move(mx + mw // 2 - a.width // 2, y)
+ self.move(mx + (mw - a.width) // 2, y)
else:
self.move(x, y)
self.old_x = x
@@ -740,8 +737,8 @@ class DockX(CairoDockX):
orient = Gtk.Orientation.HORIZONTAL
boxes = [Gtk.Box.new(orient, 0), Gtk.Box.new(orient, 0), Gtk.Box.new(orient, 0)]
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)
+ self.box.set_center_widget(boxes[1])
+ self.box.pack_end(boxes[2], True, True, 0)
else:
boxes = None
return boxes
@@ -795,20 +792,20 @@ class DockX(CairoDockX):
topw.delete_property(XDisplay.get_atom("_NET_WM_STRUT"))
topw.delete_property(XDisplay.get_atom("_NET_WM_STRUT_PARTIAL"))
return
- mr = self.get_monitor_geometry()
s = self.get_screen()
sr = self.get_screen_geometry()
+ sf = self.get_scale_factor()
sw = sr.width
sh = sr.height
- mx, my, mw, mh = mr.x, mr.y, mr.width, mr.height
+ # [ left, right, top, bottom, left_start_y, left_end_y, right_start_y, right_end_y, top_start_x, top_end_x, bottom_start_x, bottom_end_x ]
if self.globals.settings["dock/position"] == "left":
- strut = [x + w, 0, 0, 0, y, y + h - 1, 0, 0, 0, 0, 0, 0]
+ strut = [(x + w) * sf, 0, 0, 0, y * sf, (y + h - 1) * sf, 0, 0, 0, 0, 0, 0]
elif self.globals.settings["dock/position"] == "right":
- strut = [0, sw - x, 0, 0, 0, 0, y, y + h - 1, 0, 0, 0, 0]
+ strut = [0, (sw - x) * sf, 0, 0, 0, 0, y * sf, (y + h - 1) * sf, 0, 0, 0, 0]
elif self.globals.settings["dock/position"] == "top":
- strut = [0, 0, y + h, 0, 0, 0, 0, 0, x, x + w - 1, 0, 0]
+ strut = [0, 0, (y + h) * sf, 0, 0, 0, 0, 0, x * sf, (x + w - 1) * sf, 0, 0]
else:
- strut = [0, 0, 0, sh - y, 0, 0, 0, 0, 0, 0, x, x + w - 1]
+ strut = [0, 0, 0, (sh - y) * sf, 0, 0, 0, 0, 0, 0, x * sf, (x + w - 1) * sf]
strut = list(map(lambda e: 0 if e < 0 else e, strut))
topw = XDisplay.create_resource_object('window',
self.get_toplevel().get_window().get_xid())
@@ -829,8 +826,10 @@ class DockX(CairoDockX):
mr = self.get_monitor_geometry()
mx, my, mw, mh = mr.x, mr.y, mr.width, mr.height
sr = self.get_screen_geometry()
+ sf = self.get_scale_factor()
sw = sr.width
sh = sr.height
+ # [ left, right, top, bottom ]
strut = [mx, sw - (mx + mw), my, sh - (my + mh)]
strut_atom = XDisplay.get_atom('_NET_WM_STRUT')
strut_partial_atom = XDisplay.get_atom('_NET_WM_STRUT_PARTIAL')
@@ -846,6 +845,9 @@ class DockX(CairoDockX):
cl = w.get_wm_class()
if cl and cl[0] == "dockx":
continue
+ for i in range(12):
+ prop1.value[i] = prop1.value[i] // sf
+ # [ left, right, top, bottom, left_start_y, left_end_y, right_start_y, right_end_y, top_start_x, top_end_x, bottom_start_x, bottom_end_x ]
if self.globals.settings["dock/position"] == "left":
if prop1.value[6] < my + mh and \
prop1.value[7] >= my:
@@ -894,7 +896,7 @@ class DockX(CairoDockX):
if cl and cl[0] == "dockx":
continue
for i in range(4):
- strut[i] = max(strut[i], prop2.value[i])
+ strut[i] = max(strut[i], prop2.value[i] // sf)
return strut
def on_size_allocate(self, widget, allocation):
@@ -913,16 +915,16 @@ class DockX(CairoDockX):
mx, my, mw, mh = mr.x, mr.y, mr.width, mr.height
if self.globals.settings["dock/position"] == "left":
x = mx
- y = my + mh // 2 - h // 2
+ y = my + (mh - h) // 2
elif self.globals.settings["dock/position"] == "right":
- x = mx + mw - w
- y = my + mh // 2 - h // 2
+ x = mx + (mw - w)
+ y = my + (mh - h) // 2
elif self.globals.settings["dock/position"] == "top":
- x = mx + mw // 2 - w // 2
+ x = mx + (mw - w) // 2
y = my
else:
- x = mx + mw // 2 - w // 2
- y = my + mh - h
+ x = mx + (mw - w) // 2
+ y = my + (mh - h)
else:
x = self.old_x
y = self.old_y
@@ -934,6 +936,9 @@ class DockX(CairoDockX):
self.__set_dock_strut(x, y, w, h)
self.dockbar.dockbar_moved()
+ def on_scale_change(self, *args):
+ self.position_dock()
+
def show_dock(self):
self.show()
self.position_dock()
@@ -1516,7 +1521,10 @@ class DockXApp(Gtk.Application):
iface = info.lookup_interface("org.dockbar.DockX")
dbus = self.get_dbus_connection()
dbus_path = self.get_dbus_object_path()
- dbus.register_object(dbus_path, iface, self.dbus_method_call, None, None);
+ if hasattr(dbus, "register_object_with_closures2"):
+ dbus.register_object_with_closures2(dbus_path, iface, self.dbus_method_call, None, None);
+ else:
+ dbus.register_object(dbus_path, iface, self.dbus_method_call, None, None);
def dbus_method_call(self, connection, sender, object_path, interface_name, method_name, parameters, invocation):
ret = None