diff --git a/Makefile b/Makefile
index 33ac062..f47c4ba 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
UUID = desk-changer@eric.gach.gmail.com
-VERSION = 35
+VERSION = 36
ifeq ($(strip $(DESTDIR)),)
INSTALLBASE = $(HOME)/.local/share/gnome-shell/extensions
@@ -10,7 +10,10 @@ endif
all: compile-resources compile-schemas
compile-resources:
- glib-compile-resources --target=./$(UUID)/resources/org.gnome.Shell.Extensions.DeskChanger.gresource --sourcedir=./resources ./resources/org.gnome.Shell.Extensions.DeskChanger.gresource.xml
+ glib-compile-resources \
+ --target=./$(UUID)/resources/org.gnome.Shell.Extensions.DeskChanger.gresource \
+ --sourcedir=./resources \
+ ./resources/org.gnome.Shell.Extensions.DeskChanger.gresource.xml
compile-schemas:
glib-compile-schemas ./$(UUID)/schemas/
diff --git a/desk-changer.cmb b/desk-changer.cmb
new file mode 100644
index 0000000..af2320d
--- /dev/null
+++ b/desk-changer.cmb
@@ -0,0 +1,326 @@
+
+
+
+
+ (3,1,None,"resources/ui/prefs/profiles.ui",None,None,None,None,None,None,None),
+ (4,1,None,"resources/ui/prefs/keyboard/page.ui",None,None,None,None,None,None,None),
+ (5,1,None,"resources/ui/prefs/extension.ui",None,None,None,None,None,None,None),
+ (6,1,None,"resources/ui/prefs/daemon.ui",None,None,None,None,None,None,None),
+ (7,1,None,"resources/ui/prefs/about.ui",None,None,None,None,None,None,None),
+ (8,1,None,"resources/ui/prefs/keyboard/shortcutrow.ui",None,None,None,None,None,None,None),
+ (10,1,None,"resources/ui/prefs/daemon/meta_type_row.ui",None,None,None,None,None,None,None),
+ (11,1,None,"resources/ui/dialog/add.ui",None,None,None,None,None,None,None),
+ (12,1,None,"resources/ui/dialog/alert.ui",None,None,None,None,None,None,None),
+ (14,1,None,"resources/ui/dialog/new.ui",None,None,None,None,None,None,None),
+ (15,1,None,"resources/ui/dialog/keybind.ui",None,None,None,None,None,None,None)
+
+
+ (11,"gtk","4.14",None),
+ (14,"gtk","4.10",None),
+ (14,"libadwaita","1.4",None),
+ (15,"gtk","4.10",None),
+ (15,"libadwaita","1.4",None)
+
+
+
+ (3,1,"AdwPreferencesPage","icon-name","view-list-symbolic",None,None,None,None,None,None,None,None,None),
+ (3,1,"AdwPreferencesPage","title","Profiles",1,None,None,None,None,None,None,None,None),
+ (3,2,"AdwPreferencesGroup","title","Locations",None,None,None,None,None,None,None,None,None),
+ (3,8,"AdwPreferencesGroup","title","Profile",None,None,None,None,None,None,None,None,None),
+ (3,9,"AdwComboRow","factory",None,None,None,None,None,14,None,None,None,None),
+ (3,9,"AdwPreferencesRow","title","Edit Profile",None,None,None,None,None,None,None,None,None),
+ (3,19,"GtkBox","spacing","5",None,None,None,None,None,None,None,None,None),
+ (3,19,"GtkWidget","halign","end",None,None,None,None,None,None,None,None,None),
+ (3,19,"GtkWidget","margin-bottom","10",None,None,None,None,None,None,None,None,None),
+ (3,19,"GtkWidget","margin-top","10",None,None,None,None,None,None,None,None,None),
+ (3,20,"GtkButton","label","Add Profile",1,None,None,None,None,None,None,None,None),
+ (3,21,"GtkButton","label","Remove Profile",1,None,None,None,None,None,None,None,None),
+ (3,21,"GtkWidget","sensitive","False",None,None,None,None,None,None,None,None,None),
+ (3,22,"GtkBox","spacing","5",None,None,None,None,None,None,None,None,None),
+ (3,22,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
+ (3,22,"GtkWidget","margin-bottom","10",None,None,None,None,None,None,None,None,None),
+ (3,22,"GtkWidget","margin-top","10",None,None,None,None,None,None,None,None,None),
+ (3,23,"GtkButton","label","Add Item(s)",1,None,None,None,None,None,None,None,None),
+ (3,23,"GtkWidget","halign","start",None,None,None,None,None,None,None,None,None),
+ (3,24,"GtkButton","label","Add Folder(s)",None,None,None,None,None,None,None,None,None),
+ (3,24,"GtkWidget","halign","start",None,None,None,None,None,None,None,None,None),
+ (3,24,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
+ (3,25,"GtkListView","factory",None,None,None,None,None,26,None,None,None,None),
+ (3,25,"GtkListView","model",None,None,None,None,None,27,None,None,None,None),
+ (3,25,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
+ (3,25,"GtkWidget","vexpand-set","True",None,None,None,None,None,None,None,None,None),
+ (3,27,"GtkSingleSelection","autoselect","False",None,None,None,None,None,None,None,None,None),
+ (3,27,"GtkSingleSelection","can-unselect","True",None,None,None,None,None,None,None,None,None),
+ (3,28,"GtkButton","label","Remove Item",1,None,None,None,None,None,None,None,None),
+ (3,28,"GtkWidget","sensitive","False",None,None,None,None,None,None,None,None,None),
+ (4,1,"AdwPreferencesPage","icon-name","input-keyboard-symbolic",None,None,None,None,None,None,None,None,None),
+ (4,1,"AdwPreferencesPage","title","Keyboard",1,None,None,None,None,None,None,None,None),
+ (4,2,"AdwPreferencesGroup","title","Shortcuts",None,None,None,None,None,None,None,None,None),
+ (4,4,"GtkListBox","selection-mode","none",None,None,None,None,None,None,None,None,None),
+ (4,4,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
+ (5,1,"AdwPreferencesPage","icon-name","applications-system-symbolic",None,None,None,None,None,None,None,None,None),
+ (5,1,"AdwPreferencesPage","title","Extension",1,None,None,None,None,None,None,None,None),
+ (5,2,"AdwPreferencesGroup","title","Extension Settings",1,None,None,None,None,None,None,None,None),
+ (5,3,"AdwComboRow","factory",None,None,None,None,None,4,None,None,None,None),
+ (5,3,"AdwPreferencesRow","title","Current Profile",1,None,None,None,None,None,None,None,None),
+ (5,5,"AdwPreferencesRow","title","Notifications",1,None,None,None,None,None,None,None,None),
+ (5,5,"AdwPreferencesRow","use-markup","False",None,None,None,None,None,None,None,None,None),
+ (5,6,"AdwPreferencesRow","title","Icon as Preview",1,None,None,None,None,None,None,None,None),
+ (5,6,"AdwPreferencesRow","use-markup","False",None,None,None,None,None,None,None,None,None),
+ (6,1,"AdwPreferencesPage","icon-name","application-x-executable-symbolic",None,None,None,None,None,None,None,None,None),
+ (6,1,"AdwPreferencesPage","title","Daemon",None,None,None,None,None,None,None,None,None),
+ (6,2,"AdwPreferencesGroup","title","Rotation Options",None,None,None,None,None,None,None,None,None),
+ (6,3,"AdwComboRow","factory",None,None,None,None,None,4,None,None,None,None),
+ (6,3,"AdwPreferencesRow","title","Mode",None,None,None,None,None,None,None,None,None),
+ (6,5,"AdwPreferencesRow","title","Custom Interval",1,None,None,None,None,None,None,None,None),
+ (6,5,"AdwPreferencesRow","use-markup","False",None,None,None,None,None,None,None,None,None),
+ (6,5,"AdwSpinRow","adjustment",None,None,None,None,None,6,None,None,None,None),
+ (6,6,"GtkAdjustment","lower","1.0",None,None,None,None,None,None,None,None,None),
+ (6,6,"GtkAdjustment","page-increment","10.0",None,None,None,None,None,None,None,None,None),
+ (6,6,"GtkAdjustment","step-increment","1.0",None,None,None,None,None,None,None,None,None),
+ (6,6,"GtkAdjustment","upper","86400.0",None,None,None,None,None,None,None,None,None),
+ (6,6,"GtkAdjustment","value","500.0",None,None,None,None,None,None,None,None,None),
+ (6,7,"AdwPreferencesGroup","title","Daemon",1,None,None,None,None,None,None,None,None),
+ (6,8,"AdwPreferencesRow","title","Auto Start",None,None,None,None,None,None,None,None,None),
+ (6,9,"AdwPreferencesRow","title","Remember Profile State",1,None,None,None,None,None,None,None,None),
+ (6,9,"AdwPreferencesRow","use-markup","False",None,None,None,None,None,None,None,None,None),
+ (6,10,"AdwPreferencesRow","title","Currently Running",1,None,None,None,None,None,None,None,None),
+ (6,10,"AdwPreferencesRow","use-markup","False",None,None,None,None,None,None,None,None,None),
+ (6,11,"AdwPreferencesGroup","header-suffix",None,None,None,None,None,15,None,None,None,None),
+ (6,11,"AdwPreferencesGroup","title","Allowed MIME Types",1,None,None,None,None,None,None,None,None),
+ (6,12,"GtkListBox","selection-mode","none",None,None,None,None,None,None,None,None,None),
+ (6,15,"GtkButton","label","Reset",None,None,None,None,None,None,None,None,None),
+ (6,15,"GtkWidget","visible","False",None,None,None,None,None,None,None,None,None),
+ (7,1,"AdwPreferencesPage","icon-name","dialog-information-symbolic",None,None,None,None,None,None,None,None,None),
+ (7,1,"AdwPreferencesPage","title","About",None,None,None,None,None,None,None,None,None),
+ (7,10,"GtkBox","spacing","5",None,None,None,None,None,None,None,None,None),
+ (7,10,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None),
+ (7,10,"GtkWidget","margin-bottom","10",None,None,None,None,None,None,None,None,None),
+ (7,10,"GtkWidget","margin-end","10",None,None,None,None,None,None,None,None,None),
+ (7,10,"GtkWidget","margin-start","10",None,None,None,None,None,None,None,None,None),
+ (7,10,"GtkWidget","margin-top","10",None,None,None,None,None,None,None,None,None),
+ (7,11,"GtkImage","resource","/org/gnome/Shell/Extensions/DeskChanger/icons/wallpaper-icon.svg",None,None,None,None,None,None,None,None,None),
+ (7,11,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
+ (7,11,"GtkWidget","hexpand-set","True",None,None,None,None,None,None,None,None,None),
+ (7,11,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
+ (7,11,"GtkWidget","vexpand-set","True",None,None,None,None,None,None,None,None,None),
+ (7,12,"GtkLabel","label","<b>DeskChanger</b>",None,None,None,None,None,None,None,None,None),
+ (7,12,"GtkLabel","use-markup","True",None,None,None,None,None,None,None,None,None),
+ (7,13,"GtkLabel","label","Version 36",None,None,None,None,None,None,None,None,None),
+ (7,14,"GtkLabel","label","Simple wallpaper changer with multiple profile support. Integrates into the shell by providing it's own panel icon. The daemon is written using gjs and runs independently of the extension as a background process.",None,None,None,None,None,None,None,None,None),
+ (7,14,"GtkLabel","wrap","True",None,None,None,None,None,None,None,None,None),
+ (7,15,"GtkLabel","label","<a href=\"https://github.com/BigE/desk-changer\">https://github.com/BigE/desk-changer</a>",None,None,None,None,None,None,None,None,None),
+ (7,15,"GtkLabel","use-markup","True",None,None,None,None,None,None,None,None,None),
+ (7,16,"GtkLabel","label","<span size=\"small\">This program comes with absolutely no warranty.\nSee the <a href=\"https://opensource.org/licenses/mit-license.php\">The MIT License (MIT)</a> for details.</span>",None,None,None,None,None,None,None,None,None),
+ (7,16,"GtkLabel","use-markup","True",None,None,None,None,None,None,None,None,None),
+ (7,16,"GtkLabel","wrap","True",None,None,None,None,None,None,None,None,None),
+ (8,1,"AdwPreferencesRow","title","Title",None,None,None,None,None,None,None,None,None),
+ (8,1,"AdwPreferencesRow","use-markup","False",None,None,None,None,None,None,None,None,None),
+ (8,1,"GtkListBoxRow","selectable","False",None,None,None,None,None,None,None,None,None),
+ (8,4,"GtkRevealer","child",None,None,None,None,None,5,None,None,None,None),
+ (8,4,"GtkRevealer","transition-type","slide-right",None,None,None,None,None,None,None,None,None),
+ (8,5,"GtkButton","label","Reset",None,None,None,None,None,None,None,None,None),
+ (8,6,"GtkShortcutLabel","disabled-text","Disabled",None,None,None,None,None,None,None,None,None),
+ (10,1,"AdwPreferencesRow","title","META Type",None,None,None,None,None,None,None,None,None),
+ (10,2,"GtkButton","icon-name","user-trash-symbolic",None,None,None,None,None,None,None,None,None),
+ (14,1,"GtkWindow","child",None,None,None,None,None,2,None,None,None,None),
+ (14,1,"GtkWindow","destroy-with-parent","True",None,None,None,None,None,None,None,None,None),
+ (14,1,"GtkWindow","modal","True",None,None,None,None,None,None,None,None,None),
+ (14,1,"GtkWindow","resizable","False",None,None,None,None,None,None,None,None,None),
+ (14,2,"GtkBox","spacing","5",None,None,None,None,None,None,None,None,None),
+ (14,2,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None),
+ (14,2,"GtkWidget","margin-bottom","10",None,None,None,None,None,None,None,None,None),
+ (14,2,"GtkWidget","margin-end","10",None,None,None,None,None,None,None,None,None),
+ (14,2,"GtkWidget","margin-start","10",None,None,None,None,None,None,None,None,None),
+ (14,2,"GtkWidget","margin-top","10",None,None,None,None,None,None,None,None,None),
+ (14,4,"GtkBox","spacing","5",None,None,None,None,None,None,None,None,None),
+ (14,4,"GtkWidget","halign","end",None,None,None,None,None,None,None,None,None),
+ (14,4,"GtkWidget","margin-bottom","10",None,None,None,None,None,None,None,None,None),
+ (14,4,"GtkWidget","margin-end","10",None,None,None,None,None,None,None,None,None),
+ (14,4,"GtkWidget","margin-start","10",None,None,None,None,None,None,None,None,None),
+ (14,4,"GtkWidget","margin-top","10",None,None,None,None,None,None,None,None,None),
+ (14,5,"GtkButton","label","Save",None,None,None,None,None,None,None,None,None),
+ (14,6,"GtkButton","label","Cancel",None,None,None,None,None,None,None,None,None),
+ (15,1,"AdwWindow","content",None,None,None,None,None,2,None,None,None,None),
+ (15,1,"GtkWidget","height-request","300",None,None,None,None,None,None,None,None,None),
+ (15,1,"GtkWidget","width-request","400",None,None,None,None,None,None,None,None,None),
+ (15,1,"GtkWindow","hide-on-close","True",None,None,None,None,None,None,None,None,None),
+ (15,1,"GtkWindow","modal","True",None,None,None,None,None,None,None,None,None),
+ (15,1,"GtkWindow","resizable","False",None,None,None,None,None,None,None,None,None),
+ (15,1,"GtkWindow","title","Set Shortcut",1,None,None,None,None,None,None,None,None),
+ (15,2,"AdwToolbarView","content",None,None,None,None,None,17,None,None,None,None),
+ (15,2,"AdwToolbarView","top-bar-style","raised",None,None,None,None,None,None,None,None,None),
+ (15,3,"AdwHeaderBar","show-end-title-buttons","False",None,None,None,None,None,None,None,None,None),
+ (15,10,"GtkShortcut","action","action(window.close)",None,None,None,None,None,None,None,None,None),
+ (15,10,"GtkShortcut","trigger","Escape",None,None,None,None,None,None,None,None,None),
+ (15,11,"GtkEventController","propagation-phase","capture",None,None,None,None,None,None,None,None,None),
+ (15,12,"GtkButton","label","_Cancel",1,None,None,None,None,None,None,None,None),
+ (15,12,"GtkButton","use-underline","True",None,None,None,None,None,None,None,None,None),
+ (15,12,"GtkWidget","visible","False",None,None,None,None,None,None,None,None,None),
+ (15,15,"GtkButton","label","_Set",None,None,None,None,None,None,None,None,None),
+ (15,15,"GtkButton","use-underline","True",None,None,None,None,None,None,None,None,None),
+ (15,15,"GtkWidget","visible","False",None,None,None,None,None,None,None,None,None),
+ (15,17,"GtkBox","spacing","18",None,None,None,None,None,None,None,None,None),
+ (15,17,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None),
+ (15,17,"GtkWidget","margin-bottom","12",None,None,None,None,None,None,None,None,None),
+ (15,17,"GtkWidget","margin-end","12",None,None,None,None,None,None,None,None,None),
+ (15,17,"GtkWidget","margin-start","12",None,None,None,None,None,None,None,None,None),
+ (15,17,"GtkWidget","margin-top","12",None,None,None,None,None,None,None,None,None),
+ (15,20,"GtkBox","spacing","18",None,None,None,None,None,None,None,None,None),
+ (15,20,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None),
+ (15,20,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
+ (15,25,"GtkBox","spacing","18",None,None,None,None,None,None,None,None,None),
+ (15,25,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None),
+ (15,25,"GtkWidget","margin-bottom","12",None,None,None,None,None,None,None,None,None),
+ (15,25,"GtkWidget","margin-end","12",None,None,None,None,None,None,None,None,None),
+ (15,25,"GtkWidget","margin-start","12",None,None,None,None,None,None,None,None,None),
+ (15,25,"GtkWidget","margin-top","12",None,None,None,None,None,None,None,None,None),
+ (15,30,"GtkLabel","max-width-chars","20",None,None,None,None,None,None,None,None,None),
+ (15,30,"GtkLabel","width-chars","15",None,None,None,None,None,None,None,None,None),
+ (15,30,"GtkLabel","wrap","True",None,None,None,None,None,None,None,None,None),
+ (15,30,"GtkLabel","wrap-mode","word-char",None,None,None,None,None,None,None,None,None),
+ (15,31,"GtkPicture","can-shrink","False",None,None,None,None,None,None,None,None,None),
+ (15,31,"GtkPicture","file","file:///home/eric/Projects/desk-changer/resources/enter-keyboard-shortcut.svg",None,None,None,None,None,None,None,None,None),
+ (15,31,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
+ (15,31,"GtkWidget","valign","center",None,None,None,None,None,None,None,None,None),
+ (15,32,"GtkLabel","label","Press Esc to cancel or Backspace to disable the keyboard shortcut.",None,None,None,None,None,None,None,None,None),
+ (15,33,"GtkShortcutLabel","disabled-text","Disabled",None,None,None,None,None,None,None,None,None),
+ (15,33,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None)
+
+
+ (2,3,14,"GtkSignalListItemFactory","bind","_on_factory_row_profiles_bind",None,None,None,None,None),
+ (3,3,14,"GtkSignalListItemFactory","setup","_on_factory_row_profiles_setup",None,None,None,None,None),
+ (12,5,4,"GtkSignalListItemFactory","bind","_on_current_profile_combo_factory_bind",None,None,None,None,None),
+ (14,5,4,"GtkSignalListItemFactory","setup","_on_current_profile_combo_factory_setup",None,None,None,None,None),
+ (17,6,4,"GtkSignalListItemFactory","bind","_on_rotation_mode_combo_factory_bind",None,None,None,None,None),
+ (18,6,4,"GtkSignalListItemFactory","setup","_on_rotation_mode_combo_factory_setup",None,None,None,None,None),
+ (19,3,23,"GtkButton","clicked","_on_add_item_button_clicked",None,None,None,None,None),
+ (25,3,26,"GtkSignalListItemFactory","bind","_on_locations_factory_bind",None,None,None,None,None),
+ (26,3,26,"GtkSignalListItemFactory","setup","_on_locations_factory_setup",None,None,None,None,None),
+ (27,3,28,"GtkButton","clicked","_on_remove_item_button_clicked",None,None,None,None,None),
+ (28,3,24,"GtkButton","clicked","_on_add_folder_button_clicked",None,None,None,None,None),
+ (29,3,20,"GtkButton","clicked","_on_add_profile_button_clicked",None,None,None,None,None),
+ (30,3,21,"GtkButton","clicked","_on_remove_profile_button_clicked",None,None,None,None,None),
+ (35,10,2,"GtkButton","clicked","_on_meta_row_delete_button_clicked",None,None,None,None,None),
+ (36,6,15,"GtkButton","clicked","_on_allowed_mime_types_reset_button_clicked",None,None,None,None,None),
+ (37,15,11,"GtkEventControllerKey","key-pressed","_on_keybind_dialog_key_pressed",None,None,None,None,None),
+ (38,15,12,"GtkButton","clicked","_on_cancel_button_clicked",None,None,None,None,None),
+ (39,15,15,"GtkButton","clicked","_on_set_button_clicked",None,None,None,None,None)
+
+
+ (4,4,"GtkWidget",1,1,None,None,None,None,None,None),
+ (4,4,"GtkWidget",2,2,None,1,None,None,None,None),
+ (10,2,"GtkWidget",1,1,None,None,None,None,None,None),
+ (10,2,"GtkWidget",2,2,None,1,None,None,None,None),
+ (6,12,"GtkWidget",1,1,None,None,None,None,None,None),
+ (6,12,"GtkWidget",2,2,None,1,None,None,None,None),
+ (6,15,"GtkWidget",1,1,None,None,None,None,None,None),
+ (6,15,"GtkWidget",2,2,None,1,None,None,None,None),
+ (3,25,"GtkWidget",1,1,None,None,None,None,None,None),
+ (3,25,"GtkWidget",2,2,None,1,None,None,None,None),
+ (8,5,"GtkWidget",1,1,None,None,None,None,None,None),
+ (8,5,"GtkWidget",2,2,None,1,None,None,None,None),
+ (8,5,"GtkWidget",2,3,None,1,None,None,None,None),
+ (15,15,"GtkWidget",1,1,None,None,None,None,None,None),
+ (15,15,"GtkWidget",2,2,None,1,None,None,None,None),
+ (15,16,"GtkSizeGroup",1,1,None,None,None,None,None,None),
+ (15,16,"GtkSizeGroup",2,2,None,1,None,None,None,None),
+ (15,16,"GtkSizeGroup",2,3,None,1,None,None,None,None),
+ (15,32,"GtkWidget",2,2,None,1,None,None,None,None),
+ (15,32,"GtkWidget",1,1,None,None,None,None,None,None)
+
+
+ (4,4,"GtkWidget",2,2,"name","boxed-list"),
+ (6,12,"GtkWidget",2,2,"name","boxed-list"),
+ (6,15,"GtkWidget",2,2,"name","error"),
+ (3,25,"GtkWidget",2,2,"name","boxed-list"),
+ (8,5,"GtkWidget",2,2,"name","flat"),
+ (8,5,"GtkWidget",2,3,"name","circular"),
+ (10,2,"GtkWidget",2,2,"name","destructive-action"),
+ (15,15,"GtkWidget",2,2,"name","suggested-action"),
+ (15,16,"GtkSizeGroup",2,2,"name","cancel_button"),
+ (15,16,"GtkSizeGroup",2,3,"name","set_button"),
+ (15,32,"GtkWidget",2,2,"name","dim-label")
+
+
diff --git a/desk-changer@eric.gach.gmail.com/common/interface.js b/desk-changer@eric.gach.gmail.com/common/interface.js
new file mode 100644
index 0000000..99e01b9
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/common/interface.js
@@ -0,0 +1,2 @@
+export const APP_ID = 'org.gnome.extensions.desk-changer';
+export const APP_PATH = '/org/gnome/extensions/desk-changer';
\ No newline at end of file
diff --git a/desk-changer@eric.gach.gmail.com/common/logging.js b/desk-changer@eric.gach.gmail.com/common/logging.js
new file mode 100644
index 0000000..6cc6784
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/common/logging.js
@@ -0,0 +1,23 @@
+import Interface from "../daemon/interface.js";
+import { getCaller } from "./utils.js";
+
+export function debug(message, caller = null) {
+ if (Interface.force_debug || Interface.settings.debug) {
+ let _caller = caller || getCaller(),
+ method = _caller.substring(0, _caller.indexOf('@')),
+ re = new RegExp(`^.*${Interface.metadata.uuid}/`);
+
+ // do some magic to make it neat
+ _caller = _caller.substring(_caller.indexOf('@') + 1)
+ .replace(re, '')
+ .replace(/(:[0-9]+):[0-9]+$/gi, `@${method}$1`);
+
+ console.log(`[${Interface.metadata.uuid}/${_caller}] ${message}`);
+ }
+}
+
+export function error(exception, message = null) {
+ let caller = getCaller();
+
+ console.error(`[] ${message}`, exception);
+}
\ No newline at end of file
diff --git a/desk-changer@eric.gach.gmail.com/common/rotation.js b/desk-changer@eric.gach.gmail.com/common/rotation.js
new file mode 100644
index 0000000..a6cd473
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/common/rotation.js
@@ -0,0 +1,136 @@
+import Gio from 'gi://Gio';
+import GObject from 'gi://GObject';
+
+const RotationMode = new GObject.registerClass({
+ Properties: {
+ 'key': GObject.param_spec_string(
+ 'key',
+ 'Key',
+ 'Rotation mode identifier',
+ null,
+ GObject.ParamFlags.READWRITE
+ ),
+ 'rotation': GObject.param_spec_string(
+ 'rotation',
+ 'Rotation',
+ 'Rotation mode type for daemon',
+ null,
+ GObject.ParamFlags.READWRITE
+ ),
+ 'label': GObject.param_spec_string(
+ 'label',
+ 'Label',
+ 'Label for rotation mode',
+ null,
+ GObject.ParamFlags.READWRITE
+ ),
+ 'interval': GObject.param_spec_uint(
+ 'interval',
+ 'Interval',
+ 'Interval value to pass to daemon',
+ 0,
+ 86400,
+ 0,
+ GObject.ParamFlags.READWRITE
+ ),
+ }
+},
+class DeskChangerRotationMode extends GObject.Object {
+ constructor(params={key: null, rotation: null, label: null, interval: 0}) {
+ super();
+
+ this._key = params['key'];
+ this._rotation = params['rotation'];
+ this._label = params['label'];
+ this._interval = params['interval'];
+ }
+
+ get interval() {
+ return this._interval;
+ }
+
+ get key() {
+ return this._key;
+ }
+
+ get label() {
+ return this._label;
+ }
+
+ get rotation() {
+ return this._rotation;
+ }
+});
+
+const RotationModes = Gio.ListStore.new(RotationMode);
+
+RotationModes.append(new RotationMode({
+ key: 'oneminute',
+ rotation: 'interval',
+ label: 'One minute interval',
+ interval: 60,
+}));
+
+RotationModes.append(new RotationMode({
+ key: 'fiveminute',
+ rotation: 'interval',
+ label: 'Five minute interval',
+ interval: 300,
+}));
+
+RotationModes.append(new RotationMode({
+ key: 'thirtyminute',
+ rotation: 'interval',
+ label: '30 Minute Interval',
+ interval: 1800,
+}));
+RotationModes.append(new RotationMode({
+ key: 'onehour',
+ rotation: 'interval',
+ label: '1 Hour Interval',
+ interval: '3600',
+}));
+RotationModes.append(new RotationMode({
+ key: 'sixhour',
+ rotation: 'interval',
+ label: '6 Hour Interval',
+ interval: '21600',
+}));
+RotationModes.append(new RotationMode({
+ key: 'twelvehour',
+ rotation: 'interval',
+ label: '12 Hour Interval',
+ interval: '43200',
+}));
+RotationModes.append(new RotationMode({
+ key: 'twentyfourhour',
+ rotation: 'interval',
+ label: '24 Hour Interval',
+ interval: '86400',
+}));
+RotationModes.append(new RotationMode({
+ key: 'interval',
+ rotation: 'interval',
+ label: 'Custom Interval',
+ interval: '0',
+}));
+RotationModes.append(new RotationMode({
+ key: 'hourly',
+ rotation: 'hourly',
+ label: 'Hourly Timer',
+ interval: '0',
+}));
+RotationModes.append(new RotationMode({
+ key: 'daily',
+ rotation: 'daily',
+ label: 'Daily Timer',
+ interval: '0',
+}));
+RotationModes.append(new RotationMode({
+ key: 'disabled',
+ rotation: 'disabled',
+ label: 'Disabled',
+ interval: '0',
+}));
+
+export default RotationModes;
\ No newline at end of file
diff --git a/desk-changer@eric.gach.gmail.com/common/service.js b/desk-changer@eric.gach.gmail.com/common/service.js
new file mode 100644
index 0000000..314ebe7
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/common/service.js
@@ -0,0 +1,14 @@
+'use strict';
+
+import Gio from 'gi://Gio';
+
+import Interface from '../daemon/interface.js';
+
+export function makeProxyWrapper() {
+ let proxy = Gio.DBusProxy.makeProxyWrapper(Interface.dbusxml);
+ return new proxy(
+ Gio.DBus.session,
+ Interface.app_id,
+ Interface.app_path
+ );
+}
diff --git a/desk-changer@eric.gach.gmail.com/common/settings.js b/desk-changer@eric.gach.gmail.com/common/settings.js
new file mode 100644
index 0000000..48ff446
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/common/settings.js
@@ -0,0 +1,162 @@
+import Gio from "gi://Gio";
+import GLib from "gi://GLib";
+import GObject from "gi://GObject";
+
+import { debug } from './logging.js';
+import { getCaller } from "./utils.js";
+import DeskChanger from "../deskchanger.js";
+
+let settings = null;
+
+const Settings = GObject.registerClass({
+ GTypeName: "DeskChangerSettings",
+},
+class DeskChangerSettings extends Gio.Settings {
+ _init() {
+ super._init({
+ settings_schema: DeskChanger.gschema.lookup(DeskChanger.app_id, true),
+ });
+ }
+
+ get allowed_mime_types() {
+ return this.get_value('allowed-mime-types').recursiveUnpack();
+ }
+
+ set allowed_mime_types(value) {
+ this.set_value('allowed-mime-types', new GLib.Variant('as', value));
+ debug(`set allowed-mime-types: ${value}`);
+ }
+
+ get auto_start() {
+ return this.get_boolean('auto-start');
+ }
+
+ set auto_start(value) {
+ value = Boolean(value);
+ this.set_boolean('auto-start', value);
+ debug(`set auto-start: ${value}`, getCaller());
+ }
+
+ get current_profile() {
+ return this.get_string('current-profile');
+ }
+
+ set current_profile(value) {
+ this.set_string('current-profile', value);
+ debug(`set current-profile: ${value}`, getCaller());
+ }
+
+ get debug() {
+ return this.get_boolean('debug');
+ }
+
+ set debug(value) {
+ this.set_boolean('debug', Boolean(value));
+ debug(`setdebug: ${value}`, getCaller());
+ }
+
+ get icon_preview() {
+ return this.get_boolean('icon-preview');
+ }
+
+ set icon_preview(value) {
+ this.set_boolean('icon-preview', Boolean(value));
+ debug(`set icon-preview: ${value}`,getCaller());
+ }
+
+ get interval() {
+ return this.get_int('interval');
+ }
+
+ set interval(value) {
+ this.set_int('interval', value);
+ debug(`set interval: ${value}`,getCaller());
+ }
+
+ get notifications() {
+ return this.get_boolean('notifications');
+ }
+
+ set notifications(value) {
+ value = Boolean(value);
+ this.set_boolean('notifications', value);
+ debug(`set notifications: ${value}`,getCaller());
+ }
+
+ get profile_state() {
+ return this.get_value('profile-state').recursiveUnpack();
+ }
+
+ set profile_state(value) {
+ this.set_value('profile-state', new GLib.Variant('a{sas}', value));
+ }
+
+ get profiles() {
+ return this.get_value('profiles').recursiveUnpack();
+ }
+
+ set profiles(value) {
+ this.set_value('profiles', new GLib.Variant('a{sa(sb)}', value));
+ debug(`set profiles: ${value}`,getCaller());
+ }
+
+ get random() {
+ return this.get_boolean('random');
+ }
+
+ set random(value) {
+ value = Boolean(value);
+ this.set_boolean('random', value);
+ debug(`set random: ${value}`,getCaller());
+ }
+
+ get remember_profile_state() {
+ return this.get_boolean('remember-profile-state');
+ }
+
+ set remember_profile_state(value) {
+ value = Boolean(value);
+ this.set_boolean('remember-profile-state', value);
+ debug(`set remember-profile-state: ${value}`,getCaller());
+ }
+
+ get rotation() {
+ return this.get_string('rotation');
+ }
+
+ set rotation(value) {
+ this.set_string('rotation', value);
+ debug(`set rotation: ${value}`,getCaller());
+ }
+
+ connect(signal, callback) {
+ let handler_id = super.connect(signal, callback);
+
+ debug(`connect ${signal} (${handler_id})`,getCaller());
+ return handler_id;
+ }
+
+ disconnect(handler_id) {
+ debug(`disconnect (${handler_id})`,getCaller());
+ return super.disconnect(handler_id);
+ }
+
+ getKeybinding(name) {
+ let array = this.get_strv(name);
+ return (typeof array[0] === 'undefined')? null : array[0];
+ }
+
+ setKeybinding(name, value) {
+ this.set_strv(name, [value,]);
+ }
+
+ get singleton() {
+ if (!settings)
+ settings = new Settings();
+
+ return settings;
+ }
+}
+);
+
+export default Settings;
\ No newline at end of file
diff --git a/desk-changer@eric.gach.gmail.com/common/utils.js b/desk-changer@eric.gach.gmail.com/common/utils.js
index 5cb2942..4dd20f1 100644
--- a/desk-changer@eric.gach.gmail.com/common/utils.js
+++ b/desk-changer@eric.gach.gmail.com/common/utils.js
@@ -1,53 +1,96 @@
'use strict';
-const ByteArray = imports.byteArray;
-const Gio = imports.gi.Gio;
-const GLib = imports.gi.GLib;
+import Gio from "gi://Gio";
+import GLib from "gi://GLib";
-function _installFile(dirname, basename, contents) {
- try {
- let filename = GLib.build_filenamev([dirname, basename]);
+import DeskChanger from "../deskchanger.js";
+import { error } from "./logging.js";
- GLib.mkdir_with_parents(dirname, 0o755);
- return GLib.file_set_contents(filename, contents);
- } catch (e) {
- deskchanger.error(e, `failed to install ${basename} to ${dirname}`);
- return false;
- }
+/**
+ * Implemented the two functions below using tweaked code from:
+ * http://stackoverflow.com/a/13227808
+ */
+export function getCaller() {
+ let stack = getStack();
+
+ // Remove superfluous function calls on stack
+ stack.shift(); // getCaller --> getStack
+ stack.shift(); // --> getCaller
+
+ // Return caller's caller
+ return stack[0];
}
-function _installResource(dirname, basename, relativePath) {
- try {
- let contents = getResource(relativePath);
+export function getStack() {
+ // Save original Error.prepareStackTrace
+ let origPrepareStackTrace = Error.prepareStackTrace;
- return _installFile(dirname, basename, contents);
- } catch (e) {
- deskchanger.error(e, `failed to install resource ${basename} to ${dirname}`);
- return false;
- }
+ // Override with function that just returns `stack`
+ Error.prepareStackTrace = function (_, stack) {
+ return stack;
+ };
+
+ // Create a new `Error`, which automatically gets `stack`
+ let err = new Error();
+
+ // Evaluate `err.stack`, which calls our new `Error.prepareStackTrace`
+ let stack = err.stack.split("\n");
+
+ // Restore original `Error.prepareStackTrace`
+ Error.prepareStackTrace = origPrepareStackTrace;
+
+ // Remove superfluous function call on stack
+ stack.shift(); // getStack --> Error
+
+ return stack
}
-function getResource(relativePath) {
+export function getResource(relativePath, app_id = null, app_path = null) {
try {
let bytes = Gio.resources_lookup_data(
- GLib.build_filenamev([deskchanger.app_path, relativePath]),
+ GLib.build_filenamev([app_path || DeskChanger.app_path, relativePath]),
Gio.ResourceLookupFlags.NONE
),
- source = ByteArray.toString(bytes.toArray());
+ source = new TextDecoder().decode(bytes.toArray());
- source = source.replace('@APP_ID@', deskchanger.app_id);
- return source.replace('@EXTDATADIR@', deskchanger.extdatadir);
+ return source
+ .replace('@APP_ID@', app_id || DeskChanger.app_id)
+ .replace('@APP_PATH@', app_path || DeskChanger.app_path)
+ .replace('@EXTDATADIR@', DeskChanger.extdatadir);
} catch (e) {
- deskchanger.error(e, `failed to get resource ${relativePath}`);
+ error(e, `failed to get resource ${relativePath}`);
return null;
}
}
-function installService() {
+export function installService() {
let data_dir = GLib.get_user_data_dir(),
dbus_dir = GLib.build_filenamev([data_dir, 'dbus-1', 'services']),
- dbus_file = `${deskchanger.app_id}.Daemon.service`;
+ dbus_file = `${DeskChanger.app_id}.Daemon.service`;
if (!_installResource(dbus_dir, dbus_file, `${dbus_file}.in`))
throw Error(`failed to install ${dbus_file} to ${dbus_dir}`);
}
+
+function _installFile(dirname, basename, contents) {
+ try {
+ let filename = GLib.build_filenamev([dirname, basename]);
+
+ GLib.mkdir_with_parents(dirname, 0o755);
+ return GLib.file_set_contents(filename, contents);
+ } catch (e) {
+ error(e, `failed to install ${basename} to ${dirname}`);
+ return false;
+ }
+}
+
+function _installResource(dirname, basename, relativePath) {
+ try {
+ let contents = getResource(relativePath);
+
+ return _installFile(dirname, basename, contents);
+ } catch (e) {
+ error(e, `failed to install resource ${basename} to ${dirname}`);
+ return false;
+ }
+}
diff --git a/desk-changer@eric.gach.gmail.com/daemon/interface.js b/desk-changer@eric.gach.gmail.com/daemon/interface.js
index acf2e90..9003bee 100644
--- a/desk-changer@eric.gach.gmail.com/daemon/interface.js
+++ b/desk-changer@eric.gach.gmail.com/daemon/interface.js
@@ -1,4 +1,21 @@
'use strict';
-var APP_ID = `${deskchanger.app_id}.Daemon`;
-var APP_PATH = `${deskchanger.app_path}/Daemon`;
+import Gio from "gi://Gio";
+
+import DeskChanger from "../deskchanger.js";
+import { getResource } from "../common/utils.js";
+import Settings from "../common/settings.js";
+
+const settings = new Settings();
+
+export default class Interface extends DeskChanger {
+ static get app_id() { return `${DeskChanger.app_id}.Daemon`; }
+ static get app_path() { return `${DeskChanger.app_path}/Daemon`; }
+ static get dbusinfo() { return dbusinfo; }
+ static get dbusxml() { return dbusxml; }
+ static get settings() { return settings; }
+}
+
+const dbusxml = getResource(`${Interface.app_id}.xml`, Interface.app_id, Interface.app_path);
+const dbusinfo = Gio.DBusNodeInfo.new_for_xml(dbusxml);
+dbusinfo.nodes.forEach(info => info.cache_build());
diff --git a/desk-changer@eric.gach.gmail.com/daemon/profile.js b/desk-changer@eric.gach.gmail.com/daemon/profile.js
index c71b596..e5affdf 100644
--- a/desk-changer@eric.gach.gmail.com/daemon/profile.js
+++ b/desk-changer@eric.gach.gmail.com/daemon/profile.js
@@ -1,12 +1,16 @@
'use strict';
-const Gio = imports.gi.Gio;
-const GLib = imports.gi.GLib;
-const GObject = imports.gi.GObject;
+import Gio from "gi://Gio";
+import GLib from "gi://GLib";
+import GObject from "gi://GObject";
+
+import Interface from "./interface.js";
+import * as Logger from '../common/logging.js';
+import { getCaller } from "../common/utils.js";
const MAX_QUEUE_LENGTH = 2;
-var Error = GObject.registerClass({
+export const Error = GObject.registerClass({
GTypeName: 'DeskChangerProfileError',
Properties: {
'caller': GObject.ParamSpec.string('caller', 'Caller', 'Caller of the exception',
@@ -20,7 +24,7 @@ class DeskChangerProfileError extends GObject.Object {
super._init();
let args = Array.from(...arguments).slice(1);
- this._caller = deskchanger.getCaller();
+ this._caller = getCaller();
this._message = message.format(...args);
}
@@ -34,7 +38,7 @@ class DeskChangerProfileError extends GObject.Object {
}
);
-var Profile = GObject.registerClass({
+export const Profile = GObject.registerClass({
GTypeName: 'DeskChangerProfile',
Properties: {
'loaded': GObject.ParamSpec.boolean('loaded', 'Loaded', 'Boolean property to check if the profile is loaded',
@@ -98,20 +102,20 @@ class DeskChangerProfile extends GObject.Object {
let wallpaper = null;
if (!this.loaded && this._queue.length >= MAX_QUEUE_LENGTH) {
- deskchanger.debug(`the queue for ${this._profile} already has ${this._queue.length} wallpapers, not adding more to the queue`);
+ Logger.debug(`the queue for ${this._profile} already has ${this._queue.length} wallpapers, not adding more to the queue`);
return;
}
- deskchanger.debug(`filling queue for ${this._profile}`);
+ Logger.debug(`filling queue for ${this._profile}`);
do {
- if (deskchanger.settings.random) {
+ if (Interface.settings.random) {
do {
wallpaper = this._wallpapers[Math.floor(Math.random() * this._wallpapers.length)];
if (this._history.contains(wallpaper) && (this._wallpapers.length >= 128 || this._history.next === wallpaper)) {
- deskchanger.debug(`wallpaper ${wallpaper} exists in the history, skipping`);
+ Logger.debug(`wallpaper ${wallpaper} exists in the history, skipping`);
wallpaper = null;
} else if (this._queue.contains(wallpaper) && this._wallpapers.length > 2) {
- deskchanger.debug(`wallpaper ${wallpaper} is already in the queue, skipping`);
+ Logger.debug(`wallpaper ${wallpaper} is already in the queue, skipping`);
wallpaper = null;
}
} while (wallpaper === null);
@@ -136,10 +140,10 @@ class DeskChangerProfile extends GObject.Object {
* @param {string} profile Profile name to load
*/
load(profile=null) {
- let _profile = profile || deskchanger.settings.current_profile,
- profiles = deskchanger.settings.profiles;
+ let _profile = profile || Interface.settings.current_profile,
+ profiles = Interface.settings.profiles;
- deskchanger.debug(`loading profile ${_profile}`);
+ Logger.debug(`loading profile ${_profile}`);
if (!(_profile in profiles)) {
throw new Error(`unable to load ${_profile} because it doesn't exist`);
@@ -150,8 +154,8 @@ class DeskChangerProfile extends GObject.Object {
this._load_uri(_profile, uri, recursive, true);
});
- this._profiles_changed_id = deskchanger.settings.connect('changed::profiles', () => {
- let _profiles = deskchanger.settings.profiles;
+ this._profiles_changed_id = Interface.settings.connect('changed::profiles', () => {
+ let _profiles = Interface.settings.profiles;
if (profiles[_profile] === _profiles[_profile]) {
return;
}
@@ -159,17 +163,17 @@ class DeskChangerProfile extends GObject.Object {
this.reload();
});
- if (deskchanger.settings.remember_profile_state && _profile in deskchanger.settings.profile_state) {
- deskchanger.debug(`restoring state for profile ${_profile}`);
- let profile_state = deskchanger.settings.profile_state;
+ if (Interface.settings.remember_profile_state && _profile in Interface.settings.profile_state) {
+ Logger.debug(`restoring state for profile ${_profile}`);
+ let profile_state = Interface.settings.profile_state;
this._queue.restore(profile_state[_profile]);
delete profile_state[_profile];
- deskchanger.settings.profile_state = profile_state;
+ Interface.settings.profile_state = profile_state;
}
this._profile = _profile;
this.fill_queue();
- deskchanger.debug(`loaded profile ${this._profile} with ${this._wallpapers.length} wallpapers`);
+ Logger.debug(`loaded profile ${this._profile} with ${this._wallpapers.length} wallpapers`);
this.emit('loaded', true);
this.emit('preview', this._queue.next);
return true;
@@ -214,7 +218,7 @@ class DeskChangerProfile extends GObject.Object {
let wallpaper = null;
if (!this.loaded) {
- deskchanger.debug('cannot load prev wallpaper, profile is not loaded');
+ Logger.debug('cannot load prev wallpaper, profile is not loaded');
return null;
}
@@ -230,30 +234,30 @@ class DeskChangerProfile extends GObject.Object {
reload() {
if (!this.loaded) return false;
- deskchanger.debug(`reloading profile ${this._profile}`)
+ Logger.debug(`reloading profile ${this._profile}`)
this.unload();
this.load();
return true;
}
unload(current=null) {
- if (deskchanger.settings.remember_profile_state) {
- deskchanger.debug(`storing profile state for ${this._profile}`);
- let profile_state = deskchanger.settings.profile_state;
+ if (Interface.settings.remember_profile_state) {
+ Logger.debug(`storing profile state for ${this._profile}`);
+ let profile_state = Interface.settings.profile_state;
profile_state[this._profile] = [this._queue.next, ];
if (current) {
profile_state[this._profile].unshift(current);
}
- deskchanger.settings.profile_state = profile_state;
+ Interface.settings.profile_state = profile_state;
}
if (this._profiles_changed_id) {
- deskchanger.settings.disconnect(this._profiles_changed_id);
+ Interface.settings.disconnect(this._profiles_changed_id);
}
this._monitors.forEach((monitor) => {
monitor.cancel();
- deskchanger.debug(`destroyed directory monitor`);
+ Logger.debug(`destroyed directory monitor`);
});
this._history.clear();
@@ -276,7 +280,7 @@ class DeskChangerProfile extends GObject.Object {
* @param {Gio.FileMonitorEvent} event_type Event type flag
*/
_directory_changed(_monitor, file, other_file, event_type) {
- deskchanger.debug(`detected change of "${event_type}" for ${file.get_uri()}`);
+ Logger.debug(`detected change of "${event_type}" for ${file.get_uri()}`);
if (event_type === Gio.FileMonitorEvent.CREATED) {
// TODO: make the recursive check against the parent
this._load_uri(this._profile, file.get_uri(), false);
@@ -286,7 +290,7 @@ class DeskChangerProfile extends GObject.Object {
if (index >= 0) {
this._wallpapers.splice(index);
- deskchanger.debug(`removed ${uri} from profile ${this._profile}`);
+ Logger.debug(`removed ${uri} from profile ${this._profile}`);
}
}
}
@@ -304,12 +308,12 @@ class DeskChangerProfile extends GObject.Object {
_load_directory(profile, location, recursive) {
let enumerator, item;
- deskchanger.debug(`attempting to load directory ${location.get_uri()} for profile ${profile}`);
+ Logger.debug(`attempting to load directory ${location.get_uri()} for profile ${profile}`);
try {
enumerator = location.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
} catch (e) {
- deskchanger.error(e, `failed to load ${location.get_uri()} from profile ${profile}`);
+ Logger.error(e, `failed to load ${location.get_uri()} from profile ${profile}`);
return;
}
@@ -320,7 +324,7 @@ class DeskChangerProfile extends GObject.Object {
}
}
- deskchanger.debug(`loading of directory ${location.get_uri()} for profile ${profile} is complete`);
+ Logger.debug(`loading of directory ${location.get_uri()} for profile ${profile} is complete`);
}
/**
@@ -339,13 +343,13 @@ class DeskChangerProfile extends GObject.Object {
*/
_load_uri(profile, uri, recursive, top_level=false) {
let location, info,
- allowed_mime_types = deskchanger.settings.allowed_mime_types;
+ allowed_mime_types = Interface.settings.allowed_mime_types;
try {
location = Gio.File.new_for_uri(uri);
info = location.query_info('standard::*', Gio.FileQueryInfoFlags.NONE, null);
} catch (e) {
- deskchanger.error(e, `failed to load uri ${uri} for profile ${profile}`);
+ Logger.error(e, `failed to load uri ${uri} for profile ${profile}`);
return;
}
@@ -357,29 +361,29 @@ class DeskChangerProfile extends GObject.Object {
monitor = location.monitor_directory(Gio.FileMonitorFlags.NONE, cancellable);
monitor.connect('changed', this._directory_changed.bind(this));
this._monitors.push(monitor);
- deskchanger.debug(`added monitor for ${uri}`);
+ Logger.debug(`added monitor for ${uri}`);
} catch (e) {
- deskchanger.error(e, `failed to create monitor on ${uri}`);
+ Logger.error(e, `failed to create monitor on ${uri}`);
}
this._load_directory(profile, location, recursive);
} else if (info.get_file_type() === Gio.FileType.REGULAR && allowed_mime_types.includes(info.get_content_type())) {
// Load any files that are in our allowed mime types
if (location.get_uri() in this._wallpapers) {
- deskchanger.debug(`skipping duplicate file ${location.get_uri()} on profile ${profile}`);
+ Logger.debug(`skipping duplicate file ${location.get_uri()} on profile ${profile}`);
return;
}
this._wallpapers.push(location.get_uri());
- deskchanger.debug(`loaded ${location.get_uri()} to profile ${profile}`);
+ Logger.debug(`loaded ${location.get_uri()} to profile ${profile}`);
} else {
- deskchanger.debug(`skipping unknown file type of ${info.get_content_type()} for profile ${profile}`);
+ Logger.debug(`skipping unknown file type of ${info.get_content_type()} for profile ${profile}`);
}
}
}
);
-var Queue = GObject.registerClass({
+export const Queue = GObject.registerClass({
GTypeName: 'DeskChangerProfileQueue',
Properties: {
'length': GObject.ParamSpec.uint('length', 'Length', 'The current size of the queue',
@@ -408,7 +412,7 @@ class DeskChangerProfileQueue extends GObject.Object {
clear() {
this._queue = [];
- deskchanger.debug('cleared queue object');
+ Logger.debug('cleared queue object');
}
contains(uri) {
@@ -418,7 +422,7 @@ class DeskChangerProfileQueue extends GObject.Object {
dequeue() {
if (this._queue.length === 0) return undefined;
let uri = this._queue.shift();
- deskchanger.debug(`dequed ${uri} from the queue`);
+ Logger.debug(`dequed ${uri} from the queue`);
return uri;
}
@@ -429,7 +433,7 @@ class DeskChangerProfileQueue extends GObject.Object {
this._queue.push(uri);
}
- deskchanger.debug(`added ${uri} to the queue`);
+ Logger.debug(`added ${uri} to the queue`);
}
remove(uri) {
@@ -437,16 +441,16 @@ class DeskChangerProfileQueue extends GObject.Object {
if (index >= 0) {
this._queue.splice(index, 1);
- deskchanger.debug(`removed ${uri} from the queue`);
+ Logger.debug(`removed ${uri} from the queue`);
return true;
}
- deskchanger.debug(`${uri} was not found in the queue`);
+ Logger.debug(`${uri} was not found in the queue`);
return false;
}
restore(queue) {
- deskchanger.debug('restoring the queue state');
+ Logger.debug('restoring the queue state');
this._queue = queue;
}
}
diff --git a/desk-changer@eric.gach.gmail.com/daemon/server.js b/desk-changer@eric.gach.gmail.com/daemon/server.js
index 2d76b4b..cbe6842 100755
--- a/desk-changer@eric.gach.gmail.com/daemon/server.js
+++ b/desk-changer@eric.gach.gmail.com/daemon/server.js
@@ -1,27 +1,15 @@
-#!/usr/bin/env gjs
+#!/usr/bin/env -S gjs -m
'use strict';
-const Gio = imports.gi.Gio;
-const GLib = imports.gi.GLib;
-const GObject = imports.gi.GObject;
+import Gio from "gi://Gio";
+import GLib from "gi://GLib";
+import GObject from "gi://GObject";
-if ((typeof globalThis !== 'undefined' && !globalThis.deskchanger) || !window.deskchanger) {
-// Find the root datadir of the extension
- function get_datadir() {
- let m = /@(.+):\d+/.exec((new Error()).stack.split('\n')[1]);
- return Gio.File.new_for_path(m[1]).get_parent().get_parent().get_path();
- }
-
- imports.searchPath.unshift(get_datadir());
- imports._deskchanger;
-}
-
-const Interface = imports.daemon.interface;
-const Profile = imports.daemon.profile;
-const Timer = imports.daemon.timer;
-const Utils = imports.common.utils;
-const _ = deskchanger._;
+import Interface from "./interface.js";
+import * as Profile from "./profile.js";
+import * as Timer from "./timer.js";
+import * as Logger from "../common/logging.js";
var Server = GObject.registerClass({
GTypeName: 'DeskChangerDaemonServer',
@@ -29,14 +17,14 @@ var Server = GObject.registerClass({
'preview': GObject.ParamSpec.string(
'preview',
'Preview',
- _('The next wallpaper in queue'),
+ 'The next wallpaper in queue',
GObject.ParamFlags.READABLE,
null
),
'running': GObject.ParamSpec.boolean(
'running',
'Running',
- _('Check if the daemon is running'),
+ 'Check if the daemon is running',
GObject.ParamFlags.READABLE,
false
),
@@ -62,14 +50,14 @@ class Server extends Gio.Application {
this._timer = null;
super._init({
- application_id: Interface.APP_ID,
+ application_id: Interface.app_id,
flags: Gio.ApplicationFlags.IS_SERVICE |
Gio.ApplicationFlags.HANDLES_OPEN |
Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
});
- this.add_main_option('debug', 'd'.charCodeAt(0), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _('Enable debugging'), null);
- this.add_main_option('version', 'v'.charCodeAt(0), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _('Show release version'), null);
+ this.add_main_option('debug', 'd'.charCodeAt(0), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, 'Enable debugging', null);
+ this.add_main_option('version', 'v'.charCodeAt(0), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, 'Show release version', null);
}
get preview() {
@@ -84,8 +72,8 @@ class Server extends Gio.Application {
let connection = this.get_dbus_connection();
if (connection) {
- deskchanger.debug(`DBUS::${signal}(${variant.recursiveUnpack()})`);
- connection.emit_signal(null, Interface.APP_PATH, Interface.APP_ID, signal, variant);
+ Logger.debug(`DBUS::${signal}(${variant.recursiveUnpack()})`);
+ connection.emit_signal(null, Interface.app_path, Interface.app_id, signal, variant);
}
}
@@ -128,17 +116,17 @@ class Server extends Gio.Application {
start() {
if (this._running) {
- deskchanger.debug('daemon is already started');
+ Logger.debug('daemon is already started');
return false;
}
this.loadprofile();
this._create_timer();
- this._rotation_changed_id = deskchanger.settings.connect('changed::rotation', () => {
+ this._rotation_changed_id = Interface.settings.connect('changed::rotation', () => {
this._destroy_timer();
this._create_timer();
});
- this._current_profile_changed_id = deskchanger.settings.connect('changed::current-profile', () => {
+ this._current_profile_changed_id = Interface.settings.connect('changed::current-profile', () => {
this.loadprofile();
});
this._running = true;
@@ -149,19 +137,19 @@ class Server extends Gio.Application {
stop() {
if (!this._running) {
- deskchanger.debug('cannot stop, daemon isn\'t running');
+ Logger.debug('cannot stop, daemon isn\'t running');
return false;
}
this._destroy_timer();
if (this._current_profile_changed_id) {
- deskchanger.settings.disconnect(this._current_profile_changed_id);
+ Interface.settings.disconnect(this._current_profile_changed_id);
this._current_profile_changed_id = null;
}
if (this._rotation_changed_id) {
- deskchanger.settings.disconnect(this._rotation_changed_id);
+ Interface.settings.disconnect(this._rotation_changed_id);
this._rotation_changed_id = null;
}
@@ -174,15 +162,15 @@ class Server extends Gio.Application {
vfunc_dbus_register(connection, object_path) {
if (super.vfunc_dbus_register(connection, object_path)) {
- deskchanger.debug(`attempting to register object on dbus: ${object_path}`);
+ Logger.debug(`attempting to register object on dbus: ${object_path}`);
try {
this._dbus_id = connection.register_object(
object_path,
- deskchanger.dbusinfo.lookup_interface(Interface.APP_ID),
+ Interface.dbusinfo.lookup_interface(Interface.app_id),
(connection, sender, object_path, interface_name, method_name, parameters, invocation) => {
parameters = parameters.recursiveUnpack();
- deskchanger.debug(`[DBUS.call] ${interface_name}.${method_name}(${parameters})`)
+ Logger.debug(`[DBUS.call] ${interface_name}.${method_name}(${parameters})`)
if (!this._running && ['quit', 'start'].indexOf(method_name.toLowerCase()) === -1) {
invocation.return_dbus_error(`${interface_name}.${method_name}`, 'daemon must be started first');
@@ -192,7 +180,7 @@ class Server extends Gio.Application {
try {
this[`_dbus_call_${method_name.toLowerCase()}`](invocation, ...parameters);
} catch (e) {
- deskchanger.error(e, `DBUS::call ${e.message}`);
+ Logger.error(e, `DBUS::call ${e.message}`);
invocation.return_dbus_error(`${interface_name}.${method_name}`, e.message);
}
},
@@ -200,10 +188,10 @@ class Server extends Gio.Application {
() => {},
);
- deskchanger.debug(`successfully registered object on dbus: ${object_path}(${this._dbus_id})`);
+ Logger.debug(`successfully registered object on dbus: ${object_path}(${this._dbus_id})`);
return true;
} catch (e) {
- deskchanger.error(e, `failed to register object on dbus: ${object_path}`);
+ Logger.error(e, `failed to register object on dbus: ${object_path}`);
} finally {
if (this._dbus_id === 0) {
this._dbus_id = null;
@@ -216,7 +204,7 @@ class Server extends Gio.Application {
vfunc_dbus_unregister(connection, object_path) {
if (this._dbus_id) {
- deskchanger.debug(`unregistering object from dbus: ${object_path}(${this._dbus_id})`);
+ Logger.debug(`unregistering object from dbus: ${object_path}(${this._dbus_id})`);
connection.unregister_object(this._dbus_id);
}
@@ -225,19 +213,19 @@ class Server extends Gio.Application {
vfunc_handle_local_options(options) {
if (options.contains('version')) {
- print(`${deskchanger.app_id} ${deskchanger.metadata.version}`);
+ print(`${Interface.app_id} ${Interface.metadata.version}`);
return 0;
}
if (options.contains('debug')) {
- deskchanger.force_debug = true;
+ Interface.force_debug = true;
}
return -1;
}
vfunc_shutdown() {
- deskchanger.debug('vfunc_shutdown');
+ Logger.debug('vfunc_shutdown');
if (this._running) {
this.stop();
@@ -252,7 +240,7 @@ class Server extends Gio.Application {
}
vfunc_startup() {
- deskchanger.debug('vfunc_startup');
+ Logger.debug('vfunc_startup');
super.vfunc_startup();
// Keep us open and running... we are a daemon
@@ -261,30 +249,30 @@ class Server extends Gio.Application {
_create_timer() {
let interval,
- rotation = deskchanger.settings.rotation,
- [success, iterator] = deskchanger.rotation.get_iter_first();
+ rotation = Interface.settings.rotation,
+ [success, iterator] = Interface.rotation.get_iter_first();
while (success) {
- if (deskchanger.rotation.get_value(iterator, 0) === rotation) {
- interval = (rotation === 'interval')? deskchanger.settings.interval : deskchanger.rotation.get_value(iterator, 3);
- rotation = deskchanger.rotation.get_value(iterator, 1);
+ if (Interface.rotation.get_value(iterator, 0) === rotation) {
+ interval = (rotation === 'interval')? Interface.settings.interval : Interface.rotation.get_value(iterator, 3);
+ rotation = Interface.rotation.get_value(iterator, 1);
break;
}
- success = deskchanger.rotation.iter_next(iterator);
+ success = Interface.rotation.iter_next(iterator);
}
if (rotation === 'interval') {
this._timer = new Timer.Interval(interval, this.next.bind(this));
- if (deskchanger.settings.rotation === 'interval') {
- this._interval_changed_id = deskchanger.settings.connect('changed::interval', () => {
+ if (Interface.settings.rotation === 'interval') {
+ this._interval_changed_id = Interface.settings.connect('changed::interval', () => {
this._timer.destroy();
- this._timer = new Timer.Interval(deskchanger.settings.interval, this.next.bind(this));
+ this._timer = new Timer.Interval(Interface.settings.interval, this.next.bind(this));
});
}
- } else if (deskchanger.settings.rotation === 'hourly') {
+ } else if (Interface.settings.rotation === 'hourly') {
this._timer = new Timer.Hourly(this.next.bind(this));
- } else if (deskchanger.settings.rotation === 'daily') {
+ } else if (Interface.settings.rotation === 'daily') {
this._timer = new Timer.Daily(this.next.bind(this));
}
}
@@ -292,7 +280,7 @@ class Server extends Gio.Application {
_destroy_timer() {
if (this._timer) {
if (this._interval_changed_id) {
- deskchanger.settings.disconnect(this._interval_changed_id);
+ Interface.settings.disconnect(this._interval_changed_id);
this._interval_changed_id = null;
}
@@ -331,7 +319,7 @@ class Server extends Gio.Application {
}
_handle_dbus_get(connection, sender, object_path, interface_name, property_name) {
- deskchanger.debug(`DBUS::getProperty(${property_name})`);
+ Logger.debug(`DBUS::getProperty(${property_name})`);
switch (property_name.toLowerCase()) {
case 'history':
return new GLib.Variant('as', []);
@@ -340,18 +328,18 @@ class Server extends Gio.Application {
return new GLib.Variant('as', []);
case 'preview':
- return new GLib.Variant('s', this.preview);
+ return new GLib.Variant('s', String(this.preview));
case 'running':
- return new GLib.Variant('b', this.running);
+ return new GLib.Variant('b', Boolean(this.running));
}
- deskchanger.debug(`unknown property ${interface_name}.${property_name}`)
+ Logger.debug(`unknown property ${interface_name}.${property_name}`)
return null;
}
_set_wallpaper(uri) {
- deskchanger.debug(`setting wallpaper to ${uri}`);
+ Logger.debug(`setting wallpaper to ${uri}`);
this._background.set_string('picture-uri', uri);
if (this._background_schema.has_key('picture-uri-dark')) {
this._background.set_string('picture-uri-dark', uri);
diff --git a/desk-changer@eric.gach.gmail.com/daemon/timer.js b/desk-changer@eric.gach.gmail.com/daemon/timer.js
index 255edb1..dc1ed24 100644
--- a/desk-changer@eric.gach.gmail.com/daemon/timer.js
+++ b/desk-changer@eric.gach.gmail.com/daemon/timer.js
@@ -1,10 +1,11 @@
'use strict';
-const GLib = imports.gi.GLib;
-const GObject = imports.gi.GObject;
-const Signals = imports.signals;
+import GLib from "gi://GLib";
+import GObject from "gi://GObject";
-var Interval = GObject.registerClass({
+import { debug } from "../common/logging.js";
+
+export const Interval = GObject.registerClass({
Properties: {
interval: GObject.ParamSpec.uint('interval', 'Interval',
'The interval at which the callback is triggered',
@@ -25,7 +26,7 @@ class DeskChangerTimerInterval extends GObject.Object {
this._interval = parseInt(interval);
super._init(params);
this._timer = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, this._interval, this.__callback__.bind(this));
- deskchanger.debug(`added interval(${this._interval}) timer ${this._timer}`);
+ debug(`added interval(${this._interval}) timer ${this._timer}`);
}
get callback() {
@@ -38,7 +39,7 @@ class DeskChangerTimerInterval extends GObject.Object {
__callback__() {
if (this._callback) {
- deskchanger.debug('calling interval callback');
+ debug('calling interval callback');
return Boolean(this._callback());
}
@@ -46,12 +47,12 @@ class DeskChangerTimerInterval extends GObject.Object {
}
destroy() {
- deskchanger.debug(`removing interval timer ${this._timer}`);
+ debug(`removing interval timer ${this._timer}`);
GLib.source_remove(this._timer);
}
});
-var Hourly = GObject.registerClass(
+export const Hourly = GObject.registerClass(
class DeskChangerTimerHourly extends Interval {
_init(callback, params = {}) {
this._done = false;
@@ -70,7 +71,7 @@ class DeskChangerTimerHourly extends Interval {
if (this._timer_check(new Date())) {
if (!this._done) {
this._done = true;
- deskchanger.debug('calling hourly callback');
+ debug('calling hourly callback');
return super.__callback__();
}
@@ -82,7 +83,7 @@ class DeskChangerTimerHourly extends Interval {
}
});
-var Daily = GObject.registerClass(
+export const Daily = GObject.registerClass(
class DeskChangerTimerDaily extends Hourly {
_timer_check(date) {
if (super._timer_check(date) && date.getHours() === 0) {
diff --git a/desk-changer@eric.gach.gmail.com/deskchanger.js b/desk-changer@eric.gach.gmail.com/deskchanger.js
new file mode 100644
index 0000000..bc55e46
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/deskchanger.js
@@ -0,0 +1,40 @@
+import GLib from "gi://GLib";
+import Gtk from 'gi://Gtk?version=4.0'
+import Gio from "gi://Gio";
+
+const extdatadir = (() => {
+ let m = /@(.+):\d+/.exec((new Error()).stack.split('\n')[1]);
+ return Gio.File.new_for_uri(m[1]).get_parent().get_path();
+})();
+
+const gschemadir = GLib.build_filenamev([extdatadir, 'schemas']);
+
+const gschema = Gio.SettingsSchemaSource.new_from_directory(
+ gschemadir,
+ Gio.SettingsSchemaSource.get_default(),
+ false
+);
+
+const metadata = (() => {
+ let data = GLib.file_get_contents(extdatadir + '/metadata.json')[1];
+ return JSON.parse(new TextDecoder().decode(data));
+})();
+
+export default class DeskChanger
+{
+ static get app_id() { return "org.gnome.Shell.Extensions.DeskChanger"; }
+ static get app_path() { return "/org/gnome/Shell/Extensions/DeskChanger"; }
+ static get extdatadir() { return extdatadir; }
+ static force_debug = false;
+ static get gschema() { return gschema; }
+ static get gschemadir() { return gschemadir; }
+ static get metadata() { return metadata; }
+ static get rotation() { return rotation; }
+}
+
+Gio.Resource.load(
+ GLib.build_filenamev([DeskChanger.extdatadir, 'resources', `${DeskChanger.app_id}.gresource`])
+)._register();
+
+const builder = Gtk.Builder.new_from_resource(`${DeskChanger.app_path}/ui/rotation.ui`);
+const rotation = builder.get_object('rotation');
diff --git a/desk-changer@eric.gach.gmail.com/extension.js b/desk-changer@eric.gach.gmail.com/extension.js
index fa0700d..88f962b 100644
--- a/desk-changer@eric.gach.gmail.com/extension.js
+++ b/desk-changer@eric.gach.gmail.com/extension.js
@@ -1,140 +1,133 @@
'use strict';
-const ExtensionUtils = imports.misc.extensionUtils;
-const Me = ExtensionUtils.getCurrentExtension();
-// first init the common things
-Me.imports._deskchanger;
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
-const Utils = Me.imports.common.utils;
-const Convenience = Me.imports.convenience;
-const Service = Me.imports.service;
-const DeskChangerPanelMenuButton = Me.imports.ui.panelMenu.Button;
-
-const Main = imports.ui.main;
-const _ = deskchanger._;
+import DeskChanger from './deskchanger.js';
+import Interface from './daemon/interface.js';
+import { debug } from './common/logging.js';
+import * as Utils from './common/utils.js';
+import { makeProxyWrapper } from './common/service.js';
+import {Button as DeskChangerPanelMenuButton} from './ui/panelMenu.js';
// general
let daemon, button,
// signals
changed_id, current_profile_id, notifications_id, random_id, rotation_id;
-function disable() {
- deskchanger.debug('disabling extension');
+export default class DeskChangerExtension extends Extension {
+ disable() {
+ debug('disabling extension');
- // button go bye bye
- if (button && typeof button.destroy === 'function') {
- button.destroy();
- }
- button = null;
+ // button go bye bye
+ if (button && typeof button.destroy === 'function') {
+ button.destroy();
+ }
+ button = null;
- if (changed_id) {
- daemon.disconnectSignal(changed_id);
- }
- changed_id = null;
+ if (changed_id) {
+ daemon.disconnectSignal(changed_id);
+ }
+ changed_id = null;
- if (current_profile_id) {
- deskchanger.settings.disconnect(current_profile_id);
- }
- current_profile_id = null;
+ if (current_profile_id) {
+ Interface.settings.disconnect(current_profile_id);
+ }
+ current_profile_id = null;
- if (notifications_id) {
- deskchanger.settings.disconnect(notifications_id);
- }
- notifications_id = null;
+ if (notifications_id) {
+ Interface.settings.disconnect(notifications_id);
+ }
+ notifications_id = null;
- if (random_id) {
- deskchanger.settings.disconnect(random_id);
- }
- random_id = null;
+ if (random_id) {
+ Interface.settings.disconnect(random_id);
+ }
+ random_id = null;
- if (rotation_id) {
- deskchanger.settings.disconnect(rotation_id);
+ if (rotation_id) {
+ Interface.settings.disconnect(rotation_id);
+ }
+ rotation_id = null;
}
- rotation_id = null;
-}
-function enable() {
- deskchanger.debug('enabling extension');
-
- Utils.installService();
- daemon = Service.makeProxyWrapper();
-
- changed_id = daemon.connectSignal('Changed', function (proxy, name, [uri]) {
- notify(_('Wallpaper changed: %s'.format(uri)));
- });
-
- current_profile_id = deskchanger.settings.connect('changed::current-profile', function () {
- notify(_('Profile changed to %s'.format(deskchanger.settings.current_profile)));
- });
-
- notifications_id = deskchanger.settings.connect('changed::notifications', function () {
- notify(((deskchanger.settings.notifications) ?
- _('Notifications are now enabled') :
- _('Notifications are now disabled')
- ), true);
- });
-
- random_id = deskchanger.settings.connect('changed::random', function () {
- notify(((deskchanger.settings.random)?
- _('Wallpapers will be shown in a random order') :
- _('Wallpapers will be shown in the order they were loaded')
- ));
- });
-
- rotation_id = deskchanger.settings.connect('changed::rotation', function () {
- let message, interval,
- rotation = deskchanger.settings.rotation,
- [success, iterator] = deskchanger.rotation.get_iter_first();
-
- while (success) {
- if (deskchanger.rotation.get_value(iterator, 0) === rotation) {
- if (rotation === 'interval') {
- interval = `${deskchanger.rotation.get_value(iterator, 2)} of ${deskchanger.settings.interval} seconds`;
- } else {
- interval = deskchanger.rotation.get_value(iterator, 2);
+ enable() {
+ debug('enabling extension');
+
+ Utils.installService();
+ daemon = makeProxyWrapper();
+
+ changed_id = daemon.connectSignal('Changed', (proxy, name, [uri]) => {
+ this.notify(_('Wallpaper changed: %s'.format(uri)));
+ });
+
+ current_profile_id = Interface.settings.connect('changed::current-profile', () => {
+ this.notify(_('Profile changed to %s'.format(Interface.settings.current_profile)));
+ });
+
+ notifications_id = Interface.settings.connect('changed::notifications', () => {
+ this.notify(((Interface.settings.notifications) ?
+ _('Notifications are now enabled') :
+ _('Notifications are now disabled')
+ ), true);
+ });
+
+ random_id = Interface.settings.connect('changed::random', () => {
+ this.notify(((Interface.settings.random)?
+ _('Wallpapers will be shown in a random order') :
+ _('Wallpapers will be shown in the order they were loaded')
+ ));
+ });
+
+ rotation_id = Interface.settings.connect('changed::rotation', () => {
+ let message, interval,
+ rotation = Interface.settings.rotation,
+ [success, iterator] = DeskChanger.rotation.get_iter_first();
+
+ while (success) {
+ if (DeskChanger.rotation.get_value(iterator, 0) === rotation) {
+ if (rotation === 'interval') {
+ interval = `${DeskChanger.rotation.get_value(iterator, 2)} of ${Interface.settings.interval} seconds`;
+ } else {
+ interval = DeskChanger.rotation.get_value(iterator, 2);
+ }
+
+ rotation = DeskChanger.rotation.get_value(iterator, 1);
+ break;
}
- rotation = deskchanger.rotation.get_value(iterator, 1);
- break;
+ success = DeskChanger.rotation.iter_next(iterator);
}
- success = deskchanger.rotation.iter_next(iterator);
- }
-
- switch (rotation) {
- case 'interval':
- message = _(`Rotation will occur at a ${interval}`);
- break;
- case 'hourly':
- message = _('Rotation will occur at the beginning of every hour');
- break;
- case 'daily':
- message = _('Rotation will occur at the beginning of every day');
- break;
- default:
- message = _('Rotation has been disabled');
- break;
- }
+ switch (rotation) {
+ case 'interval':
+ message = _(`Rotation will occur at a ${interval}`);
+ break;
+ case 'hourly':
+ message = _('Rotation will occur at the beginning of every hour');
+ break;
+ case 'daily':
+ message = _('Rotation will occur at the beginning of every day');
+ break;
+ default:
+ message = _('Rotation has been disabled');
+ break;
+ }
- notify(message);
- });
+ this.notify(message);
+ });
- button = new DeskChangerPanelMenuButton(daemon);
- Main.panel.addToStatusArea('DeskChanger', button);
+ button = new DeskChangerPanelMenuButton(daemon);
+ Main.panel.addToStatusArea('DeskChanger', button);
- if (deskchanger.settings.auto_start && !daemon.Running) {
- daemon.StartSync();
+ if (Interface.settings.auto_start && !daemon.Running) {
+ daemon.StartSync();
+ }
}
-}
-function notify(message, force) {
- if (deskchanger.settings.notifications || force === true) {
- Main.notify('DeskChanger', message);
+ notify(message, force) {
+ if (Interface.settings.notifications || force === true) {
+ Main.notify('DeskChanger', message);
+ }
}
}
-
-function init() {
- log(`init ${Me.uuid} version ${Me.metadata.version}`);
-
- ExtensionUtils.initTranslations();
-}
\ No newline at end of file
diff --git a/desk-changer@eric.gach.gmail.com/metadata.json b/desk-changer@eric.gach.gmail.com/metadata.json
index 1ef837a..2a60d8b 100644
--- a/desk-changer@eric.gach.gmail.com/metadata.json
+++ b/desk-changer@eric.gach.gmail.com/metadata.json
@@ -4,16 +4,9 @@
"name": "Desk Changer",
"settings-schema": "org.gnome.shell.extensions.desk-changer",
"shell-version": [
- "3.34",
- "3.36",
- "3.38",
- "40",
- "41",
- "42",
- "43",
- "44"
+ "45"
],
"url": "https://github.com/BigE/desk-changer/",
"uuid": "desk-changer@eric.gach.gmail.com",
- "version": "35"
+ "version": "36"
}
diff --git a/desk-changer@eric.gach.gmail.com/prefs.js b/desk-changer@eric.gach.gmail.com/prefs.js
index 4edc53b..68fa340 100644
--- a/desk-changer@eric.gach.gmail.com/prefs.js
+++ b/desk-changer@eric.gach.gmail.com/prefs.js
@@ -1,394 +1,49 @@
-'use strict';
-
-const {Gio, GObject, Gtk} = imports.gi;
-const Config = imports.misc.config;
-const shellVersion = Number.parseInt(Config.PACKAGE_VERSION.split('.')[0]);
-const ExtensionUtils = imports.misc.extensionUtils;
-
-const Me = ExtensionUtils.getCurrentExtension();
-Me.imports._deskchanger;
-const _ = deskchanger._;
-const Service = Me.imports.service;
-
-const AddItemsDialog = GObject.registerClass({
- GTypeName: 'AddItemsDialog',
-},
-class AddItemsDialog extends Gtk.FileChooserDialog {
- _init(params = {}) {
- if (!('select-multiple' in params)) {
- params['select-multiple'] = true;
- }
-
- super._init(params);
-
- this.add_button(_('Add'), Gtk.ResponseType.OK);
- this.add_button(_('Cancel'), Gtk.ResponseType.CANCEL);
- }
-});
-
-const PrefsWidget = GObject.registerClass({
- GTypeName: 'PrefsWidget',
- InternalChildren: [
- 'allowed_mime_types',
- 'combo_current_profile',
- 'combo_location_profile',
- 'combo_rotation_mode',
- 'image_about_logo',
- 'keyboard',
- 'label_about_description',
- 'label_about_name',
- 'label_about_url',
- 'label_about_version',
- 'locations',
- 'profiles',
- 'spinner_interval',
- 'switch_auto_start',
- 'switch_daemon_state',
- 'switch_icon_preview',
- 'switch_notifications',
- 'switch_remember_profile_state',
- 'tree_locations',
- ],
- Template: `resource://${deskchanger.app_path}/ui/${(shellVersion < 40)? 'prefs.3.ui' : 'prefs.ui'}`,
-},
-class PrefsWidget extends Gtk.Box {
- _init(params={}) {
- let success, iterator,
- mime_types = deskchanger.settings.allowed_mime_types.join("\n");
-
- this._is_init = true;
- this._daemon = Service.makeProxyWrapper();
- // set up us the base
- super._init(params);
-
- // bind our simple settings
- deskchanger.settings.bind('auto-start', this._switch_auto_start, 'active', Gio.SettingsBindFlags.DEFAULT);
- deskchanger.settings.bind('icon-preview', this._switch_icon_preview, 'active', Gio.SettingsBindFlags.DEFAULT);
- deskchanger.settings.bind('interval', this._spinner_interval, 'value', Gio.SettingsBindFlags.DEFAULT);
- deskchanger.settings.bind('notifications', this._switch_notifications, 'active', Gio.SettingsBindFlags.DEFAULT);
- deskchanger.settings.bind('remember-profile-state', this._switch_remember_profile_state, 'active', Gio.SettingsBindFlags.DEFAULT);
-
- // keybindings
- [success, iterator] = this._keyboard.get_iter_first();
- while (success) {
- let name = this._keyboard.get_value(iterator, 3),
- [ok, key, mods] = Gtk.accelerator_parse(deskchanger.settings.getKeybinding(name));
-
- if (ok === true || mods === undefined) {
- if (mods === undefined) {
- mods = key;
- key = ok;
- }
-
- this._keyboard.set(iterator, [1, 2], [mods, key]);
- success = this._keyboard.iter_next(iterator);
- }
- }
-
- // load everything else
- this._allowed_mime_types.set_text(mime_types, mime_types.length);
- this._combo_rotation_mode.set_model(deskchanger.rotation);
- this._combo_rotation_mode.set_active_id(deskchanger.settings.rotation);
+import Gio from 'gi://Gio';
+import { ExtensionPreferences, gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+import Interface from './daemon/interface.js';
+import ProfilesPage from './ui/prefs/profiles.js';
+import { Location, Profile } from './ui/common/profiles.js';
+import KeyboardPage from './ui/prefs/keyboard.js';
+import ExtensionPage from './ui/prefs/extension.js';
+import DaemonPage from './ui/prefs/daemon.js';
+import AboutPage from './ui/prefs/about.js';
+
+export default class DeskChangerPreferences extends ExtensionPreferences {
+ fillPreferencesWindow(window) {
+ this._current_profile_index = null;
+ this._profiles = Gio.ListStore.new(Profile);
this._load_profiles();
- this._switch_daemon_state.set_active(this._daemon.Running);
- this._daemon.connectSignal('Running', (proxy, name, [state]) => {
- this._switch_daemon_state.set_active(state);
- });
- // label up
- this._label_about_description.set_label(Me.metadata.description);
- this._label_about_name.set_label(Me.metadata.name);
- this._label_about_url.set_markup(`${Me.metadata.url}`);
- this._label_about_version.set_label(`Version ${Me.metadata.version}`);
-
- this._is_init = false;
- if (shellVersion < 40) {
- // show it all
- this.show_all();
- }
- }
-
- _get_location_profile() {
- let [ok, iterator] = this._combo_location_profile.get_active_iter();
-
- if (!ok) return false;
- return this._profiles.get_value(iterator, 0);
- }
-
- _load_profiles() {
- this._profiles.clear();
-
- for (let profile in deskchanger.settings.profiles) {
- let iterator = this._profiles.append();
- this._profiles.set_value(iterator, 0, profile);
- if (deskchanger.settings.current_profile === profile) {
- this._combo_current_profile.set_active_iter(iterator);
- this._combo_location_profile.set_active_iter(iterator);
- }
- }
- }
-
- _on_accel_key(_widget, path, key=0, mods=0, keycode=0) {
- deskchanger.debug(_widget);
- let [success, iterator] = this._keyboard.get_iter_from_string(path);
-
- if (!success) {
- throw new Error(_('Failed to update keybinding'));
- }
-
- let name = this._keyboard.get_value(iterator, 3),
- value = Gtk.accelerator_name(key, mods);
- this._keyboard.set(iterator, [1, 2], [mods, key]);
- deskchanger.settings.setKeybinding(name, value);
- }
-
- _on_buffer_allowed_mime_types_changed() {
- if (this._is_init) return;
-
- let start = this._allowed_mime_types.get_start_iter(),
- end = this._allowed_mime_types.get_end_iter(),
- text = this._allowed_mime_types.get_text(start, end, false)
- deskchanger.settings.allowed_mime_types = text.split("\n");
- }
-
- _on_button_add_folders_clicked() {
- let dialog = new AddItemsDialog({
- action: Gtk.FileChooserAction.SELECT_FOLDER,
- title: 'Add Folders',
- transient_for: this.get_root(),
- });
-
- dialog.show();
- dialog.connect('response', this._on_response_add_items.bind(this));
- }
-
- _on_button_add_items_clicked() {
- let dialog = new AddItemsDialog({
- action: Gtk.FileChooserAction.OPEN,
- title: 'Add Images',
- transient_for: this.get_root(),
- }),
- filter = new Gtk.FileFilter();
+ Interface.settings.connect('changed::profiles', () => this._load_profiles());
- deskchanger.settings.allowed_mime_types.forEach(value => {
- filter.add_mime_type(value);
- });
- dialog.set_filter(filter);
- dialog.show();
- dialog.connect('response', this._on_response_add_items.bind(this));
- }
-
- _on_button_add_profile_clicked() {
- let dialog = new Gtk.Dialog({
- title: 'DeskChanger New Profile',
- transient_for: this.get_root(),
- }),
- mbox = dialog.get_content_area(),
- box = new Gtk.Box({orientation: Gtk.Orientation.HORIZONTAL}),
- label = new Gtk.Label({label: _('Profile Name')}),
- input = new Gtk.Entry();
-
- if (shellVersion < 40) {
- box.pack_start(label, false, false, 0);
- box.pack_start(input, true, true, 0);
- mbox.pack_start(box, true, true, 0);
- mbox.show_all();
- } else {
- box.append(label);
- box.append(input);
- mbox.append(box);
- }
-
- dialog.add_button(_('Add'), Gtk.ResponseType.OK);
- dialog.add_button(_('Cancel'), Gtk.ResponseType.CANCEL);
- dialog.set_default_response(Gtk.ResponseType.OK);
- dialog.connect('response', (_dialog, result) => {
- if (result === Gtk.ResponseType.OK) {
- let _profiles = deskchanger.settings.profiles,
- profile = input.get_text();
- _profiles[profile] = [];
- deskchanger.settings.profiles = _profiles;
- this._load_profiles();
- this._combo_location_profile.set_active_id(profile);
- }
- _dialog.destroy();
- });
- dialog.show();
- }
-
- _on_button_remove_item_clicked() {
- let [ok, iterator] = this._locations.get_iter_first(),
- profile = this._get_location_profile(),
- profiles, index, model;
-
- if (!ok) return;
-
- if (this._locations.iter_n_children(iterator) === 1) {
- let dialog = new Gtk.MessageDialog({
- buttons: Gtk.ButtonsType.OK,
- message_type: Gtk.MessageType.ERROR,
- text: 'You cannot remove the last item in a profile',
- title: 'DeskChanger Error',
- transient_for: this.get_root(),
- });
- dialog.connect('response', (_dialog, response) => {
- _dialog.destroy();
- });
- dialog.show();
- return;
- }
-
- [ok, model, iterator] = this._tree_locations.get_selection().get_selected();
- index = this._locations.get_string_from_iter(iterator);
- this._locations.remove(iterator);
- profiles = deskchanger.settings.profiles;
- profiles[profile].splice(index);
- deskchanger.settings.profiles = profiles;
- }
-
- _on_button_remove_profile_clicked() {
- let [ok, iterator] = this._combo_location_profile.get_active_iter(),
- profile, profiles, dialog;
-
- if (!ok) return;
- profile = this._profiles.get_value(iterator, 0);
-
- if (deskchanger.settings.current_profile === profile) {
- dialog = new Gtk.MessageDialog({
- buttons: Gtk.ButtonsType.CLOSE,
- message_type: Gtk.MessageType.ERROR,
- text: 'You cannot remove the current profile',
- title: 'DeskChanger Error',
- transient_for: this.get_root(),
- });
- dialog.connect('response', (_dialog, response) => {
- _dialog.destroy();
- });
- dialog.show();
- return;
- }
-
- dialog = new Gtk.MessageDialog({
- buttons: Gtk.ButtonsType.YES_NO,
- message_type: Gtk.MessageType.QUESTION,
- text: `Are you sure you want to remove the profile "${profile}"?`,
- title: 'DeskChanger Confirm',
- transient_for: this.get_root(),
- });
- dialog.connect('response', (_dialog, response) => {
- if (response === Gtk.ResponseType.YES) {
- profiles = deskchanger.settings.profiles;
- delete profiles[profile];
- deskchanger.settings.profiles = profiles;
- this._load_profiles();
- }
-
- _dialog.destroy();
- });
- dialog.show();
- }
-
- _on_cell_location_edited(_widget, path, new_text) {
- let [ok, iterator] = this._locations.get_iter_from_string(path);
+ this._profilesPage = new ProfilesPage({model: this._profiles, selected: this._current_profile_index});
+ window.add(this._profilesPage);
- if (!ok) return;
- this._locations.set_value(iterator, 0, new_text);
- this._update_location_profile(path, 0, new_text);
- }
-
- _on_cell_recursive_toggled(_widget, path) {
- let [ok, iterator] = this._locations.get_iter_from_string(path),
- new_value;
-
- deskchanger.debug(`path: ${path}; ok: ${ok}; iterator: ${iterator}`);
- if (!ok) return;
- new_value = !this._locations.get_value(iterator, 1);
- this._locations.set_value(iterator, 1, new_value);
- this._update_location_profile(path, 1, new_value);
- }
-
- _on_combo_current_profile_changed() {
- let [ok, iterator] = this._combo_current_profile.get_active_iter(),
- profile;
+ this._keyboardPage = new KeyboardPage();
+ window.add(this._keyboardPage);
- if (this._is_init || !ok) return;
- profile = this._profiles.get_value(iterator, 0);
- deskchanger.settings.current_profile = profile;
- }
-
- _on_combo_location_profile_changed(_widget) {
- let [ok, iterator] = this._combo_location_profile.get_active_iter(),
- profile;
-
- if (!ok) return;
- profile = this._profiles.get_value(iterator, 0);
- this._locations.clear();
+ this._extensionPage = new ExtensionPage({model: this._profiles, selected: this._current_profile_index});
+ window.add(this._extensionPage);
- deskchanger.settings.profiles[profile].forEach(item => {
- let [uri, recursive] = item;
+ this._daemonPage = new DaemonPage();
+ window.add(this._daemonPage);
- iterator = this._locations.append();
- // TODO: fill in third parameter
- this._locations.set(iterator, [0, 1, 2], [uri, recursive, true]);
- });
+ this._aboutPage = new AboutPage();
+ window.add(this._aboutPage);
}
- _on_combo_rotation_mode_changed(_widget) {
- let [ok, iterator] = this._combo_rotation_mode.get_active_iter();
+ _load_profiles() {
+ const profiles = Object.entries(Interface.settings.profiles);
- if (this._is_init || !ok) return;
- deskchanger.settings.rotation = deskchanger.rotation.get_value(iterator, 0);
- }
+ this._profiles.remove_all();
- _on_response_add_items(_dialog, response)
- {
- if (response === Gtk.ResponseType.OK) {
- let list = _dialog.get_files(),
- length = (shellVersion < 40)? list.length : list.get_n_items(),
- profiles = deskchanger.settings.profiles,
- profile = this._get_location_profile();
- deskchanger.debug(typeof list);
+ for (const key in profiles) {
+ if (Interface.settings.current_profile === profiles[key][0])
+ this._current_profile_index = key;
- for (let i = 0; i < length; i++) {
- let item, values;
- item = (shellVersion < 40)? list[i] : list.get_item(i);
- values = [item.get_uri(), false, true];
- if (shellVersion < 40)
- this._locations.insert_with_valuesv(-1, [0, 1, 2], values);
- else
- this._locations.insert_with_values(-1, [0, 1, 2], values);
- profiles[profile].push(values);
- }
- deskchanger.settings.profiles = profiles;
+ const locations = Gio.ListStore.new(Location);
+ profiles[key][1].forEach(item => locations.append(new Location({location: item[0], recursive: item[1]})));
+ this._profiles.append(new Profile({ name: profiles[key][0], locations: locations }));
}
-
- _dialog.destroy();
}
-
- _on_switch_daemon_running_state(_widget, state) {
- if (this._is_init) return false;
-
- if (state)
- this._daemon.StartSync();
- else
- this._daemon.StopSync(false);
- return false;
- }
-
- _update_location_profile(path, column, value) {
- let profiles = deskchanger.settings.profiles,
- profile = this._get_location_profile();
-
- profiles[profile][Number.parseInt(path)][column] = value;
- deskchanger.settings.profiles = profiles;
- }
-});
-
-function init() {
- deskchanger.debug('init()');
- ExtensionUtils.initTranslations('desk-changer');
-}
-
-function buildPrefsWidget() {
- deskchanger.debug('buildPrefsWidget()');
- return new PrefsWidget();
-}
+}
\ No newline at end of file
diff --git a/desk-changer@eric.gach.gmail.com/service.js b/desk-changer@eric.gach.gmail.com/service.js
deleted file mode 100644
index 49bdbc7..0000000
--- a/desk-changer@eric.gach.gmail.com/service.js
+++ /dev/null
@@ -1,16 +0,0 @@
-'use strict';
-
-const Gio = imports.gi.Gio;
-const GObject = imports.gi.GObject;
-
-const Me = imports.misc.extensionUtils.getCurrentExtension();
-const Interface = Me.imports.daemon.interface;
-
-function makeProxyWrapper() {
- let proxy = Gio.DBusProxy.makeProxyWrapper(deskchanger.dbusxml);
- return new proxy(
- Gio.DBus.session,
- Interface.APP_ID,
- Interface.APP_PATH
- );
-}
diff --git a/desk-changer@eric.gach.gmail.com/ui/common/location_row.js b/desk-changer@eric.gach.gmail.com/ui/common/location_row.js
new file mode 100644
index 0000000..7875fdd
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/ui/common/location_row.js
@@ -0,0 +1,60 @@
+import Adw from 'gi://Adw';
+import GObject from 'gi://GObject';
+
+import { Location } from '../profiles.js';
+
+const LocationRow = GObject.registerClass({
+ GTypeName: 'LocationRow',
+ InternalChildren: [
+ 'recursive_switch',
+ 'remove_button',
+ ],
+ Properties: {
+ 'location': GObject.param_spec_object(
+ 'location',
+ 'Location',
+ 'Location object belonging to the model',
+ Location,
+ GObject.ParamFlags.READWRITE
+ ),
+ },
+ Template: `file:///home/eric/Projects/desk-changer/resources/ui/common/location_row.ui`,
+},
+class DeskChangerLocationRow extends Adw.ActionRow {
+ constructor(params = {}) {
+ let location = null;
+
+ if ('location' in params) {
+ location = params['location'];
+ delete params['location'];
+ }
+
+ super(params);
+
+ this._location = location;
+ }
+
+ get recursive_switch() {
+ return this._recursive_switch;
+ }
+
+ get remove_button() {
+ return this._remove_button;
+ }
+
+ get_location() {
+ if (this._location === undefined)
+ this._location = null;
+
+ return this._location;
+ }
+
+ set_location(value) {
+ if (this._location === value) return;
+
+ this._location = value;
+ this.notify('location');
+ }
+});
+
+export default LocationRow;
\ No newline at end of file
diff --git a/desk-changer@eric.gach.gmail.com/ui/common/profiles.js b/desk-changer@eric.gach.gmail.com/ui/common/profiles.js
new file mode 100644
index 0000000..5ea8668
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/ui/common/profiles.js
@@ -0,0 +1,98 @@
+import Gio from 'gi://Gio';
+import GObject from 'gi://GObject';
+
+export const Location = GObject.registerClass(
+{
+ Properties: {
+ 'location': GObject.ParamSpec.string(
+ 'location',
+ 'Location',
+ 'URI of the location for the daemon to load',
+ GObject.ParamFlags.READWRITE | GObject.ParamSpec.CONSTRUCT,
+ null
+ ),
+ 'recursive': GObject.ParamSpec.boolean(
+ 'recursive',
+ 'Recursive',
+ 'Tell the daemon to load the location recursively',
+ GObject.ParamFlags.READWRITE | GObject.ParamSpec.CONSTRUCT,
+ false
+ ),
+ }
+},
+class DeskChangerLocation extends GObject.Object {
+ get location() {
+ if (this._location === undefined)
+ this._location = null;
+
+ return this._location;
+ }
+
+ get recursive() {
+ return this._recursive;
+ }
+
+ set location(value) {
+ if (this._location === value) return;
+
+ this._location = value;
+ this.notify('location');
+ }
+
+ set recursive(value) {
+ if (this._recursive === value) return;
+
+ this._recursive = value;
+ this.notify('recursive');
+ }
+}
+);
+
+export const Profile = GObject.registerClass({
+ GTypeName: 'Profile',
+ Properties: {
+ 'locations': GObject.param_spec_object(
+ 'locations',
+ 'Locations',
+ 'Locations that are contained within the profile',
+ Gio.ListModel,
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT
+ ),
+ 'name': GObject.ParamSpec.string(
+ 'name',
+ 'Name',
+ 'Profile name to be displayed',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT,
+ null
+ ),
+ },
+},
+class DeskChangerProfile extends GObject.Object {
+ get locations() {
+ if (this._locations === undefined)
+ this._locations = null;
+
+ return this._locations;
+ }
+
+ get name() {
+ if (this._name === undefined)
+ this._name = null;
+
+ return this._name;
+ }
+
+ set locations(value) {
+ if (this._locations === value) return;
+
+ this._locations = value;
+ this.notify('locations');
+ }
+
+ set name(value) {
+ if (this._name === value) return;
+
+ this._name = value;
+ this.notify('name');
+ }
+});
\ No newline at end of file
diff --git a/desk-changer@eric.gach.gmail.com/ui/control.js b/desk-changer@eric.gach.gmail.com/ui/control.js
index c31506c..064ce72 100644
--- a/desk-changer@eric.gach.gmail.com/ui/control.js
+++ b/desk-changer@eric.gach.gmail.com/ui/control.js
@@ -1,24 +1,24 @@
-const Me = imports.misc.extensionUtils.getCurrentExtension();
+import Clutter from 'gi://Clutter';
+import Cogl from 'gi://Cogl';
+import GLib from 'gi://GLib';
+import GdkPixbuf from 'gi://GdkPixbuf';
+import GObject from 'gi://GObject';
+import St from 'gi://St?version=13';
-const Clutter = imports.gi.Clutter;
-const Cogl = imports.gi.Cogl;
-const GLib = imports.gi.GLib;
-const GdkPixbuf = imports.gi.GdkPixbuf;
-const GObject = imports.gi.GObject;
-const St = imports.gi.St;
+import * as Logger from '../common/logging.js';
-var ButtonControl = GObject.registerClass(
+export const ButtonControl = GObject.registerClass(
class DeskChangerControlButtonControl extends St.Button {
_init(icon, callback) {
this._icon = new St.Icon({icon_name: `${icon}-symbolic`, icon_size: 20});
super._init({child: this._icon, style_class: 'button'});
this._clicked_id = this.connect('clicked', callback);
- deskchanger.debug(`connect clicked (${this._clicked_id})`);
+ Logger.debug(`connect clicked (${this._clicked_id})`);
}
destroy() {
if (this._clicked_id) {
- deskchanger.debug(`disconnect clicked (${this._clicked_id})`);
+ Logger.debug(`disconnect clicked (${this._clicked_id})`);
this.disconnect(this._clicked_id);
}
this._clicked_id = null;
@@ -33,22 +33,22 @@ class DeskChangerControlButtonControl extends St.Button {
}
);
-var PreviewControl = GObject.registerClass(
+export const PreviewControl = GObject.registerClass(
class DeskChangerPreviewControl extends St.Bin {
- _init(width, daemon, callback) {
- super._init({ x_align: St.Align.MIDDLE });
+ _init(size = {height: -1, width: -1}, daemon, callback) {
+ super._init();
this._file = null;
this._callback = callback;
this._daemon = daemon;
this._texture = null;
- this._width = width;
+ this._size = size;
this._next_file_id = this._daemon.connectSignal('Preview', (proxy, name, [uri]) => {
- deskchanger.debug(`DBUS::Preview(${uri})`);
+ Logger.debug(`DBUS::Preview(${uri})`);
this.set_wallpaper(uri);
});
this._running_id = this._daemon.connectSignal('Running', (proxy, name, [running]) => {
if (running === false && this._texture) {
- deskchanger.debug('clearing preview, daemon stopped');
+ Logger.debug('clearing preview, daemon stopped');
this._texture.destroy();
this._texture = null;
}
@@ -88,24 +88,33 @@ var PreviewControl = GObject.registerClass(
this._file = file = GLib.uri_unescape_string(file, null);
file = file.replace('file://', '');
- deskchanger.debug('setting preview to %s'.format(file));
+ Logger.debug('setting preview to %s'.format(file));
try{
let scale_factor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
- let pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(file, this._width * scale_factor, -1, true);
- let height = Math.round(pixbuf.get_height() / (pixbuf.get_width() / this._width));
+ let pixbuf = GdkPixbuf.Pixbuf.new_from_file(file);
+ let { height, width } = this._size;
+ let original_height = pixbuf.get_height(), original_width = pixbuf.get_width();
+ pixbuf = null;
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(file, this._size.width, this._size.height, true);
+
+ if (height > 0 && width == -1)
+ width = Math.floor(original_width / (original_height / height));
+ else if (width > 0 && height == -1)
+ height = Math.floor(original_height / (original_width / width));
+
let image = new Clutter.Image();
image.set_data(
pixbuf.get_pixels(),
(pixbuf.get_has_alpha()? Cogl.PixelFormat.RGBA_8888 : Cogl.PixelFormat.RGB_888),
- this._width * scale_factor,
+ width * scale_factor,
height * scale_factor,
pixbuf.get_rowstride()
);
- this._texture = new Clutter.Actor({height: height * scale_factor, width: this._width * scale_factor});
+ this._texture = new Clutter.Actor({height: height * scale_factor, width: width * scale_factor});
this._texture.set_content(image);
this.add_actor(this._texture);
} catch (e) {
- deskchanger.error(e, `Failed to set preview of ${file}`);
+ Logger.error(e, `Failed to set preview of ${file}`);
if (this._texture) {
this._texture.destroy();
this._texture = null;
@@ -126,7 +135,7 @@ var PreviewControl = GObject.registerClass(
}
);
-var StateButtonControl = GObject.registerClass(
+export const StateButtonControl = GObject.registerClass(
class DeskChangerControlStateButtonControl extends ButtonControl {
_init(states, callback) {
if (states.length > 2) {
diff --git a/desk-changer@eric.gach.gmail.com/ui/dialog/add.js b/desk-changer@eric.gach.gmail.com/ui/dialog/add.js
new file mode 100644
index 0000000..c2434d4
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/ui/dialog/add.js
@@ -0,0 +1,18 @@
+import GObject from 'gi://GObject';
+import Gtk from 'gi://Gtk?version=4.0';
+import Interface from '../../daemon/interface.js';
+
+const DialogAdd = GObject.registerClass({
+ Template: `file:///home/eric/Projects/desk-changer/resources/ui/dialog/add.ui`,
+},
+class DeskChangerDialogAdd extends Gtk.FileDialog {
+ vfunc_constructed() {
+ const fileFilter = new Gtk.FileFilter();
+
+ super.vfunc_constructed();
+ Interface.settings.allowed_mime_types.forEach(mime_type => fileFilter.add_mime_type(mime_type));
+ this.set_default_filter(fileFilter);
+ }
+});
+
+export default DialogAdd;
\ No newline at end of file
diff --git a/desk-changer@eric.gach.gmail.com/ui/dialog/alert.js b/desk-changer@eric.gach.gmail.com/ui/dialog/alert.js
new file mode 100644
index 0000000..39277dc
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/ui/dialog/alert.js
@@ -0,0 +1,18 @@
+import GObject from 'gi://GObject';
+import Gtk from 'gi://Gtk?version=4.0';
+
+const DialogAlert = GObject.registerClass({
+ Template: `file:///home/eric/Projects/desk-changer/resources/ui/dialog/alert.ui`,
+},
+class DeskChangerDialogAlert extends Gtk.AlertDialog {
+ vfunc_constructed() {
+ super.vfunc_constructed();
+
+ this.set_buttons(['OK', 'Cancel']);
+ this.set_default_button(0);
+ this.set_cancel_button(1);
+ }
+}
+);
+
+export default DialogAlert;
\ No newline at end of file
diff --git a/desk-changer@eric.gach.gmail.com/ui/dialog/keybind.js b/desk-changer@eric.gach.gmail.com/ui/dialog/keybind.js
new file mode 100644
index 0000000..2f720d9
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/ui/dialog/keybind.js
@@ -0,0 +1,130 @@
+import Adw from 'gi://Adw';
+import Gdk from 'gi://Gdk';
+import GObject from 'gi://GObject';
+import Gtk from 'gi://Gtk?version=4.0';
+
+import Interface from '../../daemon/interface.js';
+
+const Keybind = GObject.registerClass({},
+class DeskChangerKeybind extends GObject.Object {
+});
+
+const KeybindDialog = GObject.registerClass({
+ GTypeName: 'KeybindDialog',
+ InternalChildren: [
+ 'cancel_button',
+ 'edit_box',
+ 'headerbar',
+ 'set_button',
+ 'shortcut_accel_label',
+ 'stack',
+ 'standard_box',
+ 'top_info_label',
+ ],
+ Properties: {
+ 'keybind_id': GObject.param_spec_string(
+ 'keybind_id',
+ 'Keybind ID',
+ 'DeskChanger keybind ID that this dialog is setting',
+ null,
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT
+ ),
+ 'keybind_name': GObject.param_spec_string(
+ 'keybind_name',
+ 'Keybind Name',
+ 'DeskChanger keybind name to display',
+ null,
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT
+ ),
+ },
+ Template: `file:///home/eric/Projects/desk-changer/resources/ui/dialog/keybind.ui`,
+},
+class DeskChangerKeybindDialog extends Adw.Window {
+ get keybind_id() {
+ return this._keybind_id;
+ }
+
+ get keybind_name() {
+ return this._keybind_name;
+ }
+
+ constructor(params={keybind_id: null, keybind_name: null}) {
+ const keybind_id = params['keybind_id'],
+ keybind_name = params['keybind_name'];
+
+ delete params['keybind_id'];
+ delete params['keybind_name'];
+
+ super(params);
+
+ this._keybind_id = keybind_id;
+ this._keybind_name = keybind_name;
+ }
+
+ vfunc_show() {
+ super.vfunc_show();
+
+ this._top_info_label.set_markup(`Enter new shortcut to change ${this.keybind_name}`);
+ this._keyval = null;
+ this._mask = null;
+ }
+
+ _is_valid_binding(keyval, keycode, mask) {
+ if ((mask === 0 || mask === Gdk.SHIFT_MASK) && keycode != 0) {
+ if ((keyval >= Gdk.KEY_a && keyval <= Gdk.KEY_z)
+ || (keyval >= Gdk.KEY_A && keyval <= Gdk.KEY_Z)
+ || (keyval >= Gdk.KEY_0 && keyval <= Gdk.KEY_9)
+ || (keyval == Gdk.KEY_space && mask === 0)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ _on_cancel_button_clicked(button) {
+ this._cancel_button.set_visible(false);
+ this._set_button.set_visible(false);
+ this._keyval = null;
+ this._mask = null;
+ this._stack.set_visible_child(this._edit_box);
+ }
+
+ _on_keybind_dialog_key_pressed(widget, keyval, keycode, state) {
+ const event = widget.get_current_event(),
+ explicit_modifiers = Gtk.accelerator_get_default_mod_mask() | Gdk.SHIFT_MASK,
+ mask = state & explicit_modifiers;
+
+ if (!event.is_modifier() && mask === 0 && (keyval === Gdk.KEY_BackSpace || keyval === Gdk.KEY_Escape)) {
+ if (keyval === Gdk.KEY_BackSpace) {
+ Interface.settings.reset(this.keybind_id);
+ this.close();
+ }
+
+ return Gdk.Event.STOP;
+ }
+
+ this._set_custom_keybind(keyval, keycode, mask);
+ return Gdk.Event.STOP;
+ }
+
+ _on_set_button_clicked(button) {
+ Interface.settings.setKeybinding(this.keybind_id, Gtk.accelerator_name(this._keyval, this._mask));
+ this.close();
+ }
+
+ _set_custom_keybind(keyval, keycode, mask) {
+ if (!Gtk.accelerator_valid(keyval, mask) || !this._is_valid_binding(keyval, keycode, mask))
+ return;
+
+ console.log(keyval, keycode, mask);
+ this._shortcut_accel_label.set_accelerator(Gtk.accelerator_name(keyval, mask));
+ this._stack.set_visible_child(this._standard_box);
+ this._cancel_button.set_visible(true);
+ this._set_button.set_visible(true);
+ this._keyval = keyval;
+ this._mask = mask;
+ }
+});
+
+export default KeybindDialog;
\ No newline at end of file
diff --git a/desk-changer@eric.gach.gmail.com/ui/dialog/new.js b/desk-changer@eric.gach.gmail.com/ui/dialog/new.js
new file mode 100644
index 0000000..c6a0db7
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/ui/dialog/new.js
@@ -0,0 +1,28 @@
+import GObject from 'gi://GObject';
+import Gtk from 'gi://Gtk?version=4.0';
+import Interface from '../../daemon/interface.js';
+
+const NewDialog = GObject.registerClass({
+ GTypeName: 'NewDialog',
+ InternalChildren: [
+ 'cancel_button',
+ 'entry',
+ 'save_button',
+ ],
+ Template: `file:///home/eric/Projects/desk-changer/resources/ui/dialog/new.ui`,
+},
+class DeskChangerNewDialog extends Gtk.Window {
+ get cancel_button() {
+ return this._cancel_button;
+ }
+
+ get entry() {
+ return this._entry;
+ }
+
+ get save_button() {
+ return this._save_button;
+ }
+});
+
+export default NewDialog;
\ No newline at end of file
diff --git a/desk-changer@eric.gach.gmail.com/ui/panelMenu.js b/desk-changer@eric.gach.gmail.com/ui/panelMenu.js
index 79c0eb2..b6fd4fe 100644
--- a/desk-changer@eric.gach.gmail.com/ui/panelMenu.js
+++ b/desk-changer@eric.gach.gmail.com/ui/panelMenu.js
@@ -1,16 +1,19 @@
-const Me = imports.misc.extensionUtils.getCurrentExtension();
-const Convenience = Me.imports.convenience;
-const DeskChangerPopupMenu = Me.imports.ui.popupMenu;
-const DeskChangerControl = Me.imports.ui.control;
+'use strict';
-const Gio = imports.gi.Gio;
-const GObject = imports.gi.GObject;
-const PanelMenu = imports.ui.panelMenu;
-const PopupMenu = imports.ui.popupMenu;
-const St = imports.gi.St;
-const Util = imports.misc.util;
+import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js';
+import Gio from 'gi://Gio';
+import GObject from 'gi://GObject';
+import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
+import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
+import St from 'gi://St';
-var Button = GObject.registerClass(
+import DeskChanger from '../deskchanger.js';
+import Interface from '../daemon/interface.js';
+import * as DeskChangerLogging from '../common/logging.js';
+import * as DeskChangerControl from './control.js';
+import * as DeskChangerPopupMenu from './popupMenu.js';
+
+export const Button = GObject.registerClass(
class DeskChangerPanelMenuButton extends PanelMenu.Button {
_init(daemon) {
super._init(0.0, 'DeskChanger');
@@ -26,12 +29,9 @@ class DeskChangerPanelMenuButton extends PanelMenu.Button {
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
let menu_item = new PopupMenu.PopupMenuItem(_('DeskChanger Settings'));
- menu_item.connect('activate', function () {
- if ('openPrefs' in imports.misc.extensionUtils) {
- imports.misc.extensionUtils.openPrefs();
- } else {
- Util.spawn(['gnome-shell-extension-prefs', Me.metadata.uuid]);
- }
+ menu_item.connect('activate', () => {
+ const extensionObject = Extension.lookupByUUID(DeskChanger.metadata.uuid);
+ extensionObject.openPreferences();
});
this.menu.addMenuItem(menu_item);
}
@@ -43,26 +43,26 @@ class DeskChangerPanelMenuButton extends PanelMenu.Button {
}
);
-let Icon = GObject.registerClass(
+export const Icon = GObject.registerClass(
class DeskChangerPanelMenuIcon extends St.Bin {
_init(daemon) {
this._daemon = daemon;
- this._gicon = Gio.icon_new_for_string(`resource://${deskchanger.app_path}/icons/wallpaper-icon.svg`);
+ this._gicon = Gio.icon_new_for_string(`resource://${DeskChanger.app_path}/icons/wallpaper-icon.svg`);
super._init({
style_class: 'panel-status-menu-box',
});
this._icon = null;
this._preview = null;
- this.update_child(this._daemon.Preview);
+ this.update_child();
- this._preview_id = deskchanger.settings.connect('changed::icon-preview', (settings, key) => {
- this.update_child(this._daemon.Preview);
+ this._preview_id = Interface.settings.connect('changed::icon-preview', (settings, key) => {
+ this.update_child();
});
}
destroy() {
if (this._preview_id) {
- deskchanger.settings.disconnect(this._preview_id);
+ Interface.settings.disconnect(this._preview_id);
}
this._destroy_icon();
@@ -70,8 +70,10 @@ class DeskChangerPanelMenuIcon extends St.Bin {
super.destroy();
}
- update_child(file) {
- if (deskchanger.settings.icon_preview && this._create_preview(file)) {
+ update_child() {
+ if (Interface.settings.icon_preview) {
+ this._destroy_preview();
+ this._preview = new DeskChangerControl.PreviewControl({height: 32, width: -1}, this._daemon);
this.set_child(this._preview);
this._destroy_icon();
} else if (!(this._icon)) {
@@ -84,22 +86,6 @@ class DeskChangerPanelMenuIcon extends St.Bin {
}
}
- _create_preview(file) {
- this._destroy_preview();
- this._preview = new DeskChangerControl.PreviewControl(34, this._daemon, this.update_child.bind(this));
-
- if (!(this._preview.file)) {
- if (typeof file === 'string') {
- this._preview.set_wallpaper(file);
- } else {
- this._destroy_preview();
- return false;
- }
- }
-
- return true;
- }
-
_destroy_icon() {
if (this._icon) {
this._icon.destroy();
diff --git a/desk-changer@eric.gach.gmail.com/ui/popupMenu.js b/desk-changer@eric.gach.gmail.com/ui/popupMenu.js
index 4ee9369..7e23d56 100644
--- a/desk-changer@eric.gach.gmail.com/ui/popupMenu.js
+++ b/desk-changer@eric.gach.gmail.com/ui/popupMenu.js
@@ -1,20 +1,18 @@
'use strict';
-const Me = imports.misc.extensionUtils.getCurrentExtension();
-const Gettext = imports.gettext.domain(Me.metadata.uuid);
-const DeskChangerControl = Me.imports.ui.control;
-
-const Gio = imports.gi.Gio;
-const GObject = imports.gi.GObject;
-const Main = imports.ui.main;
-const Meta = imports.gi.Meta;
-const PopupMenu = imports.ui.popupMenu;
-const Shell = imports.gi.Shell;
-const St = imports.gi.St;
-const Util = imports.misc.util;
-const _ = Gettext.gettext;
-
-var ControlsMenuItem = GObject.registerClass(
+import Gio from 'gi://Gio';
+import GObject from 'gi://GObject';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import Meta from 'gi://Meta';
+import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
+import Shell from 'gi://Shell';
+import St from 'gi://St';
+
+import Interface from '../daemon/interface.js';
+import * as DeskChangerControl from './control.js';
+import * as Logger from '../common/logging.js';
+
+export const ControlsMenuItem = GObject.registerClass(
class DeskChangerPopupMenuControlsMenuItem extends PopupMenu.PopupBaseMenuItem {
_init(daemon) {
super._init({'can_focus': false, 'reactive': false});
@@ -45,10 +43,10 @@ class DeskChangerPopupMenuControlsMenuItem extends PopupMenu.PopupBaseMenuItem {
name: 'ordered',
},
], (state) => {
- deskchanger.debug(`setting order to ${state}`);
- deskchanger.settings.random = (state === 'random');
+ Logger.debug(`setting order to ${state}`);
+ Interface.settings.random = (state === 'random');
});
- this._random.set_state((deskchanger.settings.random)? 'random' : 'ordered');
+ this._random.set_state((Interface.settings.random)? 'random' : 'ordered');
this.add_child(this._prev);
this.add_child(this._random);
@@ -59,7 +57,7 @@ class DeskChangerPopupMenuControlsMenuItem extends PopupMenu.PopupBaseMenuItem {
let success = false;
success = Main.wm.addKeybinding(
key,
- deskchanger.settings,
+ Interface.settings,
Meta.KeyBindingFlags.NONE,
Shell.ActionMode.NORMAL,
handler
@@ -67,10 +65,10 @@ class DeskChangerPopupMenuControlsMenuItem extends PopupMenu.PopupBaseMenuItem {
this._bindings.push(key);
if (success) {
- deskchanger.debug('added keybinding ' + key);
+ Logger.debug('added keybinding ' + key);
} else {
- deskchanger.debug('failed to add keybinding ' + key);
- deskchanger.debug(success);
+ Logger.debug('failed to add keybinding ' + key);
+ Logger.debug(success);
}
}
@@ -79,26 +77,26 @@ class DeskChangerPopupMenuControlsMenuItem extends PopupMenu.PopupBaseMenuItem {
this._bindings.splice(this._bindings.indexOf(key), 1);
}
- deskchanger.debug('removing keybinding ' + key);
+ Logger.debug('removing keybinding ' + key);
Main.wm.removeKeybinding(key);
}
}
);
-var OpenCurrentMenuItem = GObject.registerClass(
+export const OpenCurrentMenuItem = GObject.registerClass(
class DeskChangerPopupMenuOpenCurrent extends PopupMenu.PopupMenuItem {
_init() {
super._init(_('Open current wallpaper'));
this._background = new Gio.Settings({'schema': 'org.gnome.desktop.background'});
this._activate_id = this.connect('activate', () => {
- deskchanger.debug(`opening current wallpaper ${this._background.get_string('picture-uri')}`);
+ Logger.debug(`opening current wallpaper ${this._background.get_string('picture-uri')}`);
Gio.AppInfo.launch_default_for_uri(this._background.get_string('picture-uri'), global.create_app_launch_context(0, -1));
});
- deskchanger.debug(`connect active (${this._activate_id})`);
+ Logger.debug(`connect active (${this._activate_id})`);
}
destroy() {
- deskchanger.debug(`disconnect active (${this._activate_id})`);
+ Logger.debug(`disconnect active (${this._activate_id})`);
this.disconnect(this._activate_id);
super.destroy();
@@ -106,7 +104,7 @@ class DeskChangerPopupMenuOpenCurrent extends PopupMenu.PopupMenuItem {
}
);
-let PopupMenuItem = GObject.registerClass(
+export const PopupMenuItem = GObject.registerClass(
class DeskChangerPopupMenuItem extends PopupMenu.PopupMenuItem {
_init(label, value, key) {
super._init(label);
@@ -114,12 +112,12 @@ class DeskChangerPopupMenuItem extends PopupMenu.PopupMenuItem {
this._key = key;
this._key_normalized = key.replace('_', '-');
- if (deskchanger.settings[this._key] === this._value) {
+ if (Interface.settings[this._key] === this._value) {
this.setOrnament(PopupMenu.Ornament.DOT);
}
- this._handler_key_changed = deskchanger.settings.connect(`changed::${this._key_normalized}`, () => {
- if (deskchanger.settings[key] === value) {
+ this._handler_key_changed = Interface.settings.connect(`changed::${this._key_normalized}`, () => {
+ if (Interface.settings[key] === value) {
this.setOrnament(PopupMenu.Ornament.DOT);
} else {
this.setOrnament(PopupMenu.Ornament.NONE);
@@ -127,13 +125,13 @@ class DeskChangerPopupMenuItem extends PopupMenu.PopupMenuItem {
});
this._handler_id = this.connect('activate', () => {
- deskchanger.settings[key] = value;
+ Interface.settings[key] = value;
});
}
destroy() {
if (this._handler_key_changed) {
- deskchanger.settings.disconnect(this._handler_key_changed);
+ Interface.settings.disconnect(this._handler_key_changed);
this._handler_key_changed = null;
}
@@ -147,20 +145,20 @@ class DeskChangerPopupMenuItem extends PopupMenu.PopupMenuItem {
}
);
-let PopupSubMenuMenuItem = GObject.registerClass(
+export const PopupSubMenuMenuItem = GObject.registerClass(
class DeskChangerPopupSubMenuMenuItem extends PopupMenu.PopupSubMenuMenuItem {
_init(prefix, key, sensitive=true) {
- super._init(`${prefix}: ${deskchanger.settings[key]}`);
+ super._init(`${prefix}: ${Interface.settings[key]}`);
this._prefix = prefix;
- this._changed_id = deskchanger.settings.connect(`changed::${key.replace('_', '-')}`, () => {
- this.setLabel(deskchanger.settings[key]);
+ this._changed_id = Interface.settings.connect(`changed::${key.replace('_', '-')}`, () => {
+ this.setLabel(Interface.settings[key]);
});
this.setSensitive(sensitive);
}
destroy() {
if (this._changed_id) {
- deskchanger.settings.disconnect(this._changed_id);
+ Interface.settings.disconnect(this._changed_id);
}
super.destroy();
@@ -172,7 +170,7 @@ class DeskChangerPopupSubMenuMenuItem extends PopupMenu.PopupSubMenuMenuItem {
}
);
-var PreviewMenuItem = GObject.registerClass(
+export const PreviewMenuItem = GObject.registerClass(
class DeskChangerPopupMenuPreviewMenuItem extends PopupMenu.PopupBaseMenuItem {
_init(daemon) {
super._init({reactive: true});
@@ -180,21 +178,21 @@ class DeskChangerPopupMenuPreviewMenuItem extends PopupMenu.PopupBaseMenuItem {
this.add_actor(this._box);
this._prefix = new St.Label({text: _('Open next wallpaper')});
this._box.add(this._prefix);
- this._preview = new DeskChangerControl.PreviewControl(220, daemon);
+ this._preview = new DeskChangerControl.PreviewControl({height: -1, width: 220}, daemon);
this._box.add(this._preview);
this._activate_id = this.connect('activate', () => {
if (this._preview.file) {
- deskchanger.debug(`opening file ${this._preview.file}`);
+ Logger.debug(`opening file ${this._preview.file}`);
Gio.AppInfo.launch_default_for_uri(this._preview.file, global.create_app_launch_context(0, -1));
} else {
Utils.error('no preview set');
}
});
- deskchanger.debug(`connect activate ${this._activate_id}`);
+ Logger.debug(`connect activate ${this._activate_id}`);
}
destroy() {
- deskchanger.debug(`disconnect activate ${this._activate_id}`);
+ Logger.debug(`disconnect activate ${this._activate_id}`);
this.disconnect(this._activate_id);
this._preview.destroy();
@@ -205,26 +203,26 @@ class DeskChangerPopupMenuPreviewMenuItem extends PopupMenu.PopupBaseMenuItem {
}
);
-let ProfileMenuItem = GObject.registerClass({
+export const ProfileMenuItem = GObject.registerClass({
Abstract: true,
},
class DeskChangerPopupSubMenuMenuItemProfile extends PopupSubMenuMenuItem {
_init(label, key, sensitive=true) {
- super._init(label, key, deskchanger.settings, sensitive);
- this._profiles_changed_id = deskchanger.settings.connect('changed::profiles', () => {
+ super._init(label, key, Interface.settings, sensitive);
+ this._profiles_changed_id = Interface.settings.connect('changed::profiles', () => {
this._populate_profiles(key);
});
this._populate_profiles(key);
}
destroy() {
- deskchanger.settings.disconnect(this._profiles_changed_id);
+ Interface.settings.disconnect(this._profiles_changed_id);
super.destroy();
}
_populate_profiles(key) {
this.menu.removeAll();
- for (let index in deskchanger.settings.profiles) {
+ for (let index in Interface.settings.profiles) {
let item = new PopupMenuItem(index, index, key);
this.menu.addMenuItem(item);
}
@@ -232,7 +230,7 @@ class DeskChangerPopupSubMenuMenuItemProfile extends PopupSubMenuMenuItem {
}
);
-var ProfileDesktopMenuItem = GObject.registerClass(
+export const ProfileDesktopMenuItem = GObject.registerClass(
class DeskChangerPopupSubMenuMenuItemProfileDesktop extends ProfileMenuItem {
_init(sensitive = true) {
super._init(_('Desktop Profile'), 'current_profile', sensitive);
diff --git a/desk-changer@eric.gach.gmail.com/ui/prefs/about.js b/desk-changer@eric.gach.gmail.com/ui/prefs/about.js
new file mode 100644
index 0000000..8eadaa8
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/ui/prefs/about.js
@@ -0,0 +1,23 @@
+import Adw from 'gi://Adw';
+import GObject from 'gi://GObject';
+import DeskChanger from '../../deskchanger.js';
+
+const AboutPage = GObject.registerClass({
+ GTypeName: 'AboutPage',
+ InternalChildren: [
+ 'description_label',
+ 'url_label',
+ 'version_label',
+ ],
+ Template: `file:///home/eric/Projects/desk-changer/resources/ui/prefs/about.ui`,
+},
+class DeskChangerPreferencesAboutPage extends Adw.PreferencesPage {
+ vfunc_realize(widget) {
+ super.vfunc_realize();
+ this._description_label.set_label(DeskChanger.metadata.description);
+ this._url_label.set_label(`${DeskChanger.metadata.url}`);
+ this._version_label.set_label(`Version ${DeskChanger.metadata.version}`);
+ }
+});
+
+export default AboutPage;
\ No newline at end of file
diff --git a/desk-changer@eric.gach.gmail.com/ui/prefs/daemon.js b/desk-changer@eric.gach.gmail.com/ui/prefs/daemon.js
new file mode 100644
index 0000000..6cd847b
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/ui/prefs/daemon.js
@@ -0,0 +1,134 @@
+import Adw from 'gi://Adw';
+import Gio from 'gi://Gio';
+import GObject from 'gi://GObject';
+import Gtk from 'gi://Gtk?version=4.0';
+
+import Interface from '../../daemon/interface.js';
+import DaemonMetaTypeRow from './daemon/meta_type_row.js';
+import RotationModes from '../../common/rotation.js';
+import { makeProxyWrapper } from '../../common/service.js';
+import NewDialog from '../dialog/new.js';
+import { debug } from '../../common/logging.js';
+
+const DaemonPage = GObject.registerClass({
+ GTypeName: 'DaemonPage',
+ InternalChildren: [
+ 'allowed_mime_types_listbox',
+ 'allowed_mime_types_reset_button',
+ 'daemon_auto_start_switch',
+ 'daemon_remember_profile_state_switch',
+ 'daemon_running_switch',
+ 'rotation_custom_interval_spinner',
+ 'rotation_mode_combo',
+ ],
+ Template: `file:///home/eric/Projects/desk-changer/resources/ui/prefs/daemon.ui`,
+},
+class DeskChangerPreferencesDaemonPage extends Adw.PreferencesPage {
+ vfunc_realize(widget) {
+ this._daemon = makeProxyWrapper();
+ this._allowed_mime_types_id = null;
+ this._rotation_mode_combo_position = 0;
+
+ super.vfunc_realize();
+ this._daemon_running_switch.set_active(this._daemon.Running);
+ this._rotation_mode_combo.set_model(RotationModes);
+ Interface.settings.bind('auto-start', this._daemon_auto_start_switch, 'active', Gio.SettingsBindFlags.DEFAULT);
+ Interface.settings.bind('remember-profile-state', this._daemon_remember_profile_state_switch, 'active', Gio.SettingsBindFlags.DEFAULT);
+ Interface.settings.bind('interval', this._rotation_custom_interval_spinner, 'value', Gio.SettingsBindFlags.DEFAULT);
+ this._daemon_running_id = this._daemon.connectSignal('Running', () => {
+ this._daemon_running_switch.set_active(this._daemon.Running);
+ });
+ this._load_mime_types();
+ this._set_rotation_mode_combo_position();
+
+ this._rotation_mode_combo_notify_id = this._rotation_mode_combo.connect('notify::selected-item', () => {
+ Interface.settings.rotation = this._rotation_mode_combo.selected_item.key;
+ });
+ }
+
+ vfunc_unrealize(widget) {
+ if (this._allowed_mime_types_id)
+ Interface.settings.disconnect(this._allowed_mime_types_id);
+
+ if (this._daemon_running_id)
+ this._daemon.disconnectSignal(this._daemon_running_id);
+
+ if (this._rotation_mode_combo_notify_id)
+ this._rotation_mode_combo.disconnect(this._rotation_mode_combo_notify_id);
+
+ Interface.settings.unbind(this._daemon_auto_start_switch, 'active');
+ Interface.settings.unbind(this._daemon_remember_profile_state_switch, 'active');
+ Interface.settings.unbind(this._rotation_custom_interval_spinner, 'value');
+ this._daemon = null;
+ super.vfunc_unrealize();
+ }
+
+ _load_mime_types() {
+ const mime_types = Interface.settings.allowed_mime_types;
+
+ if (this._allowed_mime_types_id) {
+ Interface.settings.disconnect(this._allowed_mime_types_id);
+ }
+
+ if (this._button_add_id) {
+ this._button_add.disconnect(this._button_add_id);
+ }
+
+ this._allowed_mime_types_listbox.remove_all();
+
+ this._button_add = new Gtk.Button({icon_name: 'list-add-symbolic', css_classes: ['flat']});
+ this._button_add_id = this._button_add.connect('clicked', button => this._on_allowed_mime_types_add_button_clicked(button));
+ this._allowed_mime_types_listbox.append(this._button_add)
+ this._allowed_mime_types_reset_button.set_visible(Interface.settings.get_user_value('allowed-mime-types'));
+ mime_types.forEach(mime_type => this._allowed_mime_types_listbox.append(new DaemonMetaTypeRow({title: mime_type})));
+ this._allowed_mime_types_id = Interface.settings.connect('changed::allowed-mime-types', () => this._load_mime_types());
+ }
+
+ _on_allowed_mime_types_add_button_clicked(button) {
+ const dialog = new NewDialog({title: 'New MIME Type'});
+
+ dialog.set_default_size(340, -1);
+ dialog.cancel_button.connect('clicked', widget => widget.get_root().close());
+ dialog.save_button.connect('clicked', widget => {
+ const window = widget.get_root(),
+ mime_type = window.entry.get_text();
+
+ if (mime_type.length > 0) {
+ const allowed_mime_types = Interface.settings.allowed_mime_types;
+
+ allowed_mime_types.push(mime_type);
+ Interface.settings.allowed_mime_types = allowed_mime_types;
+ window.close();
+ }
+ });
+ dialog.set_transient_for(this.get_root());
+ dialog.present();
+ }
+
+ _on_allowed_mime_types_reset_button_clicked(button) {
+ Interface.settings.reset('allowed-mime-types');
+ }
+
+ _on_rotation_mode_combo_factory_bind(widget, item) {
+ const label = item.get_child(),
+ rotationMode = item.get_item();
+
+ label.set_label(rotationMode.label);
+
+ if (rotationMode.key === String(Interface.settings.rotation)) {
+ this._rotation_mode_combo_position = item.get_position();
+ }
+ }
+
+ _on_rotation_mode_combo_factory_setup(widget, item) {
+ const label = new Gtk.Label();
+
+ item.set_child(label);
+ }
+
+ _set_rotation_mode_combo_position() {
+ this._rotation_mode_combo.set_selected(this._rotation_mode_combo_position);
+ }
+});
+
+export default DaemonPage;
\ No newline at end of file
diff --git a/desk-changer@eric.gach.gmail.com/ui/prefs/daemon/meta_type_row.js b/desk-changer@eric.gach.gmail.com/ui/prefs/daemon/meta_type_row.js
new file mode 100644
index 0000000..6699fd2
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/ui/prefs/daemon/meta_type_row.js
@@ -0,0 +1,31 @@
+import Adw from 'gi://Adw';
+import GObject from 'gi://GObject';
+import Interface from '../../../daemon/interface.js';
+import DialogAlert from '../../dialog/alert.js';
+
+const DaemonMetaTypeRow = GObject.registerClass({
+ GTypeName: 'DaemonMetaTypeRow',
+ InternalChildren: [
+ 'delete_button',
+ ],
+ Template: `file:///home/eric/Projects/desk-changer/resources/ui/prefs/daemon/meta_type_row.ui`,
+},
+class DeskChangerPreferencesDaemonMetaTypeRow extends Adw.ActionRow {
+ _on_meta_row_delete_button_clicked(button) {
+ const mime_type = button.get_parent().get_parent().get_parent().title,
+ dialog = new DialogAlert({message: `Are you sure you want to remove the MIME type ${mime_type}?`});
+
+ dialog.choose(this.get_root(), null, (widget, response) => {
+ const result = widget.choose_finish(response);
+
+ if (result === 0) {
+ const mime_types = Interface.settings.allowed_mime_types;
+
+ mime_types.splice(mime_types.indexOf(mime_type), 1);
+ Interface.settings.allowed_mime_types = mime_types;
+ }
+ });
+ }
+});
+
+export default DaemonMetaTypeRow;
diff --git a/desk-changer@eric.gach.gmail.com/ui/prefs/extension.js b/desk-changer@eric.gach.gmail.com/ui/prefs/extension.js
new file mode 100644
index 0000000..b066ec9
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/ui/prefs/extension.js
@@ -0,0 +1,74 @@
+import Adw from 'gi://Adw';
+import Gio from 'gi://Gio';
+import GObject from 'gi://GObject';
+import Gtk from 'gi://Gtk?version=4.0';
+import Interface from '../../daemon/interface.js';
+
+const ExtensionPage = GObject.registerClass({
+ GTypeName: 'ExtensionPage',
+ InternalChildren: [
+ 'current_profile_combo',
+ 'icon_preview_switch',
+ 'notifications_switch',
+ ],
+ Template: `file:///home/eric/Projects/desk-changer/resources/ui/prefs/extension.ui`,
+},
+class DeskChangerPreferencesExtensionPage extends Adw.PreferencesPage {
+ constructor(params={}) {
+ let model = null,
+ selected = null;
+
+ if ('model' in params) {
+ model = params['model'];
+ delete params['model'];
+ } else {
+ throw new Error('model is required');
+ }
+
+ if ('selected' in params) {
+ selected = params['selected'];
+ delete params['selected'];
+ }
+
+ super(params);
+
+ this._current_profile_combo.set_model(model);
+
+ if (selected)
+ this._current_profile_combo.set_selected(selected);
+
+ Interface.settings.bind('icon-preview', this._icon_preview_switch, 'active', Gio.SettingsBindFlags.DEFAULT);
+ Interface.settings.bind('notifications', this._notifications_switch, 'active', Gio.SettingsBindFlags.DEFAULT);
+
+ this._selected_changed_id = this._current_profile_combo.connect('notify::selected', (object, _pspec) => {
+ Interface.settings.current_profile = object.get_selected_item().name;
+ });
+ }
+
+ destroy() {
+ Interface.settings.unbind(this._icon_preview_switch, 'active')
+ Interface.settings.unbind(this._notifications_switch, 'active');
+
+ if (this._selected_changed_id)
+ this._current_profile_combo.disconnect(this._selected_changed_id);
+
+ super.destroy();
+ }
+
+ _on_current_profile_combo_factory_bind(wigdet, item) {
+ const label = item.get_child(),
+ profile = item.get_item();
+
+ console.log(label);
+ label.set_label(profile.name);
+ }
+
+ _on_current_profile_combo_factory_setup(widget, item) {
+ const label = new Gtk.Label();
+
+ console.log(label);
+ item.set_child(label);
+ }
+});
+
+export default ExtensionPage;
\ No newline at end of file
diff --git a/desk-changer@eric.gach.gmail.com/ui/prefs/keyboard.js b/desk-changer@eric.gach.gmail.com/ui/prefs/keyboard.js
new file mode 100644
index 0000000..0f00e42
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/ui/prefs/keyboard.js
@@ -0,0 +1,125 @@
+import Adw from 'gi://Adw';
+import GLib from 'gi://GLib';
+import Gio from 'gi://Gio';
+import GObject from 'gi://GObject';
+import Gtk from 'gi://Gtk?version=4.0';
+import KeyboardShortcutRow from './keyboard/shortcut_row.js';
+
+const KeyMapping = GObject.registerClass({
+ Properties: {
+ 'keyval': GObject.param_spec_uint(
+ 'keyval',
+ 'Keyval',
+ 'Key value returned by signal',
+ 0,
+ GLib.MAXUINT8,
+ 0,
+ GObject.ParamFlags.READWRITE
+ ),
+ 'keycode': GObject.param_spec_uint(
+ 'keycode',
+ 'Keycode',
+ 'Key code returned by signal',
+ 0,
+ GLib.MAXUINT8,
+ 0,
+ GObject.ParamFlags.READWRITE
+ ),
+ },
+},
+class DeskChangerPreferencesKeyMapping extends GObject.Object {
+ get keycode() {
+ if (this._keycode === undefined)
+ this._keycode = 0;
+
+ return this._keycode;
+ }
+
+ get keyval() {
+ if (this._keyval === undefined)
+ this._keyval = 0;
+
+ return this._keyval;
+ }
+
+ set keycode(value) {
+ if (value === this._keycode) return;
+
+ this._keycode = Number.parseInt(value);
+ this.notify('keycode');
+ }
+
+ set keyval(value) {
+ if (value === this._keyval) return;
+
+ this._keyval = Number.parseInt(value);
+ this._notify('keyval');
+ }
+});
+
+const KeyboardMapping = GObject.registerClass({
+ GTypeName: 'KeyboardMapping',
+ Properties: {
+ 'action': GObject.param_spec_string(
+ 'action',
+ 'Action',
+ 'Action performed when key mapping is activated',
+ null,
+ GObject.ParamFlags.READWRITE
+ ),
+ 'mapping': GObject.param_spec_object(
+ 'mapping',
+ 'Mapping',
+ 'Key mapping to perform the action indicated',
+ KeyMapping,
+ GObject.ParamFlags.READWRITE
+ ),
+ },
+},
+class DeskChangerPreferencesKeyboardMapping extends GObject.Object {
+ get action() {
+ if (this._action === undefined)
+ this._action = null;
+
+ return this._action;
+ }
+
+ get mapping() {
+ if (this._mapping === undefined)
+ this._mapping = null;
+
+ return this._mapping;
+ }
+
+ set action(value) {
+ if (value === this._action) return;
+
+ this._action = String(value);
+ this.notify('action');
+ }
+
+ set mapping(value) {
+ if (value === this._mapping) return;
+
+ this._mapping = value;
+ this.notify('mapping');
+ }
+});
+
+const KeyboardPage = GObject.registerClass({
+ GTypeName: 'KeyboardPage',
+ InternalChildren: [
+ 'keymap_listbox',
+ ],
+ Template: `file:///home/eric/Projects/desk-changer/resources/ui/prefs/keyboard/page.ui`,
+},
+class DeskChangerPreferencesKeyboardPage extends Adw.PreferencesPage {
+ constructor(params={}) {
+ super(params);
+
+ this._keymap_listbox.append(new KeyboardShortcutRow({title: 'Next Wallpaper', keybind: 'next-wallpaper'}));
+ this._keymap_listbox.append(new KeyboardShortcutRow({title: 'Previous Wallpaper', keybind: 'prev-wallpaper'}));
+ }
+});
+
+export default KeyboardPage;
\ No newline at end of file
diff --git a/desk-changer@eric.gach.gmail.com/ui/prefs/keyboard/shortcut_row.js b/desk-changer@eric.gach.gmail.com/ui/prefs/keyboard/shortcut_row.js
new file mode 100644
index 0000000..2b37028
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/ui/prefs/keyboard/shortcut_row.js
@@ -0,0 +1,78 @@
+import Adw from 'gi://Adw';
+import GObject from 'gi://GObject';
+import Gtk from 'gi://Gtk?version=4.0';
+
+import KeybindDialog from '../../dialog/keybind.js';
+import Interface from '../../../daemon/interface.js';
+
+const KeyboardShortcutRow = GObject.registerClass({
+ GTypeName: 'KeyboardShortcutRow',
+ InternalChildren: [
+ 'accelerator_label',
+ ],
+ Properties: {
+ 'keybind': GObject.param_spec_string(
+ 'keybind',
+ 'Keybind',
+ 'The DeskChanger specific keybind id that this row manages',
+ null,
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT
+ ),
+ },
+ Template: `file:///home/eric/Projects/desk-changer/resources/ui/prefs/keyboard/shortcutrow.ui`,
+},
+class DeskChangerPreferencesKeyboardShortcutRow extends Adw.ActionRow {
+ get keybind() {
+ if (this._keybind === undefined)
+ this._keybind = null;
+
+ return this._keybind;
+ }
+
+ constructor(params={keybind: null}) {
+ const keybind = params['keybind'];
+
+ delete params['keybind'];
+
+ super(params);
+
+ this._keybind = keybind;
+ }
+
+ vfunc_realize() {
+ super.vfunc_realize();
+
+ this.set_activatable(true);
+ this._activated_id = this.connect('activated', widget => this._on_activated(widget));
+ this._settings_id = Interface.settings.connect(`changed::${this.keybind}`, () => this._update_keybind_from_settings());
+ this._update_keybind_from_settings();
+ }
+
+ vfunc_unrealize() {
+ if (this._activated_id) {
+ this.disconnect(this._activated_id);
+ this._activated_id = null;
+ }
+
+ if (this._settings_id) {
+ this.disconnect(this._settings_id);
+ this._settings_id = null;
+ }
+ }
+
+ _on_activated(widget) {
+ const dialog = new KeybindDialog({
+ keybind_id: this.keybind,
+ keybind_name: this.get_title(),
+ transient_for: this.get_root(),
+ });
+
+ dialog.present();
+ }
+
+ _update_keybind_from_settings() {
+ this._accelerator_label.set_accelerator(Interface.settings.getKeybinding(this.keybind));
+ }
+});
+
+export default KeyboardShortcutRow;
\ No newline at end of file
diff --git a/desk-changer@eric.gach.gmail.com/ui/prefs/profiles.js b/desk-changer@eric.gach.gmail.com/ui/prefs/profiles.js
new file mode 100644
index 0000000..12294da
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/ui/prefs/profiles.js
@@ -0,0 +1,201 @@
+import Adw from 'gi://Adw';
+import GObject from 'gi://GObject';
+import Gtk from 'gi://Gtk?version=4.0';
+
+import DialogAdd from '../dialog/add.js';
+import DialogAlert from '../dialog/alert.js';
+import Interface from '../../daemon/interface.js';
+import { Location } from '../common/profiles.js';
+import NewDialog from '../dialog/new.js';
+
+const ProfilesPage = GObject.registerClass({
+ GTypeName: 'ProfilesPage',
+ InternalChildren: [
+ 'add_profile_button',
+ 'combo_row_profiles',
+ 'factory_row_profiles',
+ 'locations_listview',
+ 'locations_selection',
+ 'remove_item_button',
+ 'remove_profile_button',
+ ],
+ Template: 'file:///home/eric/Projects/desk-changer/resources/ui/prefs/profiles.ui',
+},
+class DeskChangerPreferencesProfilesPage extends Adw.PreferencesPage {
+ constructor(params={}) {
+ let model = null,
+ selected = null;
+
+ if ('model' in params) {
+ model = params['model'];
+ delete params['model'];
+ }
+
+ if ('selected' in params) {
+ selected = params['selected'];
+ delete params['selected'];
+ }
+
+ super(params);
+
+ this._dialog_items = null;
+ this._combo_row_profiles.set_model(model);
+ this._combo_row_profiles.selected = selected;
+ this._combo_row_profiles.connect('notify::selected-item', actionRow => this._on_notify_selected_item(actionRow));
+
+ this._locations_selection.set_model(this._combo_row_profiles.selected_item.locations);
+ }
+
+ get combo_row_profiles() {
+ return this._combo_row_profiles;
+ }
+
+ vfunc_realize() {
+ this._locations_selection.connect('selection-changed', (model, position, n_items) => {
+ this._remove_item_button.set_sensitive(this._locations_selection.get_model().get_n_items() > 1 && position !== -1);
+ });
+
+ console.log(this._combo_row_profiles.get_model().get_n_items() > 1);
+ this._remove_profile_button.set_sensitive(this._combo_row_profiles.get_model().get_n_items() > 1);
+
+ super.vfunc_realize();
+ }
+
+ _on_add_folder_button_clicked() {
+ const dialog = new DialogAdd();
+
+ dialog.set_title('Add Folder(s)');
+ dialog.select_multiple_folders(this.get_root(), null, this._on_dialog_add_response.bind(this));
+ this._dialog_items = false;
+ }
+
+ _on_add_item_button_clicked() {
+ const dialog = new DialogAdd();
+
+ dialog.set_title('Add Image(s)');
+ dialog.open_multiple(this.get_root(), null, this._on_dialog_add_response.bind(this));
+ this._dialog_items = true;
+ }
+
+ _on_add_profile_button_clicked() {
+ const dialog = new NewDialog({title: 'Add Profile'});
+
+ dialog.set_transient_for(this.get_root());
+ dialog.cancel_button.connect('clicked', button => dialog.close());
+ dialog.save_button.connect('clicked', button => {
+ const window = button.get_root(),
+ profile = dialog.entry.get_text();
+
+ if (profile.length > 0) {
+ const profiles = Interface.settings.profiles,
+ model = this._combo_row_profiles.get_model();
+ profiles[profile] = Array();
+ Interface.settings.profiles = profiles;
+ window.close();
+
+ for (let i = 0; i < model.get_n_items(); i++) {
+ const item = model.get_item(i);
+
+ if (item.name === profile) {
+ this._combo_row_profiles.set_selected(i);
+ break;
+ }
+ }
+ }
+ });
+ dialog.present();
+ }
+
+ _on_dialog_add_response(dialog, response) {
+ const list = (this._dialog_items)? dialog.open_multiple_finish(response) : dialog.select_multiple_folders_finish(response),
+ length = list.get_n_items(),
+ profile = Interface.settings.current_profile;
+ let profiles = Interface.settings.profiles;
+
+ for (let i = 0; i < length; i++) {
+ const item = list.get_item(i),
+ values = [item.get_uri(), false, true];
+
+ profiles[profile].push(values);
+ this._locations_selection.get_model().append(new Location({location: item.get_uri(), recursive: false}))
+ }
+
+ Interface.settings.profiles = profiles;
+ this._dialog_items = null;
+ }
+
+ _on_factory_row_profiles_bind(widget, item) {
+ const label = item.get_child(),
+ profile = item.get_item();
+
+ label.set_label(profile.name);
+ }
+
+ _on_factory_row_profiles_setup(widget, item) {
+ const label = new Gtk.Label();
+ item.set_child(label);
+ }
+
+ _on_locations_factory_bind(widget, item) {
+ const row = item.get_child(),
+ location = item.get_item();
+
+ row.set_title(location.location);
+ row.set_active(location.recursive);
+ row.connect('notify::active', (object, pspec) => {
+ let profiles = Interface.settings.profiles;
+ const index = profiles[this._combo_row_profiles.selected_item.name].findIndex(element => element[0] === object.title);
+
+ profiles[this._combo_row_profiles.selected_item.name][index][1] = object.active;
+ Interface.settings.profiles = profiles;
+ });
+ }
+
+ _on_locations_factory_setup(widget, item) {
+ const row = new Adw.SwitchRow();
+
+ item.set_child(row);
+ }
+
+ _on_notify_selected_item(widget) {
+ this._locations_selection.set_model(widget.selected_item.locations);
+ }
+
+ _on_remove_item_button_clicked() {
+ const dialog = new DialogAlert(),
+ profile = this._combo_row_profiles.get_selected_item(),
+ location = this._locations_selection.get_selected_item();
+
+ dialog.set_message(`Are you sure you want to remove ${location.location} from ${profile.name}?`);
+ dialog.choose(this.get_root(), null, (dialog, response) => {
+ const result = dialog.choose_finish(response);
+
+ if (result === 0) {
+ let profiles = Interface.settings.profiles;
+
+ profiles[profile.name].splice(profiles[profile.name].indexOf([location.location, location.recursive]), 1);
+ Interface.settings.profiles = profiles;
+ this._locations_selection.get_model().remove(this._locations_selection.get_selected());
+ this._remove_item_button.set_sensitive(false);
+ }
+ });
+ }
+
+ _on_remove_profile_button_clicked() {
+ const dialog = new DialogAlert();
+
+ dialog.set_message(`Are you sure you want to remove the profile ${this._combo_row_profiles.get_selected_item().name}?`);
+ dialog.choose(this.get_root(), null, (dialog, response) => {
+ const result = dialog.choose_finish(response);
+
+ if (result === 0) {
+ let profiles = Interface.settings.profiles;
+
+ delete profiles[this._combo_row_profiles.get_selected_item().name];
+ Interface.settings.profiles = profiles;
+ }
+ });
+ }
+});
+
+export default ProfilesPage;
diff --git a/desk-changer@eric.gach.gmail.com/ui/profiles.js b/desk-changer@eric.gach.gmail.com/ui/profiles.js
new file mode 100644
index 0000000..5135e53
--- /dev/null
+++ b/desk-changer@eric.gach.gmail.com/ui/profiles.js
@@ -0,0 +1,2 @@
+import GObject from 'gi://GObject';
+import Gtk from 'gi://Gtk?version=4.0';
diff --git a/resources/Daemon/org.gnome.Shell.Extensions.DeskChanger.Daemon.xml b/resources/Daemon/org.gnome.Shell.Extensions.DeskChanger.Daemon.xml
new file mode 100644
index 0000000..065f599
--- /dev/null
+++ b/resources/Daemon/org.gnome.Shell.Extensions.DeskChanger.Daemon.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/enter-keyboard-shortcut.svg b/resources/enter-keyboard-shortcut.svg
new file mode 100644
index 0000000..ef26b82
--- /dev/null
+++ b/resources/enter-keyboard-shortcut.svg
@@ -0,0 +1,243 @@
+
+
+
+
diff --git a/resources/org.gnome.Shell.Extensions.DeskChanger.gresource.xml b/resources/org.gnome.Shell.Extensions.DeskChanger.gresource.xml
index 237f33b..acd1c9e 100644
--- a/resources/org.gnome.Shell.Extensions.DeskChanger.gresource.xml
+++ b/resources/org.gnome.Shell.Extensions.DeskChanger.gresource.xml
@@ -1,11 +1,23 @@
+ enter-keyboard-shortcut.svg
icons/wallpaper-icon.svg
- org.gnome.Shell.Extensions.DeskChanger.xml
+ Daemon/org.gnome.Shell.Extensions.DeskChanger.Daemon.xml
org.gnome.Shell.Extensions.DeskChanger.Daemon.service.in
- ui/prefs.3.ui
- ui/prefs.ui
+ ui/dialog/add.ui
+ ui/dialog/alert.ui
+ ui/dialog/keybind.ui
+ ui/dialog/new.ui
+ ui/dialog/remove.ui
+ ui/prefs/about.ui
+ ui/prefs/daemon.ui
+ ui/prefs/daemon/meta_type_row.ui
+ ui/prefs/extension.ui
+ ui/prefs/keyboard.ui
+ ui/prefs/keyboard/page.ui
+ ui/prefs/keyboard/shortcutrow.ui
+ ui/prefs/profiles.ui
ui/rotation.ui
\ No newline at end of file
diff --git a/resources/ui/dialog/add.ui b/resources/ui/dialog/add.ui
new file mode 100644
index 0000000..09fb2fe
--- /dev/null
+++ b/resources/ui/dialog/add.ui
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/resources/ui/dialog/alert.ui b/resources/ui/dialog/alert.ui
new file mode 100644
index 0000000..a3ac6d5
--- /dev/null
+++ b/resources/ui/dialog/alert.ui
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/resources/ui/dialog/keybind.ui b/resources/ui/dialog/keybind.ui
new file mode 100644
index 0000000..b0dd5ab
--- /dev/null
+++ b/resources/ui/dialog/keybind.ui
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+ 300
+ True
+ True
+ False
+ Set Shortcut
+ 400
+
+
+
+
+ action(window.close)
+ Escape
+
+
+
+
+
+
+ capture
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/ui/dialog/new.ui b/resources/ui/dialog/new.ui
new file mode 100644
index 0000000..f4049a1
--- /dev/null
+++ b/resources/ui/dialog/new.ui
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+ 10
+ 10
+ 10
+ 10
+ vertical
+ 5
+
+
+
+
+
+ end
+ 10
+ 10
+ 10
+ 10
+ 5
+
+
+ Save
+
+
+
+
+ Cancel
+
+
+
+
+
+
+ True
+ True
+ False
+
+
diff --git a/resources/ui/dialog/remove.ui b/resources/ui/dialog/remove.ui
new file mode 100644
index 0000000..9856ac7
--- /dev/null
+++ b/resources/ui/dialog/remove.ui
@@ -0,0 +1,11 @@
+
+
+
+
+
+ OK
+Cancel
+ 1
+ 0
+
+
diff --git a/resources/ui/prefs/about.glade b/resources/ui/prefs/about.glade
new file mode 100644
index 0000000..173cea2
--- /dev/null
+++ b/resources/ui/prefs/about.glade
@@ -0,0 +1,104 @@
+
+
+
+
+
+ True
+ False
+ 10
+ 10
+ 10
+ 10
+ vertical
+ 5
+
+
+ True
+ False
+ 20
+ 20
+ 20
+ 20
+ /org/gnome/Shell/Extensions/DeskChanger/icons/wallpaper-icon.svg
+
+
+ True
+ True
+ 0
+
+
+
+
+ True
+ False
+ DeskChanger
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ False
+ Version 31
+
+
+ False
+ True
+ 2
+
+
+
+
+ True
+ False
+ 10
+ 10
+ 10
+ 10
+ Simple wallpaper changer with multiple profile support. Integrates into the shell by providing it's own panel icon. The daemon is written using gjs and runs independently of the extension as a background process.
+ center
+ True
+
+
+ False
+ True
+ 3
+
+
+
+
+ True
+ False
+ <a href="https://github.com/BigE/desk-changer">https://github.com/BigE/desk-changer</a>
+ True
+
+
+ False
+ True
+ 4
+
+
+
+
+ True
+ False
+ <span size="small">This program comes with absolutely no warranty.
+ See the <a href="https://opensource.org/licenses/mit-license.php">The MIT License (MIT)</a> for details.</span>
+ True
+ center
+
+
+ False
+ True
+ 5
+
+
+
+
diff --git a/resources/ui/prefs/about.ui b/resources/ui/prefs/about.ui
new file mode 100644
index 0000000..0a9109c
--- /dev/null
+++ b/resources/ui/prefs/about.ui
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+ dialog-information-symbolic
+ About
+
+
+
+
+ 10
+ 10
+ 10
+ 10
+ vertical
+ 5
+
+
+ True
+ True
+ /org/gnome/Shell/Extensions/DeskChanger/icons/wallpaper-icon.svg
+ True
+ True
+
+
+
+
+ <b>DeskChanger</b>
+ True
+
+
+
+
+ Version 36
+
+
+
+
+ Simple wallpaper changer with multiple profile support. Integrates into the shell by providing it's own panel icon. The daemon is written using gjs and runs independently of the extension as a background process.
+ True
+
+
+
+
+ <a href="https://github.com/BigE/desk-changer">https://github.com/BigE/desk-changer</a>
+ True
+
+
+
+
+ <span size="small">This program comes with absolutely no warranty.
+See the <a href="https://opensource.org/licenses/mit-license.php">The MIT License (MIT)</a> for details.</span>
+ True
+ True
+
+
+
+
+
+
+
+
diff --git a/resources/ui/prefs/daemon.glade b/resources/ui/prefs/daemon.glade
new file mode 100644
index 0000000..1bce04e
--- /dev/null
+++ b/resources/ui/prefs/daemon.glade
@@ -0,0 +1,298 @@
+
+
+
+
+
+
+
+
+ 1
+ 84600
+ 1
+ 1
+ 10
+
+
+ True
+ True
+ 5
+ 5
+ 5
+ 5
+ vertical
+ 10
+
+
+ True
+ False
+ 0
+
+
+ True
+ False
+ 5
+ 5
+ 5
+ 5
+ vertical
+ 5
+
+
+ True
+ False
+
+
+ True
+ False
+ The rotation mode of the damon. Interval will rotate at the specified timer interval. Hourly will rotate at the top of each hour. Disabled will disable automatic rotation.
+ Mode
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ end
+ 0
+
+
+
+
+ 2
+
+
+
+
+ True
+ True
+ 1
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+
+
+ True
+ False
+ This only applies to the Interval Timer option and sets the interval at which the timer will change the wallpaper.
+ Custom Timer (in seconds)
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ True
+ end
+ interval
+
+
+ True
+ True
+ 1
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+
+
+ True
+ False
+ Rotation Options
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Ensure that the daemon process is running when the extension is loaded.
+
+
+ True
+ False
+ Auto Start Daemon
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ True
+ end
+
+
+ True
+ True
+ 1
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ False
+ If enabled the daemon will save the current and next wallpaper when it stops. When it starts, if the values are present, it will then use them to restore the current wallpaper and fill the queue.
+
+
+ True
+ False
+ Remember Profile State
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ True
+ end
+
+
+ True
+ True
+ 1
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+ True
+ False
+ The current status of the daemon itself. Toggling this will swith the entire daemon process on/off respectively.
+
+
+ True
+ False
+ Daemon Running
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ True
+ end
+
+
+
+ True
+ True
+ 1
+
+
+
+
+ False
+ True
+ 3
+
+
+
+
+ True
+ True
+ vertical
+ 5
+
+
+ True
+ False
+ start
+ Allowed Mime Types
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ True
+ 0
+
+
+ 200
+ 100
+ True
+ True
+ allowed_mime_types
+ False
+ True
+
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ True
+ 4
+
+
+
+
diff --git a/resources/ui/prefs/daemon.ui b/resources/ui/prefs/daemon.ui
new file mode 100644
index 0000000..e170593
--- /dev/null
+++ b/resources/ui/prefs/daemon.ui
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+ application-x-executable-symbolic
+ Daemon
+
+
+ Rotation Options
+
+
+
+
+
+
+
+
+ Mode
+
+
+
+
+
+
+ 1.0
+ 10.0
+ 1.0
+ 86400.0
+ 500.0
+
+
+ Custom Interval
+ False
+
+
+
+
+
+
+ Daemon
+
+
+ Auto Start
+
+
+
+
+ Remember Profile State
+ False
+
+
+
+
+ Currently Running
+ False
+
+
+
+
+
+
+
+
+ Reset
+ False
+
+
+
+
+ Allowed MIME Types
+
+
+ none
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/ui/prefs/daemon/meta_type_row.ui b/resources/ui/prefs/daemon/meta_type_row.ui
new file mode 100644
index 0000000..e4f57cb
--- /dev/null
+++ b/resources/ui/prefs/daemon/meta_type_row.ui
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ META Type
+
+
+ user-trash-symbolic
+
+
+
+
+
+
diff --git a/resources/ui/prefs/extension.glade b/resources/ui/prefs/extension.glade
new file mode 100644
index 0000000..b393198
--- /dev/null
+++ b/resources/ui/prefs/extension.glade
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+
+
+
+
+ True
+ False
+ 5
+ 5
+ 5
+ 5
+ vertical
+ 10
+
+
+ True
+ False
+
+
+ True
+ False
+ Current Profile
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ end
+ profiles
+
+
+
+
+ 0
+
+
+
+
+ True
+ True
+ 1
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+
+
+ True
+ False
+ Show Icon as Preview
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ True
+ end
+
+
+ True
+ True
+ 1
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ False
+
+
+ True
+ False
+ Notifications
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ True
+ end
+
+
+ True
+ True
+ 1
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
diff --git a/resources/ui/prefs/extension.ui b/resources/ui/prefs/extension.ui
new file mode 100644
index 0000000..176456d
--- /dev/null
+++ b/resources/ui/prefs/extension.ui
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+ applications-system-symbolic
+ Extension
+
+
+ Extension Settings
+
+
+
+
+
+
+
+
+ Current Profile
+
+
+
+
+ Icon as Preview
+ False
+
+
+
+
+ Notifications
+ False
+
+
+
+
+
+
diff --git a/resources/ui/prefs/keyboard.glade b/resources/ui/prefs/keyboard.glade
new file mode 100644
index 0000000..458905e
--- /dev/null
+++ b/resources/ui/prefs/keyboard.glade
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Next Wallpaper
+ 0
+ 0
+ next-wallpaper
+
+
+ Previous Wallpaper
+ 0
+ 0
+ prev-wallpaper
+
+
+
+
+ True
+ True
+ vertical
+
+
+ True
+ True
+ keyboard
+
+
+
+
+
+ Shortcut
+ True
+
+
+
+ 0
+
+
+
+
+
+
+ Key Combination
+
+
+ True
+
+
+
+
+ 2
+ 1
+
+
+
+
+
+
+ True
+ True
+ 0
+
+
+
+
diff --git a/resources/ui/prefs/keyboard.ui b/resources/ui/prefs/keyboard.ui
new file mode 100644
index 0000000..ebf768a
--- /dev/null
+++ b/resources/ui/prefs/keyboard.ui
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+ input-keyboard-symbolic
+ Keyboard
+
+
+
+
+ True
+
+
+
+ False
+ True
+ True
+ True
+
+
+
+
+
+
+ True
+
+
+
+
+
+
+
+
+
+ Action
+
+
+
+
+
+
+
+
+ Mapping
+
+
diff --git a/resources/ui/prefs/keyboard/page.ui b/resources/ui/prefs/keyboard/page.ui
new file mode 100644
index 0000000..1a1f1e9
--- /dev/null
+++ b/resources/ui/prefs/keyboard/page.ui
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+ input-keyboard-symbolic
+ Keyboard
+
+
+ Shortcuts
+
+
+
+
+ True
+ none
+
+
+
+
+
+
+
+
+
diff --git a/resources/ui/prefs/keyboard/shortcutrow.ui b/resources/ui/prefs/keyboard/shortcutrow.ui
new file mode 100644
index 0000000..1705bd7
--- /dev/null
+++ b/resources/ui/prefs/keyboard/shortcutrow.ui
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+ False
+ Title
+ False
+
+
+
+
+ Disabled
+
+
+
+
+
+
+ Reset
+
+
+
+ slide-right
+
+
+
+
+
+
diff --git a/resources/ui/prefs/profiles.glade b/resources/ui/prefs/profiles.glade
new file mode 100644
index 0000000..2c03c30
--- /dev/null
+++ b/resources/ui/prefs/profiles.glade
@@ -0,0 +1,243 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ 5
+ 5
+ 5
+ 5
+ vertical
+ 5
+
+
+ True
+ True
+ 0
+
+
+ True
+ True
+ vertical
+
+
+ True
+ True
+ locations
+
+
+
+
+
+ Location URI
+ True
+
+
+ True
+
+
+
+ 0
+
+
+
+
+
+
+ Recursive
+
+
+
+
+
+ 1
+
+
+
+
+
+
+ True
+ True
+ 0
+
+
+
+
+ True
+ False
+ 5
+ 5
+ 5
+ 5
+ 5
+
+
+ Add Item(s)
+ True
+ True
+ True
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ Add Folder(s)
+ True
+ True
+ True
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ Remove Item
+ True
+ True
+ True
+
+
+
+ False
+ True
+ 2
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+
+
+
+
+
+ True
+ True
+ 0
+
+
+
+
+ True
+ False
+ Edit the locations of the currently selected profile.
+
+
+ True
+ False
+ start
+ Edit Profile
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ end
+ profiles
+ 0
+
+
+
+
+ 0
+
+
+
+
+ True
+ True
+ 1
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ False
+ end
+ 5
+
+
+ Add New Profile
+ True
+ True
+ True
+ Add a new profile
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ Remove Selected Profile
+ True
+ True
+ True
+ Remove the selected profile
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
diff --git a/resources/ui/prefs/profiles.ui b/resources/ui/prefs/profiles.ui
new file mode 100644
index 0000000..802f22c
--- /dev/null
+++ b/resources/ui/prefs/profiles.ui
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+ view-list-symbolic
+ Profiles
+
+
+ Locations
+
+
+
+
+
+
+
+
+
+
+ False
+ True
+
+
+ True
+ True
+
+
+
+
+
+ True
+ 10
+ 10
+ 5
+
+
+ start
+ Add Item(s)
+
+
+
+
+
+ start
+ True
+ Add Folder(s)
+
+
+
+
+
+ Remove Item
+ False
+
+
+
+
+
+
+
+
+
+ Profile
+
+
+
+
+
+
+
+
+ Edit Profile
+
+
+
+
+ end
+ 10
+ 10
+ 5
+
+
+ Add Profile
+
+
+
+
+
+ Remove Profile
+ False
+
+
+
+
+
+
+
+
+
diff --git a/resources/ui/profiles.ui b/resources/ui/profiles.ui
new file mode 100644
index 0000000..cb14ce7
--- /dev/null
+++ b/resources/ui/profiles.ui
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+