/* * This file is part of the Zorin Taskbar extension for Zorin OS. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * * Credits: * This file is based on code from the Dash to Dock extension by micheleg * and code from the Dash to Panel extension * Some code was also adapted from the upstream Gnome Shell source code. */ const GdkPixbuf = imports.gi.GdkPixbuf; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Gdk = imports.gi.Gdk; const Lang = imports.lang; const Mainloop = imports.mainloop; const Me = imports.misc.extensionUtils.getCurrentExtension(); const Convenience = Me.imports.convenience; const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']); const _ = Gettext.gettext; const N_ = function(e) { return e }; const Pos = Me.imports.panelPositions; const SCALE_UPDATE_TIMEOUT = 500; const DEFAULT_PANEL_SIZES = [ 128, 96, 64, 48, 32, 24 ]; const DEFAULT_MARGIN_SIZES = [ 32, 24, 16, 12, 8, 4, 0 ]; const DEFAULT_PADDING_SIZES = [ 32, 24, 16, 12, 8, 4, 0, -1 ]; const SCHEMA_PATH = '/org/gnome/shell/extensions/zorin-taskbar/'; const GSET = 'gnome-shell-extension-tool'; /** * This function was copied from the activities-config extension * https://github.com/nls1729/acme-code/tree/master/activities-config * by Norman L. Smith. */ function cssHexString(css) { let rrggbb = '#'; let start; for (let loop = 0; loop < 3; loop++) { let end = 0; let xx = ''; for (let loop = 0; loop < 2; loop++) { while (true) { let x = css.slice(end, end + 1); if ((x == '(') || (x == ',') || (x == ')')) break; end++; } if (loop == 0) { end++; start = end; } } xx = parseInt(css.slice(start, end)).toString(16); if (xx.length == 1) xx = '0' + xx; rrggbb += xx; css = css.slice(end); } return rrggbb; } function setShortcut(settings, shortcutName) { let shortcut_text = settings.get_string(shortcutName + '-text'); let [key, mods] = Gtk.accelerator_parse(shortcut_text); if (Gtk.accelerator_valid(key, mods)) { let shortcut = Gtk.accelerator_name(key, mods); settings.set_strv(shortcutName, [shortcut]); } else { settings.set_strv(shortcutName, []); } } function checkHotkeyPrefix(settings) { settings.delay(); let hotkeyPrefix = settings.get_string('hotkey-prefix-text'); if (hotkeyPrefix == 'Super') hotkeyPrefix = ''; else if (hotkeyPrefix == 'SuperAlt') hotkeyPrefix = ''; let [, mods] = Gtk.accelerator_parse(hotkeyPrefix); let [, shift_mods] = Gtk.accelerator_parse('' + hotkeyPrefix); let [, ctrl_mods] = Gtk.accelerator_parse('' + hotkeyPrefix); let numHotkeys = 10; for (let i = 1; i <= numHotkeys; i++) { let number = i; if (number == 10) number = 0; let key = Gdk.keyval_from_name(number.toString()); let key_kp = Gdk.keyval_from_name('KP_' + number.toString()); if (Gtk.accelerator_valid(key, mods)) { let shortcut = Gtk.accelerator_name(key, mods); let shortcut_kp = Gtk.accelerator_name(key_kp, mods); // Setup shortcut strings settings.set_strv('app-hotkey-' + i, [shortcut]); settings.set_strv('app-hotkey-kp-' + i, [shortcut_kp]); // With shortcut = Gtk.accelerator_name(key, shift_mods); shortcut_kp = Gtk.accelerator_name(key_kp, shift_mods); settings.set_strv('app-shift-hotkey-' + i, [shortcut]); settings.set_strv('app-shift-hotkey-kp-' + i, [shortcut_kp]); // With shortcut = Gtk.accelerator_name(key, ctrl_mods); shortcut_kp = Gtk.accelerator_name(key_kp, ctrl_mods); settings.set_strv('app-ctrl-hotkey-' + i, [shortcut]); settings.set_strv('app-ctrl-hotkey-kp-' + i, [shortcut_kp]); } else { // Reset default settings for the relevant keys if the // accelerators are invalid let keys = ['app-hotkey-' + i, 'app-shift-hotkey-' + i, 'app-ctrl-hotkey-' + i, // Regular numbers 'app-hotkey-kp-' + i, 'app-shift-hotkey-kp-' + i, 'app-ctrl-hotkey-kp-' + i]; // Key-pad numbers keys.forEach(function(val) { settings.set_value(val, settings.get_default_value(val)); }, this); } } settings.apply(); } function mergeObjects(main, bck) { for (var prop in bck) { if (!main.hasOwnProperty(prop) && bck.hasOwnProperty(prop)) { main[prop] = bck[prop]; } } return main; }; const Settings = new Lang.Class({ Name: 'ZorinTaskbar.Settings', _init: function() { this._settings = Convenience.getSettings('org.gnome.shell.extensions.zorin-taskbar'); this._gnomeInterfaceSettings = Convenience.getSettings('org.gnome.desktop.interface'); this._rtl = (Gtk.Widget.get_default_direction() == Gtk.TextDirection.RTL); this._builder = new Gtk.Builder(); this._builder.set_translation_domain(Me.metadata['gettext-domain']); this._builder.add_from_file(Me.path + '/Settings.ui'); this.notebook = this._builder.get_object('settings_notebook'); this.viewport = new Gtk.Viewport(); this.viewport.add(this.notebook); this.widget = new Gtk.ScrolledWindow(); this.widget.add(this.viewport); // Timeout to delay the update of the settings this._panel_size_timeout = 0; this._dot_height_timeout = 0; this._opacity_timeout = 0; this._bindSettings(); this._builder.connect_signals_full(Lang.bind(this, this._connector)); }, /** * Connect signals */ _connector: function(builder, object, signal, handler) { object.connect(signal, Lang.bind(this, this._SignalHandler[handler])); }, _updateVerticalRelatedOptions: function() { let position = this._getPanelPosition(this._currentMonitorIndex); let isVertical = position == Pos.LEFT || position == Pos.RIGHT; let showDesktopWidthLabel = this._builder.get_object('show_showdesktop_width_label'); showDesktopWidthLabel.set_text(_('Show Desktop button padding (px)')); this._displayPanelPositionsForMonitor(this._currentMonitorIndex); }, _getPanelPositions: function() { return Pos.getSettingsPositions(this._settings, 'panel-positions'); }, _getPanelPosition: function(monitorIndex) { let panelPositionsSettings = this._getPanelPositions(); return panelPositionsSettings[monitorIndex] || this._settings.get_string('panel-position'); }, _setPanelPosition: function(position) { let panelPositionsSettings = this._getPanelPositions(); let monitorSync = this._settings.get_boolean('panel-element-positions-monitors-sync'); let monitors = monitorSync ? this.monitors : [this._currentMonitorIndex]; monitors.forEach(m => panelPositionsSettings[m] = position); this._settings.set_string('panel-positions', JSON.stringify(panelPositionsSettings)); }, _setPositionRadios: function(position) { this._ignorePositionRadios = true; switch (position) { case Pos.BOTTOM: this._builder.get_object('position_bottom_button').set_active(true); break; case Pos.TOP: this._builder.get_object('position_top_button').set_active(true); break; case Pos.LEFT: this._builder.get_object('position_left_button').set_active(true); break; case Pos.RIGHT: this._builder.get_object('position_right_button').set_active(true); break; } this._ignorePositionRadios = false; }, _displayPanelPositionsForMonitor: function(monitorIndex) { let taskbarListBox = this._builder.get_object('taskbar_display_listbox'); taskbarListBox.get_children().forEach(c => c.destroy()); let labels = {}; let panelPosition = this._getPanelPosition(monitorIndex); let isVertical = panelPosition == Pos.LEFT || panelPosition == Pos.RIGHT; let panelElementPositionsSettings = Pos.getSettingsPositions(this._settings, 'panel-element-positions'); let panelElementPositions = panelElementPositionsSettings[monitorIndex] || Pos.defaults; let updateElementsSettings = () => { let newPanelElementPositions = []; let monitorSync = this._settings.get_boolean('panel-element-positions-monitors-sync'); let monitors = monitorSync ? this.monitors : [monitorIndex]; taskbarListBox.get_children().forEach(c => { newPanelElementPositions.push({ element: c.id, visible: c.visibleToggleBtn.get_active(), position: c.positionCombo.get_active_id() }); }); monitors.forEach(m => panelElementPositionsSettings[m] = newPanelElementPositions); this._settings.set_string('panel-element-positions', JSON.stringify(panelElementPositionsSettings)); }; this._setPositionRadios(panelPosition); labels[Pos.SHOW_APPS_BTN] = _('Show Applications button'); labels[Pos.ACTIVITIES_BTN] = _('Activities button'); labels[Pos.TASKBAR] = _('Taskbar'); labels[Pos.DATE_MENU] = _('Date menu'); labels[Pos.SYSTEM_MENU] = _('System menu'); labels[Pos.LEFT_BOX] = _('Left box'); labels[Pos.CENTER_BOX] = _('Center box'); labels[Pos.RIGHT_BOX] = _('Right box'); labels[Pos.DESKTOP_BTN] = _('Desktop button'); panelElementPositions.forEach(el => { let row = new Gtk.ListBoxRow(); let grid = new Gtk.Grid({ margin: 2, margin_left: 12, margin_right: 12, column_spacing: 8 }); let upDownGrid = new Gtk.Grid({ column_spacing: 2 }); let upBtn = new Gtk.Button({ tooltip_text: _('Move up') }); let upImg = new Gtk.Image({ icon_name: 'go-up-symbolic', pixel_size: 12 }); let downBtn = new Gtk.Button({ tooltip_text: _('Move down') }); let downImg = new Gtk.Image({ icon_name: 'go-down-symbolic', pixel_size: 12 }); let visibleToggleBtn = new Gtk.ToggleButton({ label: _('Visible'), active: el.visible }); let positionCombo = new Gtk.ComboBoxText({ tooltip_text: _('Select element position') }); let upDownClickHandler = limit => { let index = row.get_index(); if (index != limit) { taskbarListBox.remove(row); taskbarListBox.insert(row, index + (!limit ? -1 : 1)); updateElementsSettings(); } }; positionCombo.append(Pos.STACKED_TL, isVertical ? _('Stacked to top') : _('Stacked to left')); positionCombo.append(Pos.STACKED_BR, isVertical ? _('Stacked to bottom') :_('Stacked to right')); positionCombo.append(Pos.CENTERED, _('Centered')); positionCombo.append(Pos.CENTERED_MONITOR, _('Monitor Center')); positionCombo.set_active_id(el.position); upBtn.connect('clicked', () => upDownClickHandler(0)); downBtn.connect('clicked', () => upDownClickHandler(panelElementPositions.length - 1)); visibleToggleBtn.connect('toggled', () => updateElementsSettings()); positionCombo.connect('changed', () => updateElementsSettings()); upBtn.add(upImg); downBtn.add(downImg); upDownGrid.add(upBtn); upDownGrid.add(downBtn); grid.add(upDownGrid); grid.add(new Gtk.Label({ label: labels[el.element], xalign: 0, hexpand: true })); if (Pos.optionDialogFunctions[el.element]) { let cogImg = new Gtk.Image({ icon_name: 'emblem-system-symbolic' }); let optionsBtn = new Gtk.Button({ tooltip_text: _('More options') }); optionsBtn.get_style_context().add_class('circular'); optionsBtn.add(cogImg); grid.add(optionsBtn); optionsBtn.connect('clicked', () => this[Pos.optionDialogFunctions[el.element]]()); } grid.add(visibleToggleBtn); grid.add(positionCombo); row.id = el.element; row.visibleToggleBtn = visibleToggleBtn; row.positionCombo = positionCombo; row.add(grid); taskbarListBox.add(row); }); taskbarListBox.show_all(); }, _showShowAppsButtonOptions: function() { let dialog = new Gtk.Dialog({ title: _('Show Applications options'), transient_for: this.widget.get_toplevel(), use_header_bar: true, modal: true }); // GTK+ leaves positive values for application-defined response ids. // Use +1 for the reset action dialog.add_button(_('Reset to defaults'), 1); let box = this._builder.get_object('show_applications_options'); dialog.get_content_area().add(box); dialog.connect('response', Lang.bind(this, function(dialog, id) { if (id == 1) { // restore default settings this._settings.set_value('show-apps-override-escape', this._settings.get_default_value('show-apps-override-escape')); this._settings.set_value('animate-show-apps', this._settings.get_default_value('animate-show-apps')); } else { // remove the settings box so it doesn't get destroyed; dialog.get_content_area().remove(box); dialog.destroy(); } return; })); dialog.show_all(); }, _showDateMenuOptions: function() { let dialog = new Gtk.Dialog({ title: _('Date Menu options'), transient_for: this.widget.get_toplevel(), use_header_bar: true, modal: true }); // GTK+ leaves positive values for application-defined response ids. // Use +1 for the reset action dialog.add_button(_('Reset to defaults'), 1); let box = this._builder.get_object('date_menu_options'); dialog.get_content_area().add(box); dialog.connect('response', Lang.bind(this, function(dialog, id) { if (id == 1) { // restore default settings this._gnomeInterfaceSettings.set_value('clock-show-date', this._gnomeInterfaceSettings.get_default_value('clock-show-date')); this._gnomeInterfaceSettings.set_value('clock-show-seconds', this._gnomeInterfaceSettings.get_default_value('clock-show-seconds')); this._gnomeInterfaceSettings.set_value('clock-show-weekday', this._gnomeInterfaceSettings.get_default_value('clock-show-weekday')); } else { // remove the settings box so it doesn't get destroyed; dialog.get_content_area().remove(box); dialog.destroy(); } return; })); dialog.show_all(); }, _showDesktopButtonOptions: function() { let dialog = new Gtk.Dialog({ title: _('Show Desktop options'), transient_for: this.widget.get_toplevel(), use_header_bar: true, modal: true }); // GTK+ leaves positive values for application-defined response ids. // Use +1 for the reset action dialog.add_button(_('Reset to defaults'), 1); let box = this._builder.get_object('box_show_showdesktop_options'); dialog.get_content_area().add(box); this._builder.get_object('show_showdesktop_width_spinbutton').set_value(this._settings.get_int('showdesktop-button-width')); this._builder.get_object('show_showdesktop_width_spinbutton').connect('value-changed', Lang.bind (this, function(widget) { this._settings.set_int('showdesktop-button-width', widget.get_value()); })); dialog.connect('response', Lang.bind(this, function(dialog, id) { if (id == 1) { // restore default settings this._settings.set_value('show-showdesktop-icon', this._settings.get_default_value('show-showdesktop-icon')); this._settings.set_value('showdesktop-button-width', this._settings.get_default_value('showdesktop-button-width')); this._builder.get_object('show_showdesktop_width_spinbutton').set_value(this._settings.get_int('showdesktop-button-width')); this._settings.set_value('show-showdesktop-hover', this._settings.get_default_value('show-showdesktop-hover')); } else { // remove the settings box so it doesn't get destroyed; dialog.get_content_area().remove(box); dialog.destroy(); } return; })); dialog.show_all(); }, _bindSettings: function() { // size options let panel_size_scale = this._builder.get_object('panel_size_scale'); panel_size_scale.set_range(DEFAULT_PANEL_SIZES[DEFAULT_PANEL_SIZES.length-1], DEFAULT_PANEL_SIZES[0]); panel_size_scale.set_value(this._settings.get_int('panel-size')); DEFAULT_PANEL_SIZES.slice(1, -1).forEach(function(val) { panel_size_scale.add_mark(val, Gtk.PositionType.TOP, val.toString()); }); // Corrent for rtl languages if (this._rtl) { // Flip value position: this is not done automatically panel_size_scale.set_value_pos(Gtk.PositionType.LEFT); // I suppose due to a bug, having a more than one mark and one above a value of 100 // makes the rendering of the marks wrong in rtl. This doesn't happen setting the scale as not flippable // and then manually inverting it panel_size_scale.set_flippable(false); panel_size_scale.set_inverted(true); } //multi-monitor this.monitors = this._settings.get_value('available-monitors').deep_unpack(); let dtpPrimaryMonitorIndex = 0; this._currentMonitorIndex = this.monitors[dtpPrimaryMonitorIndex]; this._settings.connect('changed::panel-positions', () => this._updateVerticalRelatedOptions()); this._updateVerticalRelatedOptions(); for (let i = 0; i < this.monitors.length; ++i) { //the primary index is the first one in the "available-monitors" setting let label = !i ? _('Primary monitor') : _('Monitor ') + (i + 1); this._builder.get_object('taskbar_position_monitor_combo').append_text(label); } this._builder.get_object('taskbar_position_monitor_combo').set_active(dtpPrimaryMonitorIndex); this._settings.bind('panel-element-positions-monitors-sync', this._builder.get_object('taskbar_position_sync_button'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('panel-element-positions-monitors-sync', this._builder.get_object('taskbar_position_monitor_combo'), 'sensitive', Gio.SettingsBindFlags.INVERT_BOOLEAN); this._builder.get_object('taskbar_position_monitor_combo').connect('changed', Lang.bind (this, function(widget) { this._currentMonitorIndex = this.monitors[widget.get_active()]; this._displayPanelPositionsForMonitor(this._currentMonitorIndex); })); //panel positions this._displayPanelPositionsForMonitor(this._currentMonitorIndex); this._settings.bind('multi-monitors', this._builder.get_object('multimon_multi_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); if (this.monitors.length === 1) { this._builder.get_object('multimon_multi_switch').set_sensitive(false); } //dynamic opacity let rgba = new Gdk.RGBA(); this._settings.bind('trans-use-custom-opacity', this._builder.get_object('trans_opacity_override_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('trans-use-custom-opacity', this._builder.get_object('trans_opacity_box'), 'sensitive', Gio.SettingsBindFlags.DEFAULT); this._builder.get_object('trans_opacity_spinbutton').set_value(this._settings.get_double('trans-panel-opacity') * 100); this._builder.get_object('trans_opacity_spinbutton').connect('value-changed', Lang.bind(this, function (widget) { this._settings.set_double('trans-panel-opacity', widget.get_value() * 0.01); })); this._settings.bind('intellihide', this._builder.get_object('intellihide_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('intellihide', this._builder.get_object('intellihide_options_button'), 'sensitive', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('intellihide-floating-rounded-theme', this._builder.get_object('intellihide_floating_rounded_theme_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('intellihide-hide-from-windows', this._builder.get_object('intellihide_window_hide_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('intellihide-hide-from-windows', this._builder.get_object('intellihide_behaviour_options'), 'sensitive', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('intellihide-behaviour', this._builder.get_object('intellihide_behaviour_combo'), 'active-id', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('intellihide-use-pressure', this._builder.get_object('intellihide_use_pressure_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('intellihide-show-in-fullscreen', this._builder.get_object('intellihide_show_in_fullscreen_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('intellihide-only-secondary', this._builder.get_object('intellihide_only_secondary_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('multi-monitors', this._builder.get_object('grid_intellihide_only_secondary'), 'sensitive', Gio.SettingsBindFlags.DEFAULT); this._builder.get_object('multimon_multi_switch').connect('notify::active', (widget) => { if (!widget.get_active()) this._builder.get_object('intellihide_only_secondary_switch').set_active(false); }); this._settings.bind('intellihide-key-toggle-text', this._builder.get_object('intellihide_toggle_entry'), 'text', Gio.SettingsBindFlags.DEFAULT); this._settings.connect('changed::intellihide-key-toggle-text', () => setShortcut(this._settings, 'intellihide-key-toggle')); this._builder.get_object('intellihide_options_button').connect('clicked', Lang.bind(this, function() { let dialog = new Gtk.Dialog({ title: _('Intellihide options'), transient_for: this.widget.get_toplevel(), use_header_bar: true, modal: true }); // GTK+ leaves positive values for application-defined response ids. // Use +1 for the reset action dialog.add_button(_('Reset to defaults'), 1); let box = this._builder.get_object('box_intellihide_options'); dialog.get_content_area().add(box); dialog.connect('response', Lang.bind(this, function(dialog, id) { if (id == 1) { // restore default settings this._settings.set_value('intellihide-floating-rounded-theme', this._settings.get_default_value('intellihide-floating-rounded-theme')); this._settings.set_value('intellihide-hide-from-windows', this._settings.get_default_value('intellihide-hide-from-windows')); this._settings.set_value('intellihide-behaviour', this._settings.get_default_value('intellihide-behaviour')); this._settings.set_value('intellihide-use-pressure', this._settings.get_default_value('intellihide-use-pressure')); this._settings.set_value('intellihide-show-in-fullscreen', this._settings.get_default_value('intellihide-show-in-fullscreen')); this._settings.set_value('intellihide-only-secondary', this._settings.get_default_value('intellihide-only-secondary')); this._settings.set_value('intellihide-key-toggle-text', this._settings.get_default_value('intellihide-key-toggle-text')); } else { // remove the settings box so it doesn't get destroyed; dialog.get_content_area().remove(box); dialog.destroy(); } return; })); dialog.show_all(); })); // Behavior panel this._settings.bind('animate-show-apps', this._builder.get_object('application_button_animation_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('show-apps-override-escape', this._builder.get_object('show_applications_esc_key_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._gnomeInterfaceSettings.bind('clock-show-date', this._builder.get_object('date_menu_date_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._gnomeInterfaceSettings.bind('clock-show-seconds', this._builder.get_object('date_menu_seconds_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._gnomeInterfaceSettings.bind('clock-show-weekday', this._builder.get_object('date_menu_weekday_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('show-showdesktop-hover', this._builder.get_object('show_showdesktop_hide_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('show-showdesktop-icon', this._builder.get_object('show_showdesktop_icon_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('show-window-previews', this._builder.get_object('show_window_previews_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('show-window-previews', this._builder.get_object('show_window_previews_button'), 'sensitive', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('show-tooltip', this._builder.get_object('show_tooltip_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('show-favorites', this._builder.get_object('show_favorite_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('show-favorites-all-monitors', this._builder.get_object('multimon_multi_show_favorites_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('show-favorites', this._builder.get_object('multimon_multi_show_favorites_switch'), 'sensitive', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('show-running-apps', this._builder.get_object('show_runnning_apps_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._builder.get_object('show_window_previews_button').connect('clicked', Lang.bind(this, function() { let dialog = new Gtk.Dialog({ title: _('Window preview options'), transient_for: this.widget.get_toplevel(), use_header_bar: true, modal: true }); // GTK+ leaves positive values for application-defined response ids. // Use +1 for the reset action dialog.add_button(_('Reset to defaults'), 1); let scrolledWindow = this._builder.get_object('box_window_preview_options'); adjustScrollableHeight(this._builder.get_object('viewport_window_preview_options'), scrolledWindow); dialog.get_content_area().add(scrolledWindow); this._settings.bind('preview-middle-click-close', this._builder.get_object('preview_middle_click_close_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('peek-mode', this._builder.get_object('peek_mode_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._builder.get_object('preview_size_spinbutton').set_value(this._settings.get_int('window-preview-size')); this._builder.get_object('preview_size_spinbutton').connect('value-changed', Lang.bind (this, function(widget) { this._settings.set_int('window-preview-size', widget.get_value()); })); dialog.connect('response', Lang.bind(this, function(dialog, id) { if (id == 1) { // restore default settings this._settings.set_value('peek-mode', this._settings.get_default_value('peek-mode')); this._settings.set_value('window-preview-size', this._settings.get_default_value('window-preview-size')); this._builder.get_object('preview_size_spinbutton').set_value(this._settings.get_int('window-preview-size')); this._settings.set_value('preview-middle-click-close', this._settings.get_default_value('preview-middle-click-close')); } else { // remove the settings box so it doesn't get destroyed; dialog.get_content_area().remove(scrolledWindow); dialog.destroy(); } return; })); dialog.show_all(); })); this._settings.bind('isolate-workspaces', this._builder.get_object('isolate_workspaces_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('isolate-monitors', this._builder.get_object('multimon_multi_isolate_monitor_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('group-apps', this._builder.get_object('group_apps_switch'), 'active', Gio.SettingsBindFlags.DEFAULT | Gio.SettingsBindFlags.INVERT_BOOLEAN); this._settings.bind('group-apps', this._builder.get_object('show_group_apps_options_button'), 'sensitive', Gio.SettingsBindFlags.DEFAULT | Gio.SettingsBindFlags.INVERT_BOOLEAN); this._settings.bind('group-apps-use-fixed-width', this._builder.get_object('group_apps_use_fixed_width_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('group-apps-use-launchers', this._builder.get_object('group_apps_use_launchers_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._builder.get_object('show_group_apps_options_button').connect('clicked', Lang.bind(this, function() { let dialog = new Gtk.Dialog({ title: _('Ungrouped application options'), transient_for: this.widget.get_toplevel(), use_header_bar: true, modal: true }); // GTK+ leaves positive values for application-defined response ids. // Use +1 for the reset action dialog.add_button(_('Reset to defaults'), 1); let box = this._builder.get_object('box_group_apps_options'); dialog.get_content_area().add(box); this._builder.get_object('group_apps_label_max_width_spinbutton').set_value(this._settings.get_int('group-apps-label-max-width')); this._builder.get_object('group_apps_label_max_width_spinbutton').connect('value-changed', Lang.bind (this, function(widget) { this._settings.set_int('group-apps-label-max-width', widget.get_value()); })); dialog.connect('response', Lang.bind(this, function(dialog, id) { if (id == 1) { // restore default settings this._settings.set_value('group-apps-label-max-width', this._settings.get_default_value('group-apps-label-max-width')); this._builder.get_object('group_apps_label_max_width_spinbutton').set_value(this._settings.get_int('group-apps-label-max-width')); this._settings.set_value('group-apps-use-fixed-width', this._settings.get_default_value('group-apps-use-fixed-width')); this._settings.set_value('group-apps-use-launchers', this._settings.get_default_value('group-apps-use-launchers')); } else { // remove the settings box so it doesn't get destroyed; dialog.get_content_area().remove(box); dialog.destroy(); } return; })); dialog.show_all(); })); this._builder.get_object('click_action_combo').set_active_id(this._settings.get_string('click-action')); this._builder.get_object('click_action_combo').connect('changed', Lang.bind (this, function(widget) { this._settings.set_string('click-action', widget.get_active_id()); })); this._builder.get_object('shift_click_action_combo').connect('changed', Lang.bind (this, function(widget) { this._settings.set_string('shift-click-action', widget.get_active_id()); })); this._builder.get_object('middle_click_action_combo').connect('changed', Lang.bind (this, function(widget) { this._settings.set_string('middle-click-action', widget.get_active_id()); })); this._builder.get_object('shift_middle_click_action_combo').connect('changed', Lang.bind (this, function(widget) { this._settings.set_string('shift-middle-click-action', widget.get_active_id()); })); // Create dialog for middle-click options this._builder.get_object('middle_click_options_button').connect('clicked', Lang.bind(this, function() { let dialog = new Gtk.Dialog({ title: _('Customize middle-click behavior'), transient_for: this.widget.get_toplevel(), use_header_bar: true, modal: true }); // GTK+ leaves positive values for application-defined response ids. // Use +1 for the reset action dialog.add_button(_('Reset to defaults'), 1); let box = this._builder.get_object('box_middle_click_options'); dialog.get_content_area().add(box); this._builder.get_object('shift_click_action_combo').set_active_id(this._settings.get_string('shift-click-action')); this._builder.get_object('middle_click_action_combo').set_active_id(this._settings.get_string('middle-click-action')); this._builder.get_object('shift_middle_click_action_combo').set_active_id(this._settings.get_string('shift-middle-click-action')); this._settings.bind('shift-click-action', this._builder.get_object('shift_click_action_combo'), 'active-id', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('middle-click-action', this._builder.get_object('middle_click_action_combo'), 'active-id', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('shift-middle-click-action', this._builder.get_object('shift_middle_click_action_combo'), 'active-id', Gio.SettingsBindFlags.DEFAULT); dialog.connect('response', Lang.bind(this, function(dialog, id) { if (id == 1) { // restore default settings for the relevant keys let keys = ['shift-click-action', 'middle-click-action', 'shift-middle-click-action']; keys.forEach(function(val) { this._settings.set_value(val, this._settings.get_default_value(val)); }, this); this._builder.get_object('shift_click_action_combo').set_active_id(this._settings.get_string('shift-click-action')); this._builder.get_object('middle_click_action_combo').set_active_id(this._settings.get_string('middle-click-action')); this._builder.get_object('shift_middle_click_action_combo').set_active_id(this._settings.get_string('shift-middle-click-action')); } else { // remove the settings box so it doesn't get destroyed; dialog.get_content_area().remove(box); dialog.destroy(); } return; })); dialog.show_all(); })); this._builder.get_object('scroll_icon_combo').set_active_id(this._settings.get_string('scroll-icon-action')); this._builder.get_object('scroll_icon_combo').connect('changed', Lang.bind (this, function(widget) { this._settings.set_string('scroll-icon-action', widget.get_active_id()); })); this._settings.bind('hot-keys', this._builder.get_object('hot_keys_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('hot-keys', this._builder.get_object('overlay_button'), 'sensitive', Gio.SettingsBindFlags.DEFAULT); this._builder.get_object('overlay_combo').connect('changed', Lang.bind (this, function(widget) { this._settings.set_string('hotkeys-overlay-combo', widget.get_active_id()); })); this._settings.bind('shortcut-previews', this._builder.get_object('shortcut_preview_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); this._builder.get_object('shortcut_num_keys_combo').set_active_id(this._settings.get_string('shortcut-num-keys')); this._builder.get_object('shortcut_num_keys_combo').connect('changed', Lang.bind (this, function(widget) { this._settings.set_string('shortcut-num-keys', widget.get_active_id()); })); this._settings.connect('changed::hotkey-prefix-text', Lang.bind(this, function() {checkHotkeyPrefix(this._settings);})); this._builder.get_object('hotkey_prefix_combo').set_active_id(this._settings.get_string('hotkey-prefix-text')); this._settings.bind('hotkey-prefix-text', this._builder.get_object('hotkey_prefix_combo'), 'active-id', Gio.SettingsBindFlags.DEFAULT); this._builder.get_object('overlay_combo').set_active_id(this._settings.get_string('hotkeys-overlay-combo')); this._settings.bind('hotkeys-overlay-combo', this._builder.get_object('overlay_combo'), 'active-id', Gio.SettingsBindFlags.DEFAULT); this._settings.bind('shortcut-text', this._builder.get_object('shortcut_entry'), 'text', Gio.SettingsBindFlags.DEFAULT); this._settings.connect('changed::shortcut-text', Lang.bind(this, function() {setShortcut(this._settings, 'shortcut');})); // Create dialog for number overlay options this._builder.get_object('overlay_button').connect('clicked', Lang.bind(this, function() { let dialog = new Gtk.Dialog({ title: _('Advanced hotkeys options'), transient_for: this.widget.get_toplevel(), use_header_bar: true, modal: true }); // GTK+ leaves positive values for application-defined response ids. // Use +1 for the reset action dialog.add_button(_('Reset to defaults'), 1); let box = this._builder.get_object('box_overlay_shortcut'); dialog.get_content_area().add(box); dialog.connect('response', Lang.bind(this, function(dialog, id) { if (id == 1) { // restore default settings for the relevant keys let keys = ['hotkey-prefix-text', 'shortcut-text', 'hotkeys-overlay-combo', 'shortcut-previews']; keys.forEach(function(val) { this._settings.set_value(val, this._settings.get_default_value(val)); }, this); } else { // remove the settings box so it doesn't get destroyed; dialog.get_content_area().remove(box); dialog.destroy(); } return; })); dialog.show_all(); })); }, /** * Object containing all signals defined in the glade file */ _SignalHandler: { position_bottom_button_clicked_cb: function(button) { if (!this._ignorePositionRadios && button.get_active()) this._setPanelPosition(Pos.BOTTOM); }, position_top_button_clicked_cb: function(button) { if (!this._ignorePositionRadios && button.get_active()) this._setPanelPosition(Pos.TOP); }, position_left_button_clicked_cb: function(button) { if (!this._ignorePositionRadios && button.get_active()) this._setPanelPosition(Pos.LEFT); }, position_right_button_clicked_cb: function(button) { if (!this._ignorePositionRadios && button.get_active()) this._setPanelPosition(Pos.RIGHT); }, panel_size_scale_format_value_cb: function(scale, value) { return value+ ' px'; }, panel_size_scale_value_changed_cb: function(scale) { // Avoid settings the size consinuosly if (this._panel_size_timeout > 0) Mainloop.source_remove(this._panel_size_timeout); this._panel_size_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, Lang.bind(this, function() { this._settings.set_int('panel-size', scale.get_value()); this._panel_size_timeout = 0; return GLib.SOURCE_REMOVE; })); } } }); function init() { Convenience.initTranslations(); } function buildPrefsWidget() { let settings = new Settings(); let widget = settings.widget; // I'd like the scrolled window to default to a size large enough to show all without scrolling, if it fits on the screen // But, it doesn't seem possible, so I'm setting a minimum size if there seems to be enough screen real estate widget.show_all(); adjustScrollableHeight(settings.viewport, widget); return widget; } function adjustScrollableHeight(viewport, scrollableWindow) { let viewportSize = viewport.size_request(); let screenHeight = scrollableWindow.get_screen().get_height() - 120; scrollableWindow.set_size_request(viewportSize.width, viewportSize.height > screenHeight ? screenHeight : viewportSize.height); }