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","ProfilesPage",None,None,None,None,-1,None), + (3,2,"AdwPreferencesGroup",None,1,None,None,None,-1,None), + (3,8,"AdwPreferencesGroup",None,1,None,None,None,-1,None), + (3,9,"AdwComboRow","combo_row_profiles",8,None,None,None,-1,None), + (3,14,"GtkSignalListItemFactory","factory_row_profiles",9,None,None,None,-1,None), + (3,19,"GtkBox",None,8,None,None,None,-1,None), + (3,20,"GtkButton","add_profile_button",19,None,None,None,None,None), + (3,21,"GtkButton","remove_profile_button",19,None,None,None,1,None), + (3,22,"GtkBox",None,2,None,None,None,1,None), + (3,23,"GtkButton","add_item_button",22,None,None,None,None,None), + (3,24,"GtkButton","add_folder_button",22,None,None,None,1,None), + (3,25,"GtkListView","locations_listview",2,None,None,None,None,None), + (3,26,"GtkSignalListItemFactory","locations_factory",25,None,None,None,-1,None), + (3,27,"GtkSingleSelection","locations_selection",25,None,None,None,-1,None), + (3,28,"GtkButton","remove_item_button",22,None,None,None,2,None), + (4,1,"AdwPreferencesPage","KeyboardPage",None,None,None,None,-1,None), + (4,2,"AdwPreferencesGroup",None,1,None,None,None,-1,None), + (4,3,"GtkBox",None,2,None,None,None,-1,None), + (4,4,"GtkListBox","keymap_listbox",3,None,None,None,None,None), + (5,1,"AdwPreferencesPage","ExtensionPage",None,None,None,None,-1,None), + (5,2,"AdwPreferencesGroup",None,1,None,None,None,-1,None), + (5,3,"AdwComboRow","current_profile_combo",2,None,None,None,None,None), + (5,4,"GtkSignalListItemFactory",None,3,None,None,None,-1,None), + (5,5,"AdwSwitchRow","notifications_switch",2,None,None,None,2,None), + (5,6,"AdwSwitchRow","icon_preview_switch",2,None,None,None,1,None), + (6,1,"AdwPreferencesPage","DaemonPage",None,None,None,None,-1,None), + (6,2,"AdwPreferencesGroup",None,1,None,None,None,-1,None), + (6,3,"AdwComboRow","rotation_mode_combo",2,None,None,None,-1,None), + (6,4,"GtkSignalListItemFactory",None,3,None,None,None,-1,None), + (6,5,"AdwSpinRow","rotation_custom_interval_spinner",2,None,None,None,-1,None), + (6,6,"GtkAdjustment",None,5,None,None,None,-1,None), + (6,7,"AdwPreferencesGroup",None,1,None,None,None,-1,None), + (6,8,"AdwSwitchRow","daemon_auto_start_switch",7,None,None,None,-1,None), + (6,9,"AdwSwitchRow","daemon_remember_profile_state_switch",7,None,None,None,-1,None), + (6,10,"AdwSwitchRow","daemon_running_switch",7,None,None,None,-1,None), + (6,11,"AdwPreferencesGroup",None,1,None,None,None,-1,None), + (6,12,"GtkListBox","allowed_mime_types_listbox",11,None,None,None,-1,None), + (6,13,"GtkListBoxRow",None,12,None,None,None,2,None), + (6,15,"GtkButton","allowed_mime_types_reset_button",11,None,None,None,-1,None), + (7,1,"AdwPreferencesPage","AboutPage",None,None,None,None,-1,None), + (7,9,"AdwPreferencesGroup",None,1,None,None,None,1,None), + (7,10,"GtkBox",None,9,None,None,None,None,None), + (7,11,"GtkImage",None,10,None,None,None,None,None), + (7,12,"GtkLabel","name_label",10,None,None,None,1,None), + (7,13,"GtkLabel","version_label",10,None,None,None,2,None), + (7,14,"GtkLabel","description_label",10,None,None,None,3,None), + (7,15,"GtkLabel","url_label",10,None,None,None,4,None), + (7,16,"GtkLabel","license_label",10,None,None,None,5,None), + (8,1,"AdwActionRow","KeyboardShortcutRow",None,None,None,None,-1,None), + (8,2,"GtkBox",None,1,None,None,None,-1,None), + (8,4,"GtkRevealer",None,2,None,None,None,1,None), + (8,5,"GtkButton",None,4,None,None,None,-1,None), + (8,6,"GtkShortcutLabel","accelerator_label",2,None,None,None,None,None), + (10,1,"AdwActionRow","DaemonMetaTypeRow",None,None,None,None,-1,None), + (10,2,"GtkButton","delete_button",1,None,None,None,-1,None), + (11,1,"GtkFileDialog","DialogAdd",None,None,None,None,-1,None), + (12,1,"GtkAlertDialog","DialogAlert",None,None,None,None,-1,None), + (14,1,"GtkWindow","NewDialog",None,None,None,None,-1,None), + (14,2,"GtkBox",None,1,None,None,None,-1,None), + (14,4,"GtkBox",None,2,None,None,None,1,None), + (14,5,"GtkButton","save_button",4,None,None,None,None,None), + (14,6,"GtkButton","cancel_button",4,None,None,None,1,None), + (14,7,"GtkEntry","entry",2,None,None,None,None,None), + (15,1,"AdwWindow","KeybindDialog",None,None,None,None,-1,None), + (15,2,"AdwToolbarView",None,1,None,None,None,-1,None), + (15,3,"AdwHeaderBar","headerbar",2,None,"top",None,-1,None), + (15,9,"GtkShortcutController",None,1,None,None,None,None,None), + (15,10,"GtkShortcut",None,9,None,None,None,None,None), + (15,11,"GtkEventControllerKey",None,1,None,None,None,1,None), + (15,12,"GtkButton","cancel_button",3,None,"start",None,-1,None), + (15,15,"GtkButton","set_button",3,None,"end",None,-1,None), + (15,16,"GtkSizeGroup",None,None,None,None,None,-1,None), + (15,17,"GtkBox",None,2,None,None,None,-1,None), + (15,19,"GtkStack","stack",17,None,None,None,1,None), + (15,20,"GtkBox","standard_box",19,None,None,None,1,None), + (15,25,"GtkBox","edit_box",19,None,None,None,None,None), + (15,30,"GtkLabel","top_info_label",17,None,None,None,None,None), + (15,31,"GtkPicture",None,25,None,None,None,1,None), + (15,32,"GtkLabel",None,25,None,None,None,2,None), + (15,33,"GtkShortcutLabel","shortcut_accel_label",20,None,None,None,None,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 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + +