Compare commits
	
		
			90 Commits
		
	
	
		
			gbsneto/ea
			...
			wip/mcrha/
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | c00d79bae2 | ||
|   | 30d902f898 | ||
|   | 8f9da6f801 | ||
|   | 20648e9207 | ||
|   | 614fe202e0 | ||
|   | c3646a7642 | ||
|   | 772df91762 | ||
|   | c90910731f | ||
|   | ba69cd99d1 | ||
|   | fb6e341efd | ||
|   | 3dc4f01113 | ||
|   | d94d0f60c8 | ||
|   | 8d79f6f4c8 | ||
|   | c5e5bb0be1 | ||
|   | 8d139bbd95 | ||
|   | fb1bb291eb | ||
|   | 3199620a83 | ||
|   | c88bb66369 | ||
|   | a60d57ea1f | ||
|   | 527ce66cd4 | ||
|   | 40415a6849 | ||
|   | 1ff638a51f | ||
|   | 2a9ccf2e2c | ||
|   | 2909d91c13 | ||
|   | 0dba12193d | ||
|   | 02f40b3b63 | ||
|   | 1fd51efc7f | ||
|   | 6f881f232e | ||
|   | be12c71534 | ||
|   | 6aa1b817c9 | ||
|   | 300961e19e | ||
|   | b191e9ef91 | ||
|   | 5ec5978d4a | ||
|   | f4d90bc127 | ||
|   | bfa34914db | ||
|   | 8d1e4659d1 | ||
|   | b3b91f1699 | ||
|   | ff4c5270d3 | ||
|   | e240f7ea59 | ||
|   | 9f1ad5d86d | ||
|   | a1ab32af0f | ||
|   | 3fac0632a8 | ||
|   | c2b518929d | ||
|   | c422d82752 | ||
|   | da0c7fc2b6 | ||
|   | 167bc080d9 | ||
|   | 7d7a15f978 | ||
|   | 1b5cf0b8a8 | ||
|   | 306b005943 | ||
|   | d229abf07d | ||
|   | 6f6251c0bc | ||
|   | fd034e3551 | ||
|   | f9bee05d49 | ||
|   | 37c6fbc6b2 | ||
|   | 9719604b79 | ||
|   | b6262f0666 | ||
|   | 6c0bd207e9 | ||
|   | 91b13effc8 | ||
|   | 143ab6ac7f | ||
|   | 3e5b90dbba | ||
|   | b82039e324 | ||
|   | 83862d04a0 | ||
|   | 52d07f6d9b | ||
|   | 21de88c3ba | ||
|   | 456ca3d3e0 | ||
|   | f4ba3e4ab8 | ||
|   | d4db5a59c1 | ||
|   | 4aee87a31b | ||
|   | e8f5a842b1 | ||
|   | 5c681a76b6 | ||
|   | 53a24e6ddd | ||
|   | b27c89f836 | ||
|   | 9f870aa1c7 | ||
|   | c9cfeb8318 | ||
|   | 497e66ce6a | ||
|   | bc973b80d7 | ||
|   | 85846d88f0 | ||
|   | 6b7c85b079 | ||
|   | d80ef67d1d | ||
|   | 62f3457a95 | ||
|   | 636ab4b0e9 | ||
|   | 23e382dd33 | ||
|   | f5a170ce46 | ||
|   | fbd6beea2c | ||
|   | 360f5b1642 | ||
|   | f0a785df9d | ||
|   | e2c1bfbedd | ||
|   | 7e5a1cfd90 | ||
|   | 07deda593a | ||
|   | 1ca39e8586 | 
| @@ -18,7 +18,7 @@ variables: | ||||
|         - merge_requests | ||||
|  | ||||
| check_commit_log: | ||||
|     image: registry.gitlab.gnome.org/gnome/mutter/master:v3 | ||||
|     image: registry.gitlab.gnome.org/gnome/mutter/master:v4 | ||||
|     stage: review | ||||
|     variables: | ||||
|         GIT_DEPTH: "100" | ||||
| @@ -65,7 +65,7 @@ no_template_check: | ||||
|     <<: *only_default | ||||
|  | ||||
| build: | ||||
|     image: registry.gitlab.gnome.org/gnome/mutter/master:v3 | ||||
|     image: registry.gitlab.gnome.org/gnome/mutter/master:v4 | ||||
|     stage: build | ||||
|     before_script: | ||||
|         - .gitlab-ci/checkout-mutter.sh | ||||
| @@ -83,7 +83,7 @@ build: | ||||
|             - build | ||||
|  | ||||
| test: | ||||
|     image: registry.gitlab.gnome.org/gnome/mutter/master:v3 | ||||
|     image: registry.gitlab.gnome.org/gnome/mutter/master:v4 | ||||
|     stage: test | ||||
|     variables: | ||||
|         XDG_RUNTIME_DIR: "$CI_PROJECT_DIR/runtime-dir" | ||||
| @@ -100,7 +100,7 @@ test: | ||||
|         when: on_failure | ||||
|  | ||||
| test-pot: | ||||
|     image: registry.gitlab.gnome.org/gnome/mutter/master:v3 | ||||
|     image: registry.gitlab.gnome.org/gnome/mutter/master:v4 | ||||
|     stage: test | ||||
|     before_script: | ||||
|         - ninja -C mutter/build install | ||||
| @@ -124,11 +124,7 @@ flatpak: | ||||
|         RUNTIME_REPO: "https://nightly.gnome.org/gnome-nightly.flatpakrepo" | ||||
|         FLATPAK_MODULE: "gnome-extensions-app" | ||||
|         APP_ID: "org.gnome.Extensions" | ||||
|         MESON_ARGS: "$SUBPROJECT" | ||||
|     extends: .flatpak | ||||
|     before_script: | ||||
|         - flatpak run --command=$SUBPROJECT/generate-translations.sh | ||||
|                       --filesystem=host org.gnome.Sdk//master | ||||
|     <<: *only_default | ||||
|  | ||||
| nightly: | ||||
|   | ||||
| @@ -6,6 +6,11 @@ globs=('*.js' '*.c') | ||||
| # find source files that contain gettext keywords | ||||
| files=$(grep -lR ${globs[@]/#/--include=} '\(gettext\|[^I_)]_\)(' $srcdirs) | ||||
|  | ||||
| # filter out excluded files | ||||
| if [ -f po/POTFILES.skip ]; then | ||||
|   files=$(for f in $files; do ! grep -q ^$f po/POTFILES.skip && echo $f; done) | ||||
| fi | ||||
|  | ||||
| # find those that aren't listed in POTFILES.in | ||||
| missing=$(for f in $files; do ! grep -q ^$f po/POTFILES.in && echo $f; done) | ||||
|  | ||||
|   | ||||
| @@ -1,12 +1,19 @@ | ||||
| <node> | ||||
|   <interface name="org.gnome.Shell.CalendarServer"> | ||||
|     <method name="GetEvents"> | ||||
|       <arg type="x" direction="in" /> | ||||
|       <arg type="x" direction="in" /> | ||||
|       <arg type="b" direction="in" /> | ||||
|       <arg type="a(sssbxxa{sv})" direction="out" /> | ||||
|     <method name="SetTimeRange"> | ||||
|       <arg type="x" name="since" direction="in"/> | ||||
|       <arg type="x" name="until" direction="in"/> | ||||
|       <arg type="b" name="force_reload" direction="in"/> | ||||
|     </method> | ||||
|     <signal name="EventsAddedOrUpdated"> | ||||
|       <arg type="a(ssbxxa{sv})" name="events" direction="out"/> | ||||
|     </signal> | ||||
|     <signal name="EventsRemoved"> | ||||
|       <arg type="as" name="ids" direction="out"/> | ||||
|     </signal> | ||||
|     <signal name="ClientDisappeared"> | ||||
|       <arg type="s" name="source_uid" direction="out"/> | ||||
|     </signal> | ||||
|     <property name="HasCalendars" type="b" access="read" /> | ||||
|     <signal name="Changed" /> | ||||
|   </interface> | ||||
| </node> | ||||
|   | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 6.1 KiB | 
| @@ -0,0 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M6.5 1.031c-.371 0-.742-.035-1.11.016-.367.05-.73.203-.972.476-.125.141-.215.309-.266.485-.047.18-.054.367-.02.55.032.184.102.356.192.516.09.164.203.309.317.457L5 4H2a1.8 1.8 0 00-.41.035.791.791 0 00-.36.195.791.791 0 00-.195.36C1 4.723 1 4.863 1 5v2.75l.77-.344c.265-.117.542-.23.832-.242.289-.016.586.074.812.254.227.18.383.441.465.723.082.277.101.57.121.859.02.316.04.637-.016.95-.058.312-.199.616-.43.831a1.264 1.264 0 01-.874.32c-.317-.007-.618-.128-.91-.257L1 10.5V14c0 .137.004.277.035.41a.791.791 0 00.195.36c.098.097.227.16.36.195.133.035.273.035.41.035h3l-.328-.68c-.14-.293-.274-.597-.29-.922-.015-.32.095-.652.31-.894.214-.242.523-.39.84-.453.316-.067.644-.059.968-.059.324 0 .652-.008.969.059.316.062.625.21.84.453.214.242.324.574.308.894-.015.325-.148.63-.289.922L8 15h3a1.8 1.8 0 00.41-.035.791.791 0 00.36-.195.791.791 0 00.195-.36C12 14.277 12 14.137 12 14v-3.563l.703.297c.29.125.59.239.902.246.313.004.63-.101.864-.308.238-.203.386-.496.46-.8C15 9.565 15 9.25 15 8.937c0-.313 0-.63-.07-.934-.075-.305-.223-.598-.461-.8a1.288 1.288 0 00-.864-.31c-.312.008-.613.122-.902.247L12 7.437V5a1.8 1.8 0 00-.035-.41.791.791 0 00-.195-.36.791.791 0 00-.36-.195C11.277 4 11.137 4 11 4H8l.36-.469c.113-.148.226-.293.316-.457.09-.16.16-.332.191-.515a1.248 1.248 0 00-.02-.551 1.256 1.256 0 00-.265-.485c-.242-.273-.605-.425-.973-.476-.367-.05-.738-.016-1.109-.016zm0 0" fill="#474747"/></svg> | ||||
| After Width: | Height: | Size: 1.4 KiB | 
							
								
								
									
										1
									
								
								data/icons/meson.build
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								data/icons/meson.build
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| install_subdir('hicolor', install_dir: icondir) | ||||
| @@ -1,5 +1,6 @@ | ||||
| desktop_files = [ | ||||
|   'org.gnome.Shell.desktop', | ||||
|   'org.gnome.Shell.Extensions.desktop', | ||||
| ] | ||||
| service_files = [] | ||||
|  | ||||
| @@ -42,6 +43,7 @@ endforeach | ||||
|  | ||||
|  | ||||
| subdir('dbus-interfaces') | ||||
| subdir('icons') | ||||
| subdir('theme') | ||||
|  | ||||
| data_resources = [ | ||||
|   | ||||
							
								
								
									
										10
									
								
								data/org.gnome.Shell.Extensions.desktop.in.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								data/org.gnome.Shell.Extensions.desktop.in.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| [Desktop Entry] | ||||
| Type=Application | ||||
| # Keep in sync with subprojects/extensions-app | ||||
| Name=Extensions | ||||
| # Translators: Do NOT translate or transliterate this text (this is an icon file name)! | ||||
| Icon=org.gnome.Shell.Extensions | ||||
| # Never launch this, just provide name+icon to portal dialog | ||||
| Exec=false | ||||
| OnlyShowIn=GNOME; | ||||
| NoDisplay=true | ||||
| @@ -153,9 +153,11 @@ | ||||
|   } | ||||
|  | ||||
|   .calendar-day-with-events { | ||||
|     color: lighten($fg_color,10%); | ||||
|     font-weight: bold; | ||||
|     background-image: url("resource:///org/gnome/shell/theme/calendar-today.svg"); | ||||
|     &.calendar-work-day { | ||||
|        color: lighten($fg_color,10%); | ||||
|        font-weight: bold; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .calendar-other-month-day { | ||||
|   | ||||
| @@ -184,7 +184,7 @@ var AuthPrompt = GObject.registerClass({ | ||||
|         }); | ||||
|         this._defaultButtonWell.add_constraint(new Clutter.BindConstraint({ | ||||
|             source: this.cancelButton, | ||||
|             coordinate: Clutter.BindCoordinate.SIZE, | ||||
|             coordinate: Clutter.BindCoordinate.WIDTH, | ||||
|         })); | ||||
|         this._mainBox.add_child(this._defaultButtonWell); | ||||
|  | ||||
| @@ -424,7 +424,13 @@ var AuthPrompt = GObject.registerClass({ | ||||
|     } | ||||
|  | ||||
|     updateSensitivity(sensitive) { | ||||
|         if (this._entry.reactive === sensitive) | ||||
|             return; | ||||
|  | ||||
|         this._entry.reactive = sensitive; | ||||
|  | ||||
|         if (sensitive) | ||||
|             this._entry.grab_key_focus(); | ||||
|     } | ||||
|  | ||||
|     vfunc_hide() { | ||||
|   | ||||
| @@ -810,12 +810,13 @@ var LoginDialog = GObject.registerClass({ | ||||
|             return; | ||||
|  | ||||
|         this._logoBin.destroy_all_children(); | ||||
|         if (this._logoFile && this._logoBin.resource_scale > 0) { | ||||
|         const [valid, resourceScale] = this._logoBin.get_resource_scale(); | ||||
|         if (this._logoFile && valid) { | ||||
|             let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|             this._logoBin.add_child(this._textureCache.load_file_async(this._logoFile, | ||||
|                                                                        -1, -1, | ||||
|                                                                        scaleFactor, | ||||
|                                                                        this._logoBin.resource_scale)); | ||||
|                                                                        resourceScale)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -23,6 +23,7 @@ | ||||
|     <file>misc/modemManager.js</file> | ||||
|     <file>misc/objectManager.js</file> | ||||
|     <file>misc/params.js</file> | ||||
|     <file>misc/parentalControlsManager.js</file> | ||||
|     <file>misc/permissionStore.js</file> | ||||
|     <file>misc/smartcardManager.js</file> | ||||
|     <file>misc/systemActions.js</file> | ||||
| @@ -101,7 +102,6 @@ | ||||
|     <file>ui/swipeTracker.js</file> | ||||
|     <file>ui/switcherPopup.js</file> | ||||
|     <file>ui/switchMonitor.js</file> | ||||
|     <file>ui/tweener.js</file> | ||||
|     <file>ui/unlockDialog.js</file> | ||||
|     <file>ui/userWidget.js</file> | ||||
|     <file>ui/viewSelector.js</file> | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported getKeyboardManager, holdKeyboard, releaseKeyboard */ | ||||
|  | ||||
| const { GLib, GnomeDesktop, Meta } = imports.gi; | ||||
| const { GLib, GnomeDesktop } = imports.gi; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
|  | ||||
| @@ -62,11 +62,11 @@ var KeyboardManager = class { | ||||
|             return; | ||||
|  | ||||
|         this._currentKeymap = { layouts, variants, options }; | ||||
|         Meta.get_backend().set_keymap(layouts, variants, options); | ||||
|         global.backend.set_keymap(layouts, variants, options); | ||||
|     } | ||||
|  | ||||
|     _applyLayoutGroupIndex(idx) { | ||||
|         Meta.get_backend().lock_layout_group(idx); | ||||
|         global.backend.lock_layout_group(idx); | ||||
|     } | ||||
|  | ||||
|     apply(id) { | ||||
|   | ||||
							
								
								
									
										146
									
								
								js/misc/parentalControlsManager.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								js/misc/parentalControlsManager.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,146 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| // | ||||
| // Copyright (C) 2018, 2019, 2020 Endless Mobile, Inc. | ||||
| // | ||||
| // This is a GNOME Shell component to wrap the interactions over | ||||
| // D-Bus with the malcontent library. | ||||
| // | ||||
| // Licensed under the GNU General Public License Version 2 | ||||
| // | ||||
| // 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, write to the Free Software | ||||
| // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | ||||
|  | ||||
| /* exported getDefault */ | ||||
|  | ||||
| const { Gio, GObject, Shell } = imports.gi; | ||||
|  | ||||
| // We require libmalcontent ≥ 0.6.0 | ||||
| const HAVE_MALCONTENT = imports.package.checkSymbol( | ||||
|     'Malcontent', '0', 'ManagerGetValueFlags'); | ||||
|  | ||||
| var Malcontent = null; | ||||
| if (HAVE_MALCONTENT) { | ||||
|     Malcontent = imports.gi.Malcontent; | ||||
|     Gio._promisify(Malcontent.Manager.prototype, 'get_app_filter_async', 'get_app_filter_finish'); | ||||
| } | ||||
|  | ||||
| let _singleton = null; | ||||
|  | ||||
| function getDefault() { | ||||
|     if (_singleton === null) | ||||
|         _singleton = new ParentalControlsManager(); | ||||
|  | ||||
|     return _singleton; | ||||
| } | ||||
|  | ||||
| // A manager class which provides cached access to the constructing user’s | ||||
| // parental controls settings. It’s possible for the user’s parental controls | ||||
| // to change at runtime if the Parental Controls application is used by an | ||||
| // administrator from within the user’s session. | ||||
| var ParentalControlsManager = GObject.registerClass({ | ||||
|     Signals: { | ||||
|         'app-filter-changed': {}, | ||||
|     }, | ||||
| }, class ParentalControlsManager extends GObject.Object { | ||||
|     _init() { | ||||
|         super._init(); | ||||
|  | ||||
|         this._initialized = false; | ||||
|         this._disabled = false; | ||||
|         this._appFilter = null; | ||||
|  | ||||
|         this._initializeManager(); | ||||
|     } | ||||
|  | ||||
|     async _initializeManager() { | ||||
|         if (!HAVE_MALCONTENT) { | ||||
|             log('Skipping parental controls support as it’s disabled'); | ||||
|             this._initialized = true; | ||||
|             this.emit('app-filter-changed'); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         log(`Getting parental controls for user ${Shell.util_get_uid()}`); | ||||
|         try { | ||||
|             const connection = await Gio.DBus.get(Gio.BusType.SYSTEM, null); | ||||
|             this._manager = new Malcontent.Manager({ connection }); | ||||
|             this._appFilter = await this._manager.get_app_filter_async( | ||||
|                 Shell.util_get_uid(), | ||||
|                 Malcontent.ManagerGetValueFlags.NONE, | ||||
|                 null); | ||||
|         } catch (e) { | ||||
|             if (e.matches(Malcontent.ManagerError, Malcontent.ManagerError.DISABLED)) { | ||||
|                 log('Parental controls globally disabled'); | ||||
|                 this._disabled = true; | ||||
|             } else { | ||||
|                 logError(e, 'Failed to get parental controls settings'); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         this._manager.connect('app-filter-changed', this._onAppFilterChanged.bind(this)); | ||||
|  | ||||
|         // Signal initialisation is complete. | ||||
|         this._initialized = true; | ||||
|         this.emit('app-filter-changed'); | ||||
|     } | ||||
|  | ||||
|     async _onAppFilterChanged(manager, uid) { | ||||
|         // Emit 'changed' signal only if app-filter is changed for currently logged-in user. | ||||
|         let currentUid = Shell.util_get_uid(); | ||||
|         if (currentUid !== uid) | ||||
|             return; | ||||
|  | ||||
|         try { | ||||
|             this._appFilter = await this._manager.get_app_filter_async( | ||||
|                 currentUid, | ||||
|                 Malcontent.ManagerGetValueFlags.NONE, | ||||
|                 null); | ||||
|             this.emit('app-filter-changed'); | ||||
|         } catch (e) { | ||||
|             // Log an error and keep the old app filter. | ||||
|             logError(e, `Failed to get new MctAppFilter for uid ${Shell.util_get_uid()} on app-filter-changed`); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     get initialized() { | ||||
|         return this._initialized; | ||||
|     } | ||||
|  | ||||
|     // Calculate whether the given app (a Gio.DesktopAppInfo) should be shown | ||||
|     // on the desktop, in search results, etc. The app should be shown if: | ||||
|     //  - The .desktop file doesn’t say it should be hidden. | ||||
|     //  - The executable from the .desktop file’s Exec line isn’t blacklisted in | ||||
|     //    the user’s parental controls. | ||||
|     //  - None of the flatpak app IDs from the X-Flatpak and the | ||||
|     //    X-Flatpak-RenamedFrom lines are blacklisted in the user’s parental | ||||
|     //    controls. | ||||
|     shouldShowApp(appInfo) { | ||||
|         // Quick decision? | ||||
|         if (!appInfo.should_show()) | ||||
|             return false; | ||||
|  | ||||
|         // Are parental controls enabled (at configure time or runtime)? | ||||
|         if (!HAVE_MALCONTENT || this._disabled) | ||||
|             return true; | ||||
|  | ||||
|         // Have we finished initialising yet? | ||||
|         if (!this.initialized) { | ||||
|             log(`Warning: Hiding app because parental controls not yet initialised: ${appInfo.get_id()}`); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return this._appFilter.is_appinfo_allowed(appInfo); | ||||
|     } | ||||
| }); | ||||
| @@ -83,13 +83,17 @@ const SystemActions = GObject.registerClass({ | ||||
|         this._canHavePowerOff = true; | ||||
|         this._canHaveSuspend = true; | ||||
|  | ||||
|         function tokenizeKeywords(keywords) { | ||||
|             return keywords.split(';').map(keyword => GLib.str_tokenize_and_fold(keyword, null)).flat(2); | ||||
|         } | ||||
|  | ||||
|         this._actions = new Map(); | ||||
|         this._actions.set(POWER_OFF_ACTION_ID, { | ||||
|             // Translators: The name of the power-off action in search | ||||
|             name: C_("search-result", "Power Off"), | ||||
|             iconName: 'system-shutdown-symbolic', | ||||
|             // Translators: A list of keywords that match the power-off action, separated by semicolons | ||||
|             keywords: _('power off;shutdown;reboot;restart;halt;stop').split(/[; ]/), | ||||
|             keywords: tokenizeKeywords(_('power off;shutdown;reboot;restart;halt;stop')), | ||||
|             available: false, | ||||
|         }); | ||||
|         this._actions.set(LOCK_SCREEN_ACTION_ID, { | ||||
| @@ -97,7 +101,7 @@ const SystemActions = GObject.registerClass({ | ||||
|             name: C_("search-result", "Lock Screen"), | ||||
|             iconName: 'system-lock-screen-symbolic', | ||||
|             // Translators: A list of keywords that match the lock screen action, separated by semicolons | ||||
|             keywords: _("lock screen").split(/[; ]/), | ||||
|             keywords: tokenizeKeywords(_('lock screen')), | ||||
|             available: false, | ||||
|         }); | ||||
|         this._actions.set(LOGOUT_ACTION_ID, { | ||||
| @@ -105,7 +109,7 @@ const SystemActions = GObject.registerClass({ | ||||
|             name: C_("search-result", "Log Out"), | ||||
|             iconName: 'application-exit-symbolic', | ||||
|             // Translators: A list of keywords that match the logout action, separated by semicolons | ||||
|             keywords: _("logout;log out;sign off").split(/[; ]/), | ||||
|             keywords: tokenizeKeywords(_('logout;log out;sign off')), | ||||
|             available: false, | ||||
|         }); | ||||
|         this._actions.set(SUSPEND_ACTION_ID, { | ||||
| @@ -113,7 +117,7 @@ const SystemActions = GObject.registerClass({ | ||||
|             name: C_("search-result", "Suspend"), | ||||
|             iconName: 'media-playback-pause-symbolic', | ||||
|             // Translators: A list of keywords that match the suspend action, separated by semicolons | ||||
|             keywords: _("suspend;sleep").split(/[; ]/), | ||||
|             keywords: tokenizeKeywords(_('suspend;sleep')), | ||||
|             available: false, | ||||
|         }); | ||||
|         this._actions.set(SWITCH_USER_ACTION_ID, { | ||||
| @@ -121,14 +125,14 @@ const SystemActions = GObject.registerClass({ | ||||
|             name: C_("search-result", "Switch User"), | ||||
|             iconName: 'system-switch-user-symbolic', | ||||
|             // Translators: A list of keywords that match the switch user action, separated by semicolons | ||||
|             keywords: _("switch user").split(/[; ]/), | ||||
|             keywords: tokenizeKeywords(_('switch user')), | ||||
|             available: false, | ||||
|         }); | ||||
|         this._actions.set(LOCK_ORIENTATION_ACTION_ID, { | ||||
|             name: '', | ||||
|             iconName: '', | ||||
|             // Translators: A list of keywords that match the lock orientation action, separated by semicolons | ||||
|             keywords: _("lock orientation;unlock orientation;screen;rotation").split(/[; ]/), | ||||
|             keywords: tokenizeKeywords(_('lock orientation;unlock orientation;screen;rotation')), | ||||
|             available: false, | ||||
|         }); | ||||
|  | ||||
| @@ -277,7 +281,7 @@ const SystemActions = GObject.registerClass({ | ||||
|  | ||||
|     getMatchingActions(terms) { | ||||
|         // terms is a list of strings | ||||
|         terms = terms.map(term => term.toLowerCase()); | ||||
|         terms = terms.map(term => GLib.str_tokenize_and_fold(term, null)[0]); | ||||
|  | ||||
|         let results = []; | ||||
|  | ||||
|   | ||||
| @@ -15,8 +15,7 @@ class Animation extends St.Bin { | ||||
|         const themeContext = St.ThemeContext.get_for_stage(global.stage); | ||||
|  | ||||
|         super._init({ | ||||
|             width: width * themeContext.scale_factor, | ||||
|             height: height * themeContext.scale_factor, | ||||
|             style: `width: ${width}px; height: ${height}px;`, | ||||
|         }); | ||||
|  | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
|   | ||||
| @@ -10,6 +10,7 @@ const GrabHelper = imports.ui.grabHelper; | ||||
| const IconGrid = imports.ui.iconGrid; | ||||
| const Main = imports.ui.main; | ||||
| const PageIndicators = imports.ui.pageIndicators; | ||||
| const ParentalControlsManager = imports.misc.parentalControlsManager; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| const Search = imports.ui.search; | ||||
| const SwipeTracker = imports.ui.swipeTracker; | ||||
| @@ -114,7 +115,8 @@ function _findBestFolderName(apps) { | ||||
|     }, commonCategories); | ||||
|  | ||||
|     for (let category of commonCategories) { | ||||
|         let translated = Shell.util_get_translated_folder_name(category); | ||||
|         const directory = '%s.directory'.format(category); | ||||
|         const translated = Shell.util_get_translated_folder_name(directory); | ||||
|         if (translated !== null) | ||||
|             return translated; | ||||
|     } | ||||
| @@ -161,6 +163,12 @@ var BaseAppView = GObject.registerClass({ | ||||
|         this._animateLaterId = 0; | ||||
|         this._viewLoadedHandlerId = 0; | ||||
|         this._viewIsReady = false; | ||||
|  | ||||
|         // Filter the apps through the user’s parental controls. | ||||
|         this._parentalControlsManager = ParentalControlsManager.getDefault(); | ||||
|         this._parentalControlsManager.connect('app-filter-changed', () => { | ||||
|             this._redisplay(); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     _childFocused(_actor) { | ||||
| @@ -334,14 +342,37 @@ var AllView = GObject.registerClass({ | ||||
|             use_pagination: true, | ||||
|         }); | ||||
|  | ||||
|         this._grid._delegate = this; | ||||
|  | ||||
|         this._stack = new St.Widget({ | ||||
|             layout_manager: new Clutter.BinLayout(), | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|         }); | ||||
|         this.add_actor(this._stack); | ||||
|  | ||||
|         let box = new St.BoxLayout({ | ||||
|             vertical: true, | ||||
|             y_align: Clutter.ActorAlign.START, | ||||
|         }); | ||||
|         box.add_child(this._grid); | ||||
|  | ||||
|         this._scrollView = new St.ScrollView({ | ||||
|             style_class: 'all-apps', | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|             reactive: true, | ||||
|         }); | ||||
|         this.add_actor(this._scrollView); | ||||
|         this._grid._delegate = this; | ||||
|         this._scrollView.add_actor(box); | ||||
|         this._stack.add_actor(this._scrollView); | ||||
|  | ||||
|         this._eventBlocker = new St.Widget({ | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|             reactive: true, | ||||
|             visible: false, | ||||
|         }); | ||||
|         this._stack.add_actor(this._eventBlocker); | ||||
|  | ||||
|         this._scrollView.set_policy(St.PolicyType.NEVER, | ||||
|                                     St.PolicyType.EXTERNAL); | ||||
| @@ -362,24 +393,7 @@ var AllView = GObject.registerClass({ | ||||
|  | ||||
|         this._folderIcons = []; | ||||
|  | ||||
|         this._stack = new St.Widget({ layout_manager: new Clutter.BinLayout() }); | ||||
|         let box = new St.BoxLayout({ | ||||
|             vertical: true, | ||||
|             y_align: Clutter.ActorAlign.START, | ||||
|         }); | ||||
|  | ||||
|         this._grid.currentPage = 0; | ||||
|         this._stack.add_actor(this._grid); | ||||
|         this._eventBlocker = new St.Widget({ | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|             reactive: true, | ||||
|             visible: false, | ||||
|         }); | ||||
|         this._stack.add_actor(this._eventBlocker); | ||||
|  | ||||
|         box.add_actor(this._stack); | ||||
|         this._scrollView.add_actor(box); | ||||
|  | ||||
|         this._scrollView.connect('scroll-event', this._onScroll.bind(this)); | ||||
|  | ||||
| @@ -514,7 +528,7 @@ var AllView = GObject.registerClass({ | ||||
|             } catch (e) { | ||||
|                 return false; | ||||
|             } | ||||
|             return appInfo.should_show(); | ||||
|             return this._parentalControlsManager.shouldShowApp(appInfo); | ||||
|         }); | ||||
|  | ||||
|         let apps = this._appInfoList.map(app => app.get_id()); | ||||
| @@ -624,7 +638,7 @@ var AllView = GObject.registerClass({ | ||||
|  | ||||
|         this._grid.currentPage = pageNumber; | ||||
|  | ||||
|         // Tween the change between pages. | ||||
|         // Animate the change between pages. | ||||
|         this._adjustment.ease(this._grid.getPageY(this._grid.currentPage), { | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_CUBIC, | ||||
|             duration: animate ? PAGE_SWITCH_TIME : 0, | ||||
| @@ -1004,7 +1018,7 @@ class FrequentView extends BaseAppView { | ||||
|         let favoritesWritable = global.settings.is_writable('favorite-apps'); | ||||
|  | ||||
|         for (let i = 0; i < mostUsed.length; i++) { | ||||
|             if (!mostUsed[i].get_app_info().should_show()) | ||||
|             if (!this._parentalControlsManager.shouldShowApp(mostUsed[i].get_app_info())) | ||||
|                 continue; | ||||
|             let appIcon = this._items.get(mostUsed[i].get_id()); | ||||
|             if (!appIcon) { | ||||
| @@ -1250,6 +1264,8 @@ var AppSearchProvider = class AppSearchProvider { | ||||
|         this.canLaunchSearch = false; | ||||
|  | ||||
|         this._systemActions = new SystemActions.getDefault(); | ||||
|  | ||||
|         this._parentalControlsManager = ParentalControlsManager.getDefault(); | ||||
|     } | ||||
|  | ||||
|     getResultMetas(apps, callback) { | ||||
| @@ -1284,14 +1300,27 @@ var AppSearchProvider = class AppSearchProvider { | ||||
|     } | ||||
|  | ||||
|     getInitialResultSet(terms, callback, _cancellable) { | ||||
|         // Defer until the parental controls manager is initialised, so the | ||||
|         // results can be filtered correctly. | ||||
|         if (!this._parentalControlsManager.initialized) { | ||||
|             let initializedId = this._parentalControlsManager.connect('app-filter-changed', () => { | ||||
|                 if (this._parentalControlsManager.initialized) { | ||||
|                     this._parentalControlsManager.disconnect(initializedId); | ||||
|                     this.getInitialResultSet(terms, callback, _cancellable); | ||||
|                 } | ||||
|             }); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let query = terms.join(' '); | ||||
|         let groups = Shell.AppSystem.search(query); | ||||
|         let usage = Shell.AppUsage.get_default(); | ||||
|         let results = []; | ||||
|  | ||||
|         groups.forEach(group => { | ||||
|             group = group.filter(appID => { | ||||
|                 const app = this._appSys.lookup_app(appID); | ||||
|                 return app && app.app_info.should_show(); | ||||
|                 return app && this._parentalControlsManager.shouldShowApp(app.app_info); | ||||
|             }); | ||||
|             results = results.concat(group.sort( | ||||
|                 (a, b) => usage.compare(a, b) | ||||
| @@ -1376,12 +1405,12 @@ class FolderView extends BaseAppView { | ||||
|         }); | ||||
|         layout.hookup_style(icon); | ||||
|         let subSize = Math.floor(FOLDER_SUBICON_FRACTION * size); | ||||
|         let scale = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|  | ||||
|         let numItems = this._orderedItems.length; | ||||
|         let rtl = icon.get_text_direction() == Clutter.TextDirection.RTL; | ||||
|         for (let i = 0; i < 4; i++) { | ||||
|             let bin = new St.Bin({ width: subSize * scale, height: subSize * scale }); | ||||
|             const style = 'width: %dpx; height: %dpx;'.format(subSize, subSize); | ||||
|             let bin = new St.Bin({ style }); | ||||
|             if (i < numItems) | ||||
|                 bin.child = this._orderedItems[i].app.create_icon_texture(subSize); | ||||
|             layout.attach(bin, rtl ? (i + 1) % 2 : i % 2, Math.floor(i / 2), 1, 1); | ||||
| @@ -1430,7 +1459,7 @@ class FolderView extends BaseAppView { | ||||
|             if (!app) | ||||
|                 return; | ||||
|  | ||||
|             if (!app.get_app_info().should_show()) | ||||
|             if (!this._parentalControlsManager.shouldShowApp(app.get_app_info())) | ||||
|                 return; | ||||
|  | ||||
|             if (apps.some(appIcon => appIcon.id == appId)) | ||||
| @@ -2176,7 +2205,7 @@ var AppIcon = GObject.registerClass({ | ||||
|     } | ||||
|  | ||||
|     vfunc_leave_event(crossingEvent) { | ||||
|         let ret = super.vfunc_leave_event(crossingEvent); | ||||
|         const ret = super.vfunc_leave_event(crossingEvent); | ||||
|  | ||||
|         this.fake_release(); | ||||
|         this._removeMenuTimeout(); | ||||
| @@ -2184,22 +2213,22 @@ var AppIcon = GObject.registerClass({ | ||||
|     } | ||||
|  | ||||
|     vfunc_button_press_event(buttonEvent) { | ||||
|         super.vfunc_button_press_event(buttonEvent); | ||||
|         const ret = super.vfunc_button_press_event(buttonEvent); | ||||
|         if (buttonEvent.button == 1) { | ||||
|             this._setPopupTimeout(); | ||||
|         } else if (buttonEvent.button == 3) { | ||||
|             this.popupMenu(); | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     vfunc_touch_event(touchEvent) { | ||||
|         super.vfunc_touch_event(touchEvent); | ||||
|         const ret = super.vfunc_touch_event(touchEvent); | ||||
|         if (touchEvent.type == Clutter.EventType.TOUCH_BEGIN) | ||||
|             this._setPopupTimeout(); | ||||
|  | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     vfunc_clicked(button) { | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
| /* exported getAppFavorites */ | ||||
|  | ||||
| const Shell = imports.gi.Shell; | ||||
| const ParentalControlsManager = imports.misc.parentalControlsManager; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| @@ -64,6 +65,13 @@ const RENAMED_DESKTOP_IDS = { | ||||
|  | ||||
| class AppFavorites { | ||||
|     constructor() { | ||||
|         // Filter the apps through the user’s parental controls. | ||||
|         this._parentalControlsManager = ParentalControlsManager.getDefault(); | ||||
|         this._parentalControlsManager.connect('app-filter-changed', () => { | ||||
|             this.reload(); | ||||
|             this.emit('changed'); | ||||
|         }); | ||||
|  | ||||
|         this.FAVORITE_APPS_KEY = 'favorite-apps'; | ||||
|         this._favorites = {}; | ||||
|         global.settings.connect('changed::%s'.format(this.FAVORITE_APPS_KEY), this._onFavsChanged.bind(this)); | ||||
| @@ -95,7 +103,7 @@ class AppFavorites { | ||||
|             global.settings.set_strv(this.FAVORITE_APPS_KEY, ids); | ||||
|  | ||||
|         let apps = ids.map(id => appSys.lookup_app(id)) | ||||
|                       .filter(app => app != null); | ||||
|                       .filter(app => app !== null && this._parentalControlsManager.shouldShowApp(app.app_info)); | ||||
|         this._favorites = {}; | ||||
|         for (let i = 0; i < apps.length; i++) { | ||||
|             let app = apps[i]; | ||||
| @@ -134,6 +142,9 @@ class AppFavorites { | ||||
|         if (!app) | ||||
|             return false; | ||||
|  | ||||
|         if (!this._parentalControlsManager.shouldShowApp(app.app_info)) | ||||
|             return false; | ||||
|  | ||||
|         let ids = this._getIds(); | ||||
|         if (pos == -1) | ||||
|             ids.push(appId); | ||||
|   | ||||
| @@ -222,7 +222,12 @@ class DBusEventSource extends EventSourceBase { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         this._dbusProxy.connectSignal('Changed', this._onChanged.bind(this)); | ||||
|         this._dbusProxy.connectSignal('EventsAddedOrUpdated', | ||||
|             this._onEventsAddedOrUpdated.bind(this)); | ||||
|         this._dbusProxy.connectSignal('EventsRemoved', | ||||
|             this._onEventsRemoved.bind(this)); | ||||
|         this._dbusProxy.connectSignal('ClientDisappeared', | ||||
|             this._onClientDisappeared.bind(this)); | ||||
|  | ||||
|         this._dbusProxy.connect('notify::g-name-owner', () => { | ||||
|             if (this._dbusProxy.g_name_owner) | ||||
| @@ -258,7 +263,7 @@ class DBusEventSource extends EventSourceBase { | ||||
|     } | ||||
|  | ||||
|     _resetCache() { | ||||
|         this._events = []; | ||||
|         this._events = new Map(); | ||||
|         this._lastRequestBegin = null; | ||||
|         this._lastRequestEnd = null; | ||||
|     } | ||||
| @@ -274,28 +279,47 @@ class DBusEventSource extends EventSourceBase { | ||||
|         this.emit('changed'); | ||||
|     } | ||||
|  | ||||
|     _onChanged() { | ||||
|         this._loadEvents(false); | ||||
|     _onEventsAddedOrUpdated(dbusProxy, nameOwner, argArray) { | ||||
|         const [appointments = []] = argArray; | ||||
|         let changed = false; | ||||
|  | ||||
|         for (let n = 0; n < appointments.length; n++) { | ||||
|             const [id, summary, allDay, startTime, endTime] = appointments[n]; | ||||
|             const date = new Date(startTime * 1000); | ||||
|             const end = new Date(endTime * 1000); | ||||
|             let event = new CalendarEvent(id, date, end, summary, allDay); | ||||
|             this._events.set(event.id, event); | ||||
|  | ||||
|             changed = true; | ||||
|         } | ||||
|  | ||||
|         if (changed) | ||||
|             this.emit('changed'); | ||||
|     } | ||||
|  | ||||
|     _onEventsReceived(results, _error) { | ||||
|         let newEvents = []; | ||||
|         let appointments = results[0] || []; | ||||
|         for (let n = 0; n < appointments.length; n++) { | ||||
|             let a = appointments[n]; | ||||
|             let date = new Date(a[4] * 1000); | ||||
|             let end = new Date(a[5] * 1000); | ||||
|             let id = a[0]; | ||||
|             let summary = a[1]; | ||||
|             let allDay = a[3]; | ||||
|             let event = new CalendarEvent(id, date, end, summary, allDay); | ||||
|             newEvents.push(event); | ||||
|         } | ||||
|         newEvents.sort((ev1, ev2) => ev1.date.getTime() - ev2.date.getTime()); | ||||
|     _onEventsRemoved(dbusProxy, nameOwner, argArray) { | ||||
|         const [ids = []] = argArray; | ||||
|  | ||||
|         this._events = newEvents; | ||||
|         this._isLoading = false; | ||||
|         this.emit('changed'); | ||||
|         let changed = false; | ||||
|         for (const id of ids) | ||||
|             changed |= this._events.delete(id); | ||||
|  | ||||
|         if (changed) | ||||
|             this.emit('changed'); | ||||
|     } | ||||
|  | ||||
|     _onClientDisappeared(dbusProxy, nameOwner, argArray) { | ||||
|         let [sourceUid = ''] = argArray; | ||||
|         sourceUid += '\n'; | ||||
|  | ||||
|         let changed = false; | ||||
|         for (const id of this._events.keys()) { | ||||
|             if (id.startsWith(sourceUid)) | ||||
|                 changed |= this._events.delete(id); | ||||
|         } | ||||
|  | ||||
|         if (changed) | ||||
|             this.emit('changed'); | ||||
|     } | ||||
|  | ||||
|     _loadEvents(forceReload) { | ||||
| @@ -304,33 +328,38 @@ class DBusEventSource extends EventSourceBase { | ||||
|             return; | ||||
|  | ||||
|         if (this._curRequestBegin && this._curRequestEnd) { | ||||
|             this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000, | ||||
|                                             this._curRequestEnd.getTime() / 1000, | ||||
|                                             forceReload, | ||||
|                                             this._onEventsReceived.bind(this), | ||||
|                                             Gio.DBusCallFlags.NONE); | ||||
|             if (forceReload) { | ||||
|                 this._events.clear(); | ||||
|                 this.emit('changed'); | ||||
|             } | ||||
|             this._dbusProxy.SetTimeRangeRemote( | ||||
|                 this._curRequestBegin.getTime() / 1000, | ||||
|                 this._curRequestEnd.getTime() / 1000, | ||||
|                 forceReload, | ||||
|                 Gio.DBusCallFlags.NONE); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     requestRange(begin, end) { | ||||
|         if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) { | ||||
|             this._isLoading = true; | ||||
|             this._lastRequestBegin = begin; | ||||
|             this._lastRequestEnd = end; | ||||
|             this._curRequestBegin = begin; | ||||
|             this._curRequestEnd = end; | ||||
|             this._loadEvents(false); | ||||
|             this._loadEvents(true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     *_getFilteredEvents(begin, end) { | ||||
|         for (const event of this._events.values()) { | ||||
|             if (_dateIntervalsOverlap(event.date, event.end, begin, end)) | ||||
|                 yield event; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     getEvents(begin, end) { | ||||
|         let result = []; | ||||
|         for (let n = 0; n < this._events.length; n++) { | ||||
|             let event = this._events[n]; | ||||
|         let result = [...this._getFilteredEvents(begin, end)]; | ||||
|  | ||||
|             if (_dateIntervalsOverlap(event.date, event.end, begin, end)) | ||||
|                 result.push(event); | ||||
|         } | ||||
|         result.sort((event1, event2) => { | ||||
|             // sort events by end time on ending day | ||||
|             let d1 = event1.date < begin && event1.end <= end ? event1.end : event1.date; | ||||
| @@ -344,12 +373,8 @@ class DBusEventSource extends EventSourceBase { | ||||
|         let dayBegin = _getBeginningOfDay(day); | ||||
|         let dayEnd = _getEndOfDay(day); | ||||
|  | ||||
|         let events = this.getEvents(dayBegin, dayEnd); | ||||
|  | ||||
|         if (events.length == 0) | ||||
|             return false; | ||||
|  | ||||
|         return true; | ||||
|         const { done } = this._getFilteredEvents(dayBegin, dayEnd).next(); | ||||
|         return !done; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -701,12 +726,11 @@ var Calendar = GObject.registerClass({ | ||||
| var EventMessage = GObject.registerClass( | ||||
| class EventMessage extends MessageList.Message { | ||||
|     _init(event, date) { | ||||
|         super._init('', event.summary); | ||||
|         super._init('', ''); | ||||
|  | ||||
|         this._event = event; | ||||
|         this._date = date; | ||||
|  | ||||
|         this.setTitle(this._formatEventTime()); | ||||
|         this.update(event); | ||||
|  | ||||
|         this._icon = new St.Icon({ icon_name: 'x-office-calendar-symbolic' }); | ||||
|         this.setIcon(this._icon); | ||||
| @@ -718,6 +742,13 @@ class EventMessage extends MessageList.Message { | ||||
|         super.vfunc_style_changed(); | ||||
|     } | ||||
|  | ||||
|     update(event) { | ||||
|         this._event = event; | ||||
|  | ||||
|         this.setTitle(this._formatEventTime()); | ||||
|         this.setBody(event.summary); | ||||
|     } | ||||
|  | ||||
|     _formatEventTime() { | ||||
|         let periodBegin = _getBeginningOfDay(this._date); | ||||
|         let periodEnd = _getEndOfDay(this._date); | ||||
| @@ -875,7 +906,7 @@ class EventsSection extends MessageList.MessageListSection { | ||||
|     } | ||||
|  | ||||
|     _reloadEvents() { | ||||
|         if (this._eventSource.isLoading) | ||||
|         if (this._eventSource.isLoading || this._reloading) | ||||
|             return; | ||||
|  | ||||
|         this._reloading = true; | ||||
| @@ -901,6 +932,7 @@ class EventsSection extends MessageList.MessageListSection { | ||||
|                 this._messageById.set(event.id, message); | ||||
|                 this.addMessage(message, false); | ||||
|             } else { | ||||
|                 message.update(event); | ||||
|                 this.moveMessage(message, i, false); | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -327,12 +327,16 @@ var AuthenticationDialog = GObject.registerClass({ | ||||
|         } | ||||
|  | ||||
|         let resetDialog = () => { | ||||
|             this._sessionRequestTimeoutId = 0; | ||||
|  | ||||
|             if (this.state != ModalDialog.State.OPENED) | ||||
|                 return; | ||||
|                 return GLib.SOURCE_REMOVE; | ||||
|  | ||||
|             this._passwordEntry.hide(); | ||||
|             this._cancelButton.grab_key_focus(); | ||||
|             this._okButton.reactive = false; | ||||
|  | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|         }; | ||||
|  | ||||
|         if (delay) { | ||||
|   | ||||
| @@ -281,13 +281,13 @@ class WeatherSection extends St.Button { | ||||
|         this.child = box; | ||||
|  | ||||
|         let titleBox = new St.BoxLayout({ style_class: 'weather-header-box' }); | ||||
|         titleBox.add_child(new St.Label({ | ||||
|         this._titleLabel = new St.Label({ | ||||
|             style_class: 'weather-header', | ||||
|             x_align: Clutter.ActorAlign.START, | ||||
|             x_expand: true, | ||||
|             y_align: Clutter.ActorAlign.END, | ||||
|             text: _('Weather'), | ||||
|         })); | ||||
|         }); | ||||
|         titleBox.add_child(this._titleLabel); | ||||
|         box.add_child(titleBox); | ||||
|  | ||||
|         this._titleLocation = new St.Label({ | ||||
| @@ -414,10 +414,8 @@ class WeatherSection extends St.Button { | ||||
|     _updateForecasts() { | ||||
|         this._forecastGrid.destroy_all_children(); | ||||
|  | ||||
|         if (!this._weatherClient.hasLocation) { | ||||
|             this._setStatusLabel(_("Select a location…")); | ||||
|         if (!this._weatherClient.hasLocation) | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         const { info } = this._weatherClient; | ||||
|         this._titleLocation.text = this._findBestLocationName(info.location); | ||||
| @@ -444,6 +442,12 @@ class WeatherSection extends St.Button { | ||||
|         if (!this.visible) | ||||
|             return; | ||||
|  | ||||
|         if (this._weatherClient.hasLocation) | ||||
|             this._titleLabel.text = _('Weather'); | ||||
|         else | ||||
|             this._titleLabel.text = _('Select weather location…'); | ||||
|  | ||||
|         this._forecastGrid.visible = this._weatherClient.hasLocation; | ||||
|         this._titleLocation.visible = this._weatherClient.hasLocation; | ||||
|  | ||||
|         this._updateForecasts(); | ||||
|   | ||||
| @@ -245,16 +245,15 @@ function _loggingFunc(...args) { | ||||
| } | ||||
|  | ||||
| function init() { | ||||
|     // Add some bindings to the global JS namespace; (gjs keeps the web | ||||
|     // browser convention of having that namespace be called 'window'.) | ||||
|     window.global = Shell.Global.get(); | ||||
|     // Add some bindings to the global JS namespace | ||||
|     globalThis.global = Shell.Global.get(); | ||||
|  | ||||
|     window.log = _loggingFunc; | ||||
|     globalThis.log = _loggingFunc; | ||||
|  | ||||
|     window._ = Gettext.gettext; | ||||
|     window.C_ = Gettext.pgettext; | ||||
|     window.ngettext = Gettext.ngettext; | ||||
|     window.N_ = s => s; | ||||
|     globalThis._ = Gettext.gettext; | ||||
|     globalThis.C_ = Gettext.pgettext; | ||||
|     globalThis.ngettext = Gettext.ngettext; | ||||
|     globalThis.N_ = s => s; | ||||
|  | ||||
|     GObject.gtypeNameBasedOnJSPath = true; | ||||
|  | ||||
| @@ -356,9 +355,7 @@ function init() { | ||||
|  | ||||
|     // OK, now things are initialized enough that we can import shell JS | ||||
|     const Format = imports.format; | ||||
|     const Tweener = imports.ui.tweener; | ||||
|  | ||||
|     Tweener.init(); | ||||
|     String.prototype.format = Format.format; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1119,7 +1119,7 @@ var KeyboardManager = class KeyBoardManager { | ||||
|         this._seat.connect('notify::touch-mode', this._syncEnabled.bind(this)); | ||||
|  | ||||
|         this._lastDevice = null; | ||||
|         Meta.get_backend().connect('last-device-changed', (backend, device) => { | ||||
|         global.backend.connect('last-device-changed', (backend, device) => { | ||||
|             if (device.device_type === Clutter.InputDeviceType.KEYBOARD_DEVICE) | ||||
|                 return; | ||||
|  | ||||
|   | ||||
| @@ -37,8 +37,8 @@ const LG_ANIMATION_TIME = 500; | ||||
|  | ||||
| function _getAutoCompleteGlobalKeywords() { | ||||
|     const keywords = ['true', 'false', 'null', 'new']; | ||||
|     // Don't add the private properties of window (i.e., ones starting with '_') | ||||
|     const windowProperties = Object.getOwnPropertyNames(window).filter( | ||||
|     // Don't add the private properties of globalThis (i.e., ones starting with '_') | ||||
|     const windowProperties = Object.getOwnPropertyNames(globalThis).filter( | ||||
|         a => a.charAt(0) != '_' | ||||
|     ); | ||||
|     const headerProperties = JsParse.getDeclaredConstants(commandHeader); | ||||
|   | ||||
| @@ -46,6 +46,7 @@ const XdndHandler = imports.ui.xdndHandler; | ||||
| const KbdA11yDialog = imports.ui.kbdA11yDialog; | ||||
| const LocatePointer = imports.ui.locatePointer; | ||||
| const PointerA11yTimeout = imports.ui.pointerA11yTimeout; | ||||
| const ParentalControlsManager = imports.misc.parentalControlsManager; | ||||
|  | ||||
| const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard'; | ||||
| const STICKY_KEYS_ENABLE = 'stickykeys-enable'; | ||||
| @@ -96,6 +97,8 @@ let _oskResource = null; | ||||
| Gio._promisify(Gio._LocalFilePrototype, 'delete_async', 'delete_finish'); | ||||
| Gio._promisify(Gio._LocalFilePrototype, 'touch_async', 'touch_finish'); | ||||
|  | ||||
| let _remoteAccessInhibited = false; | ||||
|  | ||||
| function _sessionUpdated() { | ||||
|     if (sessionMode.isPrimary) | ||||
|         _loadDefaultStylesheet(); | ||||
| @@ -120,12 +123,23 @@ function _sessionUpdated() { | ||||
|         if (lookingGlass) | ||||
|             lookingGlass.close(); | ||||
|     } | ||||
|  | ||||
|     let remoteAccessController = global.backend.get_remote_access_controller(); | ||||
|     if (remoteAccessController) { | ||||
|         if (sessionMode.allowScreencast && _remoteAccessInhibited) { | ||||
|             remoteAccessController.uninhibit_remote_access(); | ||||
|             _remoteAccessInhibited = false; | ||||
|         } else if (!sessionMode.allowScreencast && !_remoteAccessInhibited) { | ||||
|             remoteAccessController.inhibit_remote_access(); | ||||
|             _remoteAccessInhibited = true; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| function start() { | ||||
|     // These are here so we don't break compatibility. | ||||
|     global.logError = window.log; | ||||
|     global.log = window.log; | ||||
|     global.logError = globalThis.log; | ||||
|     global.log = globalThis.log; | ||||
|  | ||||
|     // Chain up async errors reported from C | ||||
|     global.connect('notify-error', (global, msg, detail) => { | ||||
| @@ -140,6 +154,10 @@ function start() { | ||||
|     sessionMode.connect('updated', _sessionUpdated); | ||||
|  | ||||
|     St.Settings.get().connect('notify::gtk-theme', _loadDefaultStylesheet); | ||||
|  | ||||
|     // Initialize ParentalControlsManager before the UI | ||||
|     ParentalControlsManager.getDefault(); | ||||
|  | ||||
|     _initializeUI(); | ||||
|  | ||||
|     shellAccessDialogDBusService = new AccessDialog.AccessDialogDBus(); | ||||
| @@ -797,7 +815,7 @@ function showRestartMessage(message) { | ||||
|  | ||||
| var AnimationsSettings = class { | ||||
|     constructor() { | ||||
|         let backend = Meta.get_backend(); | ||||
|         let backend = global.backend; | ||||
|         if (!backend.is_rendering_hardware_accelerated()) { | ||||
|             St.Settings.get().inhibit_animations(); | ||||
|             return; | ||||
|   | ||||
| @@ -136,29 +136,22 @@ var FocusGrabber = class FocusGrabber { | ||||
| var NotificationPolicy = GObject.registerClass({ | ||||
|     Properties: { | ||||
|         'enable': GObject.ParamSpec.boolean( | ||||
|             'enable', 'enable', 'enable', | ||||
|             GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, | ||||
|             true), | ||||
|             'enable', 'enable', 'enable', GObject.ParamFlags.READABLE, true), | ||||
|         'enable-sound': GObject.ParamSpec.boolean( | ||||
|             'enable-sound', 'enable-sound', 'enable-sound', | ||||
|             GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, | ||||
|             true), | ||||
|             GObject.ParamFlags.READABLE, true), | ||||
|         'show-banners': GObject.ParamSpec.boolean( | ||||
|             'show-banners', 'show-banners', 'show-banners', | ||||
|             GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, | ||||
|             true), | ||||
|             GObject.ParamFlags.READABLE, true), | ||||
|         'force-expanded': GObject.ParamSpec.boolean( | ||||
|             'force-expanded', 'force-expanded', 'force-expanded', | ||||
|             GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, | ||||
|             false), | ||||
|             GObject.ParamFlags.READABLE, false), | ||||
|         'show-in-lock-screen': GObject.ParamSpec.boolean( | ||||
|             'show-in-lock-screen', 'show-in-lock-screen', 'show-in-lock-screen', | ||||
|             GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, | ||||
|             false), | ||||
|             GObject.ParamFlags.READABLE, false), | ||||
|         'details-in-lock-screen': GObject.ParamSpec.boolean( | ||||
|             'details-in-lock-screen', 'details-in-lock-screen', 'details-in-lock-screen', | ||||
|             GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, | ||||
|             false), | ||||
|             GObject.ParamFlags.READABLE, false), | ||||
|     }, | ||||
| }, class NotificationPolicy extends GObject.Object { | ||||
|     // Do nothing for the default policy. These methods are only useful for the | ||||
| @@ -170,23 +163,23 @@ var NotificationPolicy = GObject.registerClass({ | ||||
|     } | ||||
|  | ||||
|     get enableSound() { | ||||
|         return this.enable_sound; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     get showBanners() { | ||||
|         return this.show_banners; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     get forceExpanded() { | ||||
|         return this.force_expanded; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     get showInLockScreen() { | ||||
|         return this.show_in_lock_screen; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     get detailsInLockScreen() { | ||||
|         return this.details_in_lock_screen; | ||||
|         return false; | ||||
|     } | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -281,7 +281,7 @@ var AppMenuButton = GObject.registerClass({ | ||||
|         this.remove_all_transitions(); | ||||
|         this.ease({ | ||||
|             opacity: 0, | ||||
|             mode: Clutter.Animation.EASE_OUT_QUAD, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             duration: Overview.ANIMATION_TIME, | ||||
|             onComplete: () => this.hide(), | ||||
|         }); | ||||
|   | ||||
| @@ -198,7 +198,7 @@ var ScreenShield = class { | ||||
|         let lockEnabled = this._settings.get_boolean(LOCK_ENABLED_KEY); | ||||
|         let lockLocked = this._lockSettings.get_boolean(DISABLE_LOCK_KEY); | ||||
|         let inhibit = this._loginSession && this._loginSession.Active && | ||||
|                        !this._isActive && lockEnabled && !lockLocked; | ||||
|                        !this._isActive && lockEnabled && !lockLocked && Main.sessionMode.unlockDialog; | ||||
|         if (inhibit) { | ||||
|             this._loginManager.inhibit(_("GNOME needs to lock the screen"), | ||||
|                 inhibitor => { | ||||
| @@ -345,7 +345,7 @@ var ScreenShield = class { | ||||
|         this._lockDialogGroup.remove_all_transitions(); | ||||
|  | ||||
|         if (animate) { | ||||
|             // Tween the lock screen out of screen | ||||
|             // Animate the lock screen out of screen | ||||
|             // if velocity is not specified (i.e. we come here from pressing ESC), | ||||
|             // use the same speed regardless of original position | ||||
|             // if velocity is specified, it's in pixels per milliseconds | ||||
| @@ -561,7 +561,8 @@ var ScreenShield = class { | ||||
|         if (this._activationTime == 0) | ||||
|             this._activationTime = GLib.get_monotonic_time(); | ||||
|  | ||||
|         this._ensureUnlockDialog(true); | ||||
|         if (!this._ensureUnlockDialog(true)) | ||||
|             return; | ||||
|  | ||||
|         this.actor.show(); | ||||
|  | ||||
|   | ||||
| @@ -6,6 +6,7 @@ const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi; | ||||
| const AppDisplay = imports.ui.appDisplay; | ||||
| const IconGrid = imports.ui.iconGrid; | ||||
| const Main = imports.ui.main; | ||||
| const ParentalControlsManager = imports.misc.parentalControlsManager; | ||||
| const RemoteSearch = imports.ui.remoteSearch; | ||||
| const Util = imports.misc.util; | ||||
|  | ||||
| @@ -431,6 +432,9 @@ var SearchResultsView = GObject.registerClass({ | ||||
|     _init() { | ||||
|         super._init({ name: 'searchResults', vertical: true }); | ||||
|  | ||||
|         this._parentalControlsManager = ParentalControlsManager.getDefault(); | ||||
|         this._parentalControlsManager.connect('app-filter-changed', this._reloadRemoteProviders.bind(this)); | ||||
|  | ||||
|         this._content = new MaxWidthBox({ | ||||
|             name: 'searchResultsContent', | ||||
|             vertical: true, | ||||
| @@ -505,6 +509,11 @@ var SearchResultsView = GObject.registerClass({ | ||||
|  | ||||
|     _registerProvider(provider) { | ||||
|         provider.searchInProgress = false; | ||||
|  | ||||
|         // Filter out unwanted providers. | ||||
|         if (provider.appInfo && !this._parentalControlsManager.shouldShowApp(provider.appInfo)) | ||||
|             return; | ||||
|  | ||||
|         this._providers.push(provider); | ||||
|         this._ensureProviderDisplay(provider); | ||||
|     } | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported Indicator */ | ||||
|  | ||||
| const { Gio, GnomeBluetooth, GObject } = imports.gi; | ||||
| const { Gio, GLib, GnomeBluetooth, GObject } = imports.gi; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const PanelMenu = imports.ui.panelMenu; | ||||
| @@ -35,7 +35,7 @@ class Indicator extends PanelMenu.SystemIndicator { | ||||
|  | ||||
|                                                  this._sync(); | ||||
|                                              }); | ||||
|         this._proxy.connect('g-properties-changed', this._sync.bind(this)); | ||||
|         this._proxy.connect('g-properties-changed', this._queueSync.bind(this)); | ||||
|  | ||||
|         this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true); | ||||
|         this._item.icon.icon_name = 'bluetooth-active-symbolic'; | ||||
| @@ -49,15 +49,27 @@ class Indicator extends PanelMenu.SystemIndicator { | ||||
|         this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop'); | ||||
|         this.menu.addMenuItem(this._item); | ||||
|  | ||||
|         this._syncId = 0; | ||||
|         this._adapter = null; | ||||
|  | ||||
|         this._client = new GnomeBluetooth.Client(); | ||||
|         this._model = this._client.get_model(); | ||||
|         this._model.connect('row-changed', this._sync.bind(this)); | ||||
|         this._model.connect('row-deleted', this._sync.bind(this)); | ||||
|         this._model.connect('row-deleted', this._queueSync.bind(this)); | ||||
|         this._model.connect('row-changed', this._queueSync.bind(this)); | ||||
|         this._model.connect('row-inserted', this._sync.bind(this)); | ||||
|         Main.sessionMode.connect('updated', this._sync.bind(this)); | ||||
|         this._sync(); | ||||
|     } | ||||
|  | ||||
|     _setHadSetupDevices(value) { | ||||
|         if (this._hadSetupDevices === value) | ||||
|             return; | ||||
|  | ||||
|         this._hadSetupDevices = value; | ||||
|         global.settings.set_boolean( | ||||
|             HAD_BLUETOOTH_DEVICES_SETUP, this._hadSetupDevices); | ||||
|     } | ||||
|  | ||||
|     _getDefaultAdapter() { | ||||
|         let [ret, iter] = this._model.get_iter_first(); | ||||
|         while (ret) { | ||||
| @@ -96,20 +108,28 @@ class Indicator extends PanelMenu.SystemIndicator { | ||||
|             ret = this._model.iter_next(iter); | ||||
|         } | ||||
|  | ||||
|         if (this._hadSetupDevices !== (deviceInfos.length > 0)) { | ||||
|             this._hadSetupDevices = !this._hadSetupDevices; | ||||
|             global.settings.set_boolean(HAD_BLUETOOTH_DEVICES_SETUP, this._hadSetupDevices); | ||||
|         } | ||||
|  | ||||
|         return deviceInfos; | ||||
|     } | ||||
|  | ||||
|     _queueSync() { | ||||
|         if (this._syncId) | ||||
|             return; | ||||
|         this._syncId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { | ||||
|             this._syncId = 0; | ||||
|             this._sync(); | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     _sync() { | ||||
|         let adapter = this._getDefaultAdapter(); | ||||
|         let devices = this._getDeviceInfos(adapter); | ||||
|         const connectedDevices = devices.filter(dev => dev.connected); | ||||
|         const nConnectedDevices = connectedDevices.length; | ||||
|         const nDevices = devices.length; | ||||
|  | ||||
|         if (adapter && this._adapter) | ||||
|             this._setHadSetupDevices(devices.length > 0); | ||||
|         this._adapter = adapter; | ||||
|  | ||||
|         let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter; | ||||
|  | ||||
| @@ -118,7 +138,7 @@ class Indicator extends PanelMenu.SystemIndicator { | ||||
|  | ||||
|         // Remember if there were setup devices and show the menu | ||||
|         // if we've seen setup devices and we're not hard blocked | ||||
|         if (nDevices > 0) | ||||
|         if (this._hadSetupDevices) | ||||
|             this._item.visible = !this._proxy.BluetoothHardwareAirplaneMode; | ||||
|         else | ||||
|             this._item.visible = this._proxy.BluetoothHasAirplaneMode && !this._proxy.BluetoothAirplaneMode; | ||||
|   | ||||
| @@ -11,8 +11,7 @@ class RemoteAccessApplet extends PanelMenu.SystemIndicator { | ||||
|     _init() { | ||||
|         super._init(); | ||||
|  | ||||
|         let backend = Meta.get_backend(); | ||||
|         let controller = backend.get_remote_access_controller(); | ||||
|         let controller = global.backend.get_remote_access_controller(); | ||||
|  | ||||
|         if (!controller) | ||||
|             return; | ||||
|   | ||||
| @@ -317,7 +317,7 @@ var SwitcherPopup = GObject.registerClass({ | ||||
|             this.ease({ | ||||
|                 opacity: 0, | ||||
|                 duration: POPUP_FADE_OUT_TIME, | ||||
|                 mode: Clutter.Animation.EASE_OUT_QUAD, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|                 onComplete: () => this.destroy(), | ||||
|             }); | ||||
|         } else { | ||||
|   | ||||
							
								
								
									
										228
									
								
								js/ui/tweener.js
									
									
									
									
									
								
							
							
						
						
									
										228
									
								
								js/ui/tweener.js
									
									
									
									
									
								
							| @@ -1,228 +0,0 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported init, addCaller, addTween, getTweenCount, removeTweens, | ||||
|             pauseTweens, resumeTweens, registerSpecialProperty, | ||||
|             registerSpecialPropertyModifier, registerSpecialPropertySplitter */ | ||||
|  | ||||
| const { Clutter, GLib, Shell } = imports.gi; | ||||
| const Signals = imports.signals; | ||||
| const Tweener = imports.tweener.tweener; | ||||
|  | ||||
| const { adjustAnimationTime } = imports.ui.environment; | ||||
|  | ||||
| // This is a wrapper around imports.tweener.tweener that adds a bit of | ||||
| // Clutter integration. If the tweening target is a Clutter.Actor, then | ||||
| // the tweenings will automatically be removed if the actor is destroyed. | ||||
|  | ||||
| // ActionScript Tweener methods that imports.tweener.tweener doesn't | ||||
| // currently implement: getTweens, getVersion, registerTransition, | ||||
| // setTimeScale, updateTime. | ||||
|  | ||||
| // imports.tweener.tweener methods that we don't re-export: | ||||
| // pauseAllTweens, removeAllTweens, resumeAllTweens. (It would be hard | ||||
| // to clean up properly after removeAllTweens, and also, any code that | ||||
| // calls any of these is almost certainly wrong anyway, because they | ||||
| // affect the entire application.) | ||||
|  | ||||
| // Called from Main.start | ||||
| function init() { | ||||
|     Tweener.setFrameTicker(new ClutterFrameTicker()); | ||||
| } | ||||
|  | ||||
|  | ||||
| function addCaller(target, tweeningParameters) { | ||||
|     _wrapTweening(target, tweeningParameters); | ||||
|     Tweener.addCaller(target, tweeningParameters); | ||||
| } | ||||
|  | ||||
| function addTween(target, tweeningParameters) { | ||||
|     _wrapTweening(target, tweeningParameters); | ||||
|     Tweener.addTween(target, tweeningParameters); | ||||
| } | ||||
|  | ||||
| function _wrapTweening(target, tweeningParameters) { | ||||
|     let state = _getTweenState(target); | ||||
|  | ||||
|     if (!state.destroyedId) { | ||||
|         if (target instanceof Clutter.Actor) { | ||||
|             state.actor = target; | ||||
|             state.destroyedId = target.connect('destroy', _actorDestroyed); | ||||
|         } else if (target.actor && target.actor instanceof Clutter.Actor) { | ||||
|             state.actor = target.actor; | ||||
|             state.destroyedId = target.actor.connect('destroy', () => _actorDestroyed(target)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let { time, delay } = tweeningParameters; | ||||
|     if (!isNaN(time)) | ||||
|         tweeningParameters['time'] = adjustAnimationTime(1000 * time) / 1000; | ||||
|     if (!isNaN(delay)) | ||||
|         tweeningParameters['delay'] = adjustAnimationTime(1000 * delay) / 1000; | ||||
|  | ||||
|     _addHandler(target, tweeningParameters, 'onComplete', _tweenCompleted); | ||||
| } | ||||
|  | ||||
| function _getTweenState(target) { | ||||
|     // If we were paranoid, we could keep a plist mapping targets to | ||||
|     // states... but we're not that paranoid. | ||||
|     if (!target.__ShellTweenerState) | ||||
|         target.__ShellTweenerState = {}; | ||||
|     return target.__ShellTweenerState; | ||||
| } | ||||
|  | ||||
| function _resetTweenState(target) { | ||||
|     let state = target.__ShellTweenerState; | ||||
|  | ||||
|     if (state) { | ||||
|         if (state.destroyedId) | ||||
|             state.actor.disconnect(state.destroyedId); | ||||
|     } | ||||
|  | ||||
|     target.__ShellTweenerState = {}; | ||||
| } | ||||
|  | ||||
| function _addHandler(target, params, name, handler) { | ||||
|     if (params[name]) { | ||||
|         let oldHandler = params[name]; | ||||
|         let oldScope = params[`${name}Scope`]; | ||||
|         let oldParams = params[`${name}Params`]; | ||||
|         let eventScope = oldScope ? oldScope : target; | ||||
|  | ||||
|         params[name] = () => { | ||||
|             oldHandler.apply(eventScope, oldParams); | ||||
|             handler(target); | ||||
|         }; | ||||
|     } else { | ||||
|         params[name] = () => handler(target); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function _actorDestroyed(target) { | ||||
|     _resetTweenState(target); | ||||
|     Tweener.removeTweens(target); | ||||
| } | ||||
|  | ||||
| function _tweenCompleted(target) { | ||||
|     if (!isTweening(target)) | ||||
|         _resetTweenState(target); | ||||
| } | ||||
|  | ||||
| function getTweenCount(scope) { | ||||
|     return Tweener.getTweenCount(scope); | ||||
| } | ||||
|  | ||||
| // imports.tweener.tweener doesn't provide this method (which exists | ||||
| // in the ActionScript version) but it's easy to implement. | ||||
| function isTweening(scope) { | ||||
|     return Tweener.getTweenCount(scope) != 0; | ||||
| } | ||||
|  | ||||
| function removeTweens(...args) { | ||||
|     if (Tweener.removeTweens(args)) { | ||||
|         let [scope] = args; | ||||
|         // If we just removed the last active tween, clean up | ||||
|         if (Tweener.getTweenCount(scope) == 0) | ||||
|             _tweenCompleted(scope); | ||||
|         return true; | ||||
|     } else { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| function pauseTweens(...args) { | ||||
|     return Tweener.pauseTweens(...args); | ||||
| } | ||||
|  | ||||
| function resumeTweens(...args) { | ||||
|     return Tweener.resumeTweens(...args); | ||||
| } | ||||
|  | ||||
|  | ||||
| function registerSpecialProperty(...args) { | ||||
|     Tweener.registerSpecialProperty(...args); | ||||
| } | ||||
|  | ||||
| function registerSpecialPropertyModifier(name, modifyFunction, getFunction) { | ||||
|     Tweener.registerSpecialPropertyModifier(name, modifyFunction, getFunction); | ||||
| } | ||||
|  | ||||
| function registerSpecialPropertySplitter(name, splitFunction, parameters) { | ||||
|     Tweener.registerSpecialPropertySplitter(name, splitFunction, parameters); | ||||
| } | ||||
|  | ||||
|  | ||||
| // The 'FrameTicker' object is an object used to feed new frames to | ||||
| // Tweener so it can update values and redraw. The default frame | ||||
| // ticker for Tweener just uses a simple timeout at a fixed frame rate | ||||
| // and has no idea of "catching up" by dropping frames. | ||||
| // | ||||
| // We substitute it with custom frame ticker here that connects | ||||
| // Tweener to a Clutter.TimeLine. Now, Clutter.Timeline itself isn't a | ||||
| // whole lot more sophisticated than a simple timeout at a fixed frame | ||||
| // rate, but at least it knows how to drop frames. (See | ||||
| // HippoAnimationManager for a more sophisticated view of continuous | ||||
| // time updates; even better is to pay attention to the vertical | ||||
| // vblank and sync to that when possible.) | ||||
| // | ||||
| var ClutterFrameTicker = class { | ||||
|     constructor() { | ||||
|         // We don't have a finite duration; tweener will tell us to stop | ||||
|         // when we need to stop, so use 1000 seconds as "infinity", and | ||||
|         // set the timeline to loop. Doing this means we have to track | ||||
|         // time ourselves, since clutter timeline's time will cycle | ||||
|         // instead of strictly increase. | ||||
|         this._timeline = new Clutter.Timeline({ duration: 1000 * 1000 }); | ||||
|         this._timeline.set_loop(true); | ||||
|         this._startTime = -1; | ||||
|         this._currentTime = -1; | ||||
|  | ||||
|         this._timeline.connect('new-frame', () => { | ||||
|             this._onNewFrame(); | ||||
|         }); | ||||
|  | ||||
|         let perfLog = Shell.PerfLog.get_default(); | ||||
|         perfLog.define_event("tweener.framePrepareStart", | ||||
|                              "Start of a new animation frame", | ||||
|                              ""); | ||||
|         perfLog.define_event("tweener.framePrepareDone", | ||||
|                              "Finished preparing frame", | ||||
|                              ""); | ||||
|     } | ||||
|  | ||||
|     get FRAME_RATE() { | ||||
|         return 60; | ||||
|     } | ||||
|  | ||||
|     _onNewFrame() { | ||||
|         // If there is a lot of setup to start the animation, then | ||||
|         // first frame number we get from clutter might be a long ways | ||||
|         // into the animation (or the animation might even be done). | ||||
|         // That looks bad, so we always start at the first frame of the | ||||
|         // animation then only do frame dropping from there. | ||||
|         if (this._startTime < 0) | ||||
|             this._startTime = GLib.get_monotonic_time() / 1000.0; | ||||
|  | ||||
|         // currentTime is in milliseconds | ||||
|         let perfLog = Shell.PerfLog.get_default(); | ||||
|         this._currentTime = GLib.get_monotonic_time() / 1000.0 - this._startTime; | ||||
|         perfLog.event("tweener.framePrepareStart"); | ||||
|         this.emit('prepare-frame'); | ||||
|         perfLog.event("tweener.framePrepareDone"); | ||||
|     } | ||||
|  | ||||
|     getTime() { | ||||
|         return this._currentTime; | ||||
|     } | ||||
|  | ||||
|     start() { | ||||
|         this._timeline.start(); | ||||
|         global.begin_work(); | ||||
|     } | ||||
|  | ||||
|     stop() { | ||||
|         this._timeline.stop(); | ||||
|         this._startTime = -1; | ||||
|         this._currentTime = -1; | ||||
|         global.end_work(); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(ClutterFrameTicker.prototype); | ||||
| @@ -605,7 +605,7 @@ var UnlockDialog = GObject.registerClass({ | ||||
|         this._showPrompt(); | ||||
|  | ||||
|         if (GLib.unichar_isgraph(unichar)) | ||||
|             this.addCharacter(unichar); | ||||
|             this._authPrompt.addCharacter(unichar); | ||||
|  | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
| @@ -835,11 +835,6 @@ var UnlockDialog = GObject.registerClass({ | ||||
|             this._authPrompt.cancel(); | ||||
|     } | ||||
|  | ||||
|     addCharacter(unichar) { | ||||
|         this._showPrompt(); | ||||
|         this._authPrompt.addCharacter(unichar); | ||||
|     } | ||||
|  | ||||
|     finish(onComplete) { | ||||
|         this._ensureAuthPrompt(); | ||||
|         this._authPrompt.finish(onComplete); | ||||
|   | ||||
| @@ -82,8 +82,10 @@ class DisplayChangeDialog extends ModalDialog.ModalDialog { | ||||
|     } | ||||
|  | ||||
|     _formatCountDown() { | ||||
|         let fmt = ngettext("Settings changes will revert in %d second", | ||||
|                            "Settings changes will revert in %d seconds"); | ||||
|         const fmt = ngettext( | ||||
|             'Settings changes will revert in %d second', | ||||
|             'Settings changes will revert in %d seconds', | ||||
|             this._countDown); | ||||
|         return fmt.format(this._countDown); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const { Clutter, Meta } = imports.gi; | ||||
| const { Clutter } = imports.gi; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const DND = imports.ui.dnd; | ||||
| @@ -17,7 +17,7 @@ var XdndHandler = class { | ||||
|         Main.uiGroup.add_actor(this._dummy); | ||||
|         this._dummy.hide(); | ||||
|  | ||||
|         var dnd = Meta.get_backend().get_dnd(); | ||||
|         var dnd = global.backend.get_dnd(); | ||||
|         dnd.connect('dnd-enter', this._onEnter.bind(this)); | ||||
|         dnd.connect('dnd-position-change', this._onPositionChanged.bind(this)); | ||||
|         dnd.connect('dnd-leave', this._onLeave.bind(this)); | ||||
|   | ||||
| @@ -218,12 +218,12 @@ globals: | ||||
|   ARGV: readonly | ||||
|   Debugger: readonly | ||||
|   GIRepositoryGType: readonly | ||||
|   globalThis: readonly | ||||
|   imports: readonly | ||||
|   Intl: readonly | ||||
|   log: readonly | ||||
|   logError: readonly | ||||
|   print: readonly | ||||
|   printerr: readonly | ||||
|   window: readonly | ||||
| parserOptions: | ||||
|   ecmaVersion: 2019 | ||||
|   | ||||
							
								
								
									
										28
									
								
								meson.build
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								meson.build
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| project('gnome-shell', 'c', | ||||
|   version: '3.37.0', | ||||
|   meson_version: '>= 0.47.0', | ||||
|   meson_version: '>= 0.53.0', | ||||
|   license: 'GPLv2+' | ||||
| ) | ||||
|  | ||||
| @@ -19,11 +19,11 @@ cogl_pango_pc = 'mutter-cogl-pango-' + mutter_api_version | ||||
| libmutter_pc = 'libmutter-' + mutter_api_version | ||||
|  | ||||
| ecal_req = '>= 3.33.1' | ||||
| eds_req = '>= 3.17.2' | ||||
| eds_req = '>= 3.33.1' | ||||
| gcr_req = '>= 3.7.5' | ||||
| gio_req = '>= 2.56.0' | ||||
| gi_req = '>= 1.49.1' | ||||
| gjs_req = '>= 1.63.2' | ||||
| gjs_req = '>= 1.65.1' | ||||
| gtk_req = '>= 3.15.0' | ||||
| mutter_req = '>= 3.36.0' | ||||
| polkit_req = '>= 0.100' | ||||
| @@ -63,16 +63,9 @@ portaldir = join_paths(datadir, 'xdg-desktop-portal', 'portals') | ||||
| schemadir = join_paths(datadir, 'glib-2.0', 'schemas') | ||||
| servicedir = join_paths(datadir, 'dbus-1', 'services') | ||||
|  | ||||
| # XXX: Once https://github.com/systemd/systemd/issues/9595 is fixed and we can | ||||
| # depend on this version, replace with something like: | ||||
| # systemduserunitdir = systemd_dep.get_pkgconfig_variable('systemduserunitdir', | ||||
| #                                                         define_variable: ['prefix', prefix]) | ||||
| # and uncomment systemd_dep below | ||||
| systemduserunitdir = join_paths(prefix, 'lib', 'systemd', 'user') | ||||
|  | ||||
| keybindings_dep = dependency('gnome-keybindings', required: false) | ||||
| if keybindings_dep.found() | ||||
|   keysdir = keybindings_dep.get_pkgconfig_variable('keysdir') | ||||
|   keysdir = keybindings_dep.get_pkgconfig_variable('keysdir', define_variable: ['datadir', datadir]) | ||||
| else | ||||
|   keysdir = join_paths(datadir, 'gnome-control-center', 'keybindings') | ||||
| endif | ||||
| @@ -122,8 +115,9 @@ endif | ||||
|  | ||||
| if get_option('systemd') | ||||
|   libsystemd_dep = dependency('libsystemd') | ||||
|   # XXX: see systemduserunitdir | ||||
|   # systemd_dep = dependency('systemd') | ||||
|   systemd_dep = dependency('systemd') | ||||
|   systemduserunitdir = systemd_dep.get_pkgconfig_variable('systemduserunitdir', | ||||
|                                                           define_variable: ['prefix', prefix]) | ||||
|   have_systemd = true | ||||
| else | ||||
|   libsystemd_dep = [] | ||||
| @@ -321,8 +315,6 @@ if get_option('man') | ||||
|   summary_dirs += { 'mandir': get_option('mandir') } | ||||
| endif | ||||
|  | ||||
| if meson.version().version_compare('>= 0.53.0') | ||||
|   summary(summary_dirs, section: 'Directories') | ||||
|   summary(summary_build, section: 'Build Configuration') | ||||
|   summary(summary_options, section: 'Build Options') | ||||
| endif | ||||
| summary(summary_dirs, section: 'Directories') | ||||
| summary(summary_build, section: 'Build Configuration') | ||||
| summary(summary_options, section: 'Build Options') | ||||
|   | ||||
| @@ -94,5 +94,7 @@ subprojects/extensions-tool/src/command-prefs.c | ||||
| subprojects/extensions-tool/src/command-reset.c | ||||
| subprojects/extensions-tool/src/command-uninstall.c | ||||
| subprojects/extensions-tool/src/main.c | ||||
| subprojects/extensions-tool/src/templates/00-plain.desktop.in | ||||
| subprojects/extensions-tool/src/templates/indicator.desktop.in | ||||
| # Please do not remove this file from POTFILES.in. Run "git submodule init && git submodule update" to get it. | ||||
| subprojects/gvc/gvc-mixer-control.c | ||||
|   | ||||
							
								
								
									
										1
									
								
								po/POTFILES.skip
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								po/POTFILES.skip
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| subprojects/extensions-tool/src/templates/indicator/extension.js | ||||
							
								
								
									
										35
									
								
								po/fi.po
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								po/fi.po
									
									
									
									
									
								
							| @@ -25,8 +25,8 @@ msgid "" | ||||
| msgstr "" | ||||
| "Project-Id-Version: gnome-shell\n" | ||||
| "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n" | ||||
| "POT-Creation-Date: 2020-03-31 07:15+0000\n" | ||||
| "PO-Revision-Date: 2020-04-02 12:43+0300\n" | ||||
| "POT-Creation-Date: 2020-04-02 09:45+0000\n" | ||||
| "PO-Revision-Date: 2020-04-13 18:02+0300\n" | ||||
| "Last-Translator: Jiri Grönroos <jiri.gronroos+l10n@iki.fi>\n" | ||||
| "Language-Team: suomi <lokalisointi-lista@googlegroups.com>\n" | ||||
| "Language: fi\n" | ||||
| @@ -736,44 +736,44 @@ msgstr "Estä pääsy" | ||||
| msgid "Grant Access" | ||||
| msgstr "Salli pääsy" | ||||
|  | ||||
| #: js/ui/appDisplay.js:932 | ||||
| #: js/ui/appDisplay.js:937 | ||||
| msgid "Unnamed Folder" | ||||
| msgstr "Nimetön kansio" | ||||
|  | ||||
| #: js/ui/appDisplay.js:955 | ||||
| #: js/ui/appDisplay.js:960 | ||||
| msgid "Frequently used applications will appear here" | ||||
| msgstr "Usein käytetyt sovellukset ilmestyvät tänne" | ||||
|  | ||||
| #: js/ui/appDisplay.js:1090 | ||||
| #: js/ui/appDisplay.js:1095 | ||||
| msgid "Frequent" | ||||
| msgstr "Käytetyimmät" | ||||
|  | ||||
| #: js/ui/appDisplay.js:1097 | ||||
| #: js/ui/appDisplay.js:1102 | ||||
| msgid "All" | ||||
| msgstr "Kaikki" | ||||
|  | ||||
| #. Translators: This is the heading of a list of open windows | ||||
| #: js/ui/appDisplay.js:2473 js/ui/panel.js:75 | ||||
| #: js/ui/appDisplay.js:2478 js/ui/panel.js:75 | ||||
| msgid "Open Windows" | ||||
| msgstr "Avoimet ikkunat" | ||||
|  | ||||
| #: js/ui/appDisplay.js:2493 js/ui/panel.js:82 | ||||
| #: js/ui/appDisplay.js:2498 js/ui/panel.js:82 | ||||
| msgid "New Window" | ||||
| msgstr "Uusi ikkuna" | ||||
|  | ||||
| #: js/ui/appDisplay.js:2504 | ||||
| #: js/ui/appDisplay.js:2509 | ||||
| msgid "Launch using Dedicated Graphics Card" | ||||
| msgstr "Käynnistä erillisnäytönohjainta käyttäen" | ||||
|  | ||||
| #: js/ui/appDisplay.js:2532 js/ui/dash.js:239 | ||||
| #: js/ui/appDisplay.js:2537 js/ui/dash.js:239 | ||||
| msgid "Remove from Favorites" | ||||
| msgstr "Poista suosikeista" | ||||
|  | ||||
| #: js/ui/appDisplay.js:2538 | ||||
| #: js/ui/appDisplay.js:2543 | ||||
| msgid "Add to Favorites" | ||||
| msgstr "Lisää suosikkeihin" | ||||
|  | ||||
| #: js/ui/appDisplay.js:2548 js/ui/panel.js:93 | ||||
| #: js/ui/appDisplay.js:2553 js/ui/panel.js:93 | ||||
| msgid "Show Details" | ||||
| msgstr "Näytä tiedot" | ||||
|  | ||||
| @@ -1472,11 +1472,11 @@ msgstr "Näytä lähde" | ||||
| msgid "Web Page" | ||||
| msgstr "Verkkosivusto" | ||||
|  | ||||
| #: js/ui/main.js:277 | ||||
| #: js/ui/main.js:279 | ||||
| msgid "Logged in as a privileged user" | ||||
| msgstr "Kirjautuneena etuoikeutettuna käyttäjänä" | ||||
|  | ||||
| #: js/ui/main.js:278 | ||||
| #: js/ui/main.js:280 | ||||
| msgid "" | ||||
| "Running a session as a privileged user should be avoided for security " | ||||
| "reasons. If possible, you should log in as a normal user." | ||||
| @@ -1484,11 +1484,11 @@ msgstr "" | ||||
| "Istunnon suorittamista etuoikeutettuna käyttäjänä tulisi välttää " | ||||
| "tietoturvasyistä. Jos mahdollista, kirjaudu tavallisena käyttäjänä." | ||||
|  | ||||
| #: js/ui/main.js:317 | ||||
| #: js/ui/main.js:319 | ||||
| msgid "Screen Lock disabled" | ||||
| msgstr "Näytön lukitus pois käytöstä" | ||||
|  | ||||
| #: js/ui/main.js:318 | ||||
| #: js/ui/main.js:320 | ||||
| msgid "Screen Locking requires the GNOME display manager." | ||||
| msgstr "Näytön lukitus vaatii Gnomen kirjautumishallinnan." | ||||
|  | ||||
| @@ -2407,6 +2407,9 @@ msgid "" | ||||
| "GNOME Extensions handles updating extensions, configuring extension " | ||||
| "preferences and removing or disabling unwanted extensions." | ||||
| msgstr "" | ||||
| "Gnomen laajennussovellus Laajennukset käsittelee laajennusten päivitykset, " | ||||
| "niiden asetukset ja sen avulla voi poistaa laajennuksia käytöstä tai " | ||||
| "kokonaan järjestelmästä." | ||||
|  | ||||
| #: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7 | ||||
| msgid "Configure GNOME Shell Extensions" | ||||
|   | ||||
							
								
								
									
										423
									
								
								po/pt_BR.po
									
									
									
									
									
								
							
							
						
						
									
										423
									
								
								po/pt_BR.po
									
									
									
									
									
								
							| @@ -24,8 +24,8 @@ msgid "" | ||||
| msgstr "" | ||||
| "Project-Id-Version: gnome-shell\n" | ||||
| "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n" | ||||
| "POT-Creation-Date: 2020-03-19 14:34+0000\n" | ||||
| "PO-Revision-Date: 2020-03-19 11:36-0300\n" | ||||
| "POT-Creation-Date: 2020-03-31 07:15+0000\n" | ||||
| "PO-Revision-Date: 2020-04-22 09:30-0300\n" | ||||
| "Last-Translator: Rafael Fontenelle <rafaelff@gnome.org>\n" | ||||
| "Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n" | ||||
| "Language: pt_BR\n" | ||||
| @@ -413,66 +413,12 @@ msgstr "Atrasar foco altera o modo do mouse até o ponteiro parar de mover" | ||||
| msgid "Network Login" | ||||
| msgstr "Sessão de Rede" | ||||
|  | ||||
| #: js/extensionPrefs/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5 | ||||
| #: js/extensionPrefs/data/org.gnome.Extensions.desktop.in.in:4 | ||||
| #: js/extensionPrefs/js/main.js:242 | ||||
| #: js/extensionPrefs/data/ui/extensions-window.ui:61 | ||||
| msgid "Extensions" | ||||
| msgstr "Extensões" | ||||
|  | ||||
| #: js/extensionPrefs/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6 | ||||
| #: js/extensionPrefs/js/main.js:243 | ||||
| msgid "Manage your GNOME Extensions" | ||||
| msgstr "Gerenciar suas extensões do GNOME" | ||||
|  | ||||
| #: js/extensionPrefs/data/metainfo/org.gnome.Extensions.metainfo.xml.in:35 | ||||
| msgid "" | ||||
| "GNOME Extensions handles updating extensions, configuring extension " | ||||
| "preferences and removing or disabling unwanted extensions." | ||||
| msgstr "" | ||||
| "GNOME Extensões lida com a atualização da extensões, configuração das " | ||||
| "preferências de extensões e remoção ou desabilitação de extensões " | ||||
| "indesejadas." | ||||
|  | ||||
| #: js/extensionPrefs/data/org.gnome.Extensions.desktop.in.in:7 | ||||
| msgid "Configure GNOME Shell Extensions" | ||||
| msgstr "Configurar extensões do Shell do GNOME" | ||||
|  | ||||
| #: js/extensionPrefs/js/main.js:164 | ||||
| #, javascript-format | ||||
| msgid "Remove “%s”?" | ||||
| msgstr "Remover “%s”?" | ||||
|  | ||||
| #: js/extensionPrefs/js/main.js:165 | ||||
| msgid "" | ||||
| "If you remove the extension, you need to return to download it if you want " | ||||
| "to enable it again" | ||||
| msgstr "" | ||||
| "Se você remover a extensão, você precisa voltar a baixá-la se você quiser " | ||||
| "habilitá-la novamente" | ||||
|  | ||||
| #: js/extensionPrefs/js/main.js:168 js/gdm/authPrompt.js:135 | ||||
| #: js/ui/audioDeviceSelection.js:57 js/ui/components/networkAgent.js:109 | ||||
| #: js/ui/components/polkitAgent.js:139 js/ui/endSessionDialog.js:374 | ||||
| #: js/ui/extensionDownloader.js:177 js/ui/shellMountOperation.js:376 | ||||
| #: js/ui/shellMountOperation.js:386 js/ui/status/network.js:913 | ||||
| msgid "Cancel" | ||||
| msgstr "Cancelar" | ||||
|  | ||||
| #: js/extensionPrefs/js/main.js:169 | ||||
| msgid "Remove" | ||||
| msgstr "Remover" | ||||
|  | ||||
| #: js/extensionPrefs/js/main.js:241 | ||||
| msgid "translator-credits" | ||||
| msgstr "Rafael Fontenelle <rafaelff@gnome.org>" | ||||
|  | ||||
| #: js/extensionPrefs/js/main.js:285 | ||||
| #: js/extensionPrefs/data/ui/extensions-window.ui:223 | ||||
| #: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:36 | ||||
| #: subprojects/extensions-app/data/ui/extensions-window.ui:223 | ||||
| msgid "Something’s gone wrong" | ||||
| msgstr "Algo deu errado" | ||||
|  | ||||
| #: js/extensionPrefs/js/main.js:292 | ||||
| #: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:48 | ||||
| msgid "" | ||||
| "We’re very sorry, but there’s been a problem: the settings for this " | ||||
| "extension can’t be displayed. We recommend that you report the issue to the " | ||||
| @@ -482,105 +428,25 @@ msgstr "" | ||||
| "ser exibidas. Recomendamos que você relate o problema aos autores da " | ||||
| "extensão." | ||||
|  | ||||
| #: js/extensionPrefs/js/main.js:299 | ||||
| #: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:82 | ||||
| msgid "Technical Details" | ||||
| msgstr "Detalhes técnicos" | ||||
|  | ||||
| #: js/extensionPrefs/js/main.js:334 | ||||
| msgid "Copy Error" | ||||
| msgstr "Copiar erro" | ||||
|  | ||||
| #: js/extensionPrefs/js/main.js:361 | ||||
| #: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:165 | ||||
| msgid "Homepage" | ||||
| msgstr "Site" | ||||
|  | ||||
| #: js/extensionPrefs/js/main.js:362 | ||||
| #: js/dbusServices/extensions/ui/extension-prefs-dialog.ui:166 | ||||
| msgid "Visit extension homepage" | ||||
| msgstr "Visita a página web da extensão" | ||||
|  | ||||
| #: js/extensionPrefs/js/main.js:479 | ||||
| #, javascript-format | ||||
| msgid "%d extension will be updated on next login." | ||||
| msgid_plural "%d extensions will be updated on next login." | ||||
| msgstr[0] "%d extensão será atualizada na próxima sessão." | ||||
| msgstr[1] "%d extensões serão atualizadas na próxima sessão." | ||||
|  | ||||
| #: js/extensionPrefs/data/ui/extension-row.ui:100 | ||||
| #: subprojects/extensions-tool/src/command-create.c:211 | ||||
| #: subprojects/extensions-tool/src/main.c:173 | ||||
| msgid "Description" | ||||
| msgstr "Descrição" | ||||
|  | ||||
| #: js/extensionPrefs/data/ui/extension-row.ui:123 | ||||
| #: subprojects/extensions-tool/src/main.c:185 | ||||
| msgid "Version" | ||||
| msgstr "Versão" | ||||
|  | ||||
| #: js/extensionPrefs/data/ui/extension-row.ui:151 | ||||
| msgid "Author" | ||||
| msgstr "Autor" | ||||
|  | ||||
| #: js/extensionPrefs/data/ui/extension-row.ui:175 | ||||
| msgid "Website" | ||||
| msgstr "Site" | ||||
|  | ||||
| #: js/extensionPrefs/data/ui/extension-row.ui:192 | ||||
| msgid "Remove…" | ||||
| msgstr "Remover…" | ||||
|  | ||||
| #: js/extensionPrefs/data/ui/extensions-window.ui:8 | ||||
| msgid "Help" | ||||
| msgstr "Ajuda" | ||||
|  | ||||
| #: js/extensionPrefs/data/ui/extensions-window.ui:12 | ||||
| msgid "About Extensions" | ||||
| msgstr "Sobre as Extensões" | ||||
|  | ||||
| #: js/extensionPrefs/data/ui/extensions-window.ui:27 | ||||
| msgid "" | ||||
| "To find and add extensions, visit <a href=\"https://extensions.gnome.org" | ||||
| "\">extensions.gnome.org</a>." | ||||
| msgstr "" | ||||
| "Para encontrar e adicionar extensões, visite <a href=\"https://extensions." | ||||
| "gnome.org\">extensions.gnome.org</a>." | ||||
|  | ||||
| #: js/extensionPrefs/data/ui/extensions-window.ui:35 | ||||
| msgid "Warning" | ||||
| msgstr "Aviso" | ||||
|  | ||||
| #: js/extensionPrefs/data/ui/extensions-window.ui:46 | ||||
| msgid "" | ||||
| "Extensions can cause system issues, including performance problems. If you " | ||||
| "encounter problems with your system, it is recommended to disable all " | ||||
| "extensions." | ||||
| msgstr "" | ||||
| "Extensões podem causar problemas no sistema, incluindo problemas de " | ||||
| "desempenho. Se você encontrar problemas com o seu sistema, é recomendável " | ||||
| "desativar todas as extensões." | ||||
|  | ||||
| #: js/extensionPrefs/data/ui/extensions-window.ui:134 | ||||
| msgid "Manually Installed" | ||||
| msgstr "Instalada manualmente" | ||||
|  | ||||
| #: js/extensionPrefs/data/ui/extensions-window.ui:158 | ||||
| msgid "Built-In" | ||||
| msgstr "Interna" | ||||
|  | ||||
| #: js/extensionPrefs/data/ui/extensions-window.ui:199 | ||||
| msgid "No Installed Extensions" | ||||
| msgstr "Nenhuma extensão instalada" | ||||
|  | ||||
| #: js/extensionPrefs/data/ui/extensions-window.ui:235 | ||||
| msgid "" | ||||
| "We’re very sorry, but it was not possible to get the list of installed " | ||||
| "extensions. Make sure you are logged into GNOME and try again." | ||||
| msgstr "" | ||||
| "Sentimos muito, mas não foi possível obter a lista de extensões instaladas. " | ||||
| "Certifique-se de estar em uma sessão do GNOME e tente novamente." | ||||
|  | ||||
| #: js/extensionPrefs/data/ui/extensions-window.ui:288 | ||||
| msgid "Log Out…" | ||||
| msgstr "Encerrar sessão…" | ||||
| #: js/gdm/authPrompt.js:135 js/ui/audioDeviceSelection.js:57 | ||||
| #: js/ui/components/networkAgent.js:109 js/ui/components/polkitAgent.js:139 | ||||
| #: js/ui/endSessionDialog.js:374 js/ui/extensionDownloader.js:181 | ||||
| #: js/ui/shellMountOperation.js:376 js/ui/shellMountOperation.js:386 | ||||
| #: js/ui/status/network.js:913 subprojects/extensions-app/js/main.js:148 | ||||
| msgid "Cancel" | ||||
| msgstr "Cancelar" | ||||
|  | ||||
| #. Cisco LEAP | ||||
| #: js/gdm/authPrompt.js:237 js/ui/components/networkAgent.js:204 | ||||
| @@ -862,44 +728,44 @@ msgstr "Negar acesso" | ||||
| msgid "Grant Access" | ||||
| msgstr "Conceder acesso" | ||||
|  | ||||
| #: js/ui/appDisplay.js:898 | ||||
| #: js/ui/appDisplay.js:932 | ||||
| msgid "Unnamed Folder" | ||||
| msgstr "Pasta sem nome" | ||||
|  | ||||
| #: js/ui/appDisplay.js:921 | ||||
| #: js/ui/appDisplay.js:955 | ||||
| msgid "Frequently used applications will appear here" | ||||
| msgstr "Aplicativos usados frequentemente vão aparecer aqui" | ||||
|  | ||||
| #: js/ui/appDisplay.js:1056 | ||||
| #: js/ui/appDisplay.js:1090 | ||||
| msgid "Frequent" | ||||
| msgstr "Frequente" | ||||
|  | ||||
| #: js/ui/appDisplay.js:1063 | ||||
| #: js/ui/appDisplay.js:1097 | ||||
| msgid "All" | ||||
| msgstr "Todos" | ||||
|  | ||||
| #. Translators: This is the heading of a list of open windows | ||||
| #: js/ui/appDisplay.js:2446 js/ui/panel.js:75 | ||||
| #: js/ui/appDisplay.js:2473 js/ui/panel.js:75 | ||||
| msgid "Open Windows" | ||||
| msgstr "Janelas abertas" | ||||
|  | ||||
| #: js/ui/appDisplay.js:2466 js/ui/panel.js:82 | ||||
| #: js/ui/appDisplay.js:2493 js/ui/panel.js:82 | ||||
| msgid "New Window" | ||||
| msgstr "Nova janela" | ||||
|  | ||||
| #: js/ui/appDisplay.js:2477 | ||||
| #: js/ui/appDisplay.js:2504 | ||||
| msgid "Launch using Dedicated Graphics Card" | ||||
| msgstr "Inicia usando placa de vídeo dedicada" | ||||
|  | ||||
| #: js/ui/appDisplay.js:2505 js/ui/dash.js:239 | ||||
| #: js/ui/appDisplay.js:2532 js/ui/dash.js:239 | ||||
| msgid "Remove from Favorites" | ||||
| msgstr "Remover dos favoritos" | ||||
|  | ||||
| #: js/ui/appDisplay.js:2511 | ||||
| #: js/ui/appDisplay.js:2538 | ||||
| msgid "Add to Favorites" | ||||
| msgstr "Adicionar aos favoritos" | ||||
|  | ||||
| #: js/ui/appDisplay.js:2521 js/ui/panel.js:93 | ||||
| #: js/ui/appDisplay.js:2548 js/ui/panel.js:93 | ||||
| msgid "Show Details" | ||||
| msgstr "Mostrar detalhes" | ||||
|  | ||||
| @@ -929,7 +795,7 @@ msgstr "Fones de ouvido" | ||||
| msgid "Headset" | ||||
| msgstr "Fone de ouvido com microfone" | ||||
|  | ||||
| #: js/ui/audioDeviceSelection.js:68 js/ui/status/volume.js:269 | ||||
| #: js/ui/audioDeviceSelection.js:68 js/ui/status/volume.js:270 | ||||
| msgid "Microphone" | ||||
| msgstr "Microfone" | ||||
|  | ||||
| @@ -1070,7 +936,7 @@ msgstr "Nenhum evento" | ||||
| msgid "Do Not Disturb" | ||||
| msgstr "Não perturbe" | ||||
|  | ||||
| #: js/ui/calendar.js:1171 | ||||
| #: js/ui/calendar.js:1176 | ||||
| msgid "Clear" | ||||
| msgstr "Limpar" | ||||
|  | ||||
| @@ -1220,7 +1086,7 @@ msgstr "Desculpe, isto não funcionou. Por favor, tente novamente." | ||||
|  | ||||
| #. Translators: this is the other person changing their old IM name to their new | ||||
| #. IM name. | ||||
| #: js/ui/components/telepathyClient.js:787 | ||||
| #: js/ui/components/telepathyClient.js:823 | ||||
| #, javascript-format | ||||
| msgid "%s is now known as %s" | ||||
| msgstr "%s agora é conhecido como %s" | ||||
| @@ -1264,106 +1130,106 @@ msgstr "Adicionar relógios mundiais…" | ||||
| msgid "World Clocks" | ||||
| msgstr "Relógios mundiais" | ||||
|  | ||||
| #: js/ui/dateMenu.js:279 | ||||
| #: js/ui/dateMenu.js:289 | ||||
| msgid "Weather" | ||||
| msgstr "Meteorologia" | ||||
|  | ||||
| #: js/ui/dateMenu.js:394 | ||||
| #: js/ui/dateMenu.js:418 | ||||
| msgid "Select a location…" | ||||
| msgstr "Selecione uma localização…" | ||||
|  | ||||
| #: js/ui/dateMenu.js:407 | ||||
| #: js/ui/dateMenu.js:426 | ||||
| msgid "Loading…" | ||||
| msgstr "Carregando…" | ||||
|  | ||||
| #: js/ui/dateMenu.js:417 | ||||
| #: js/ui/dateMenu.js:436 | ||||
| msgid "Go online for weather information" | ||||
| msgstr "Conecte-se à internet para obter as informações meteorológicas" | ||||
|  | ||||
| #: js/ui/dateMenu.js:419 | ||||
| #: js/ui/dateMenu.js:438 | ||||
| msgid "Weather information is currently unavailable" | ||||
| msgstr "No momento as informações meteorológicas não estão disponíveis" | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:37 | ||||
| #: js/ui/endSessionDialog.js:39 | ||||
| #, javascript-format | ||||
| msgctxt "title" | ||||
| msgid "Log Out %s" | ||||
| msgstr "Encerrar sessão de %s" | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:38 | ||||
| #: js/ui/endSessionDialog.js:40 | ||||
| msgctxt "title" | ||||
| msgid "Log Out" | ||||
| msgstr "Encerrar sessão" | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:40 | ||||
| #: js/ui/endSessionDialog.js:42 | ||||
| #, javascript-format | ||||
| msgid "%s will be logged out automatically in %d second." | ||||
| msgid_plural "%s will be logged out automatically in %d seconds." | ||||
| msgstr[0] "%s encerrará a sessão automaticamente em %d segundo." | ||||
| msgstr[1] "%s encerrará a sessão automaticamente em %d segundos." | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:45 | ||||
| #: js/ui/endSessionDialog.js:47 | ||||
| #, javascript-format | ||||
| msgid "You will be logged out automatically in %d second." | ||||
| msgid_plural "You will be logged out automatically in %d seconds." | ||||
| msgstr[0] "Sua sessão será encerrada automaticamente em %d segundo." | ||||
| msgstr[1] "Sua sessão será encerrada automaticamente em %d segundos." | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:51 | ||||
| #: js/ui/endSessionDialog.js:53 | ||||
| msgctxt "button" | ||||
| msgid "Log Out" | ||||
| msgstr "Encerrar sessão" | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:56 | ||||
| #: js/ui/endSessionDialog.js:58 | ||||
| msgctxt "title" | ||||
| msgid "Power Off" | ||||
| msgstr "Desligar" | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:57 | ||||
| #: js/ui/endSessionDialog.js:59 | ||||
| msgctxt "title" | ||||
| msgid "Install Updates & Power Off" | ||||
| msgstr "Instalar atualizações & desligar" | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:59 | ||||
| #: js/ui/endSessionDialog.js:61 | ||||
| #, javascript-format | ||||
| msgid "The system will power off automatically in %d second." | ||||
| msgid_plural "The system will power off automatically in %d seconds." | ||||
| msgstr[0] "O sistema será desligado automaticamente em %d segundo." | ||||
| msgstr[1] "O sistema será desligado automaticamente em %d segundos." | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:63 | ||||
| #: js/ui/endSessionDialog.js:65 | ||||
| msgctxt "checkbox" | ||||
| msgid "Install pending software updates" | ||||
| msgstr "Instalar atualizações de software pendentes" | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:66 js/ui/endSessionDialog.js:82 | ||||
| #: js/ui/endSessionDialog.js:68 js/ui/endSessionDialog.js:84 | ||||
| msgctxt "button" | ||||
| msgid "Restart" | ||||
| msgstr "Reiniciar" | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:68 | ||||
| #: js/ui/endSessionDialog.js:70 | ||||
| msgctxt "button" | ||||
| msgid "Power Off" | ||||
| msgstr "Desligar" | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:74 | ||||
| #: js/ui/endSessionDialog.js:76 | ||||
| msgctxt "title" | ||||
| msgid "Restart" | ||||
| msgstr "Reiniciar" | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:76 | ||||
| #: js/ui/endSessionDialog.js:78 | ||||
| #, javascript-format | ||||
| msgid "The system will restart automatically in %d second." | ||||
| msgid_plural "The system will restart automatically in %d seconds." | ||||
| msgstr[0] "O sistema irá reiniciar automaticamente em %d segundo." | ||||
| msgstr[1] "O sistema irá reiniciar automaticamente em %d segundos." | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:89 | ||||
| #: js/ui/endSessionDialog.js:91 | ||||
| msgctxt "title" | ||||
| msgid "Restart & Install Updates" | ||||
| msgstr "Reiniciar & instalar atualizações" | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:91 | ||||
| #: js/ui/endSessionDialog.js:93 | ||||
| #, javascript-format | ||||
| msgid "The system will automatically restart and install updates in %d second." | ||||
| msgid_plural "" | ||||
| @@ -1375,22 +1241,22 @@ msgstr[1] "" | ||||
| "O sistema irá reiniciar e instalar atualizações automaticamente em %d " | ||||
| "segundos." | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:97 js/ui/endSessionDialog.js:116 | ||||
| #: js/ui/endSessionDialog.js:99 js/ui/endSessionDialog.js:118 | ||||
| msgctxt "button" | ||||
| msgid "Restart & Install" | ||||
| msgstr "Reiniciar & instalar" | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:98 | ||||
| #: js/ui/endSessionDialog.js:100 | ||||
| msgctxt "button" | ||||
| msgid "Install & Power Off" | ||||
| msgstr "Instalar & desligar" | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:99 | ||||
| #: js/ui/endSessionDialog.js:101 | ||||
| msgctxt "checkbox" | ||||
| msgid "Power off after updates are installed" | ||||
| msgstr "Desligar após atualizações serem instaladas" | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:106 | ||||
| #: js/ui/endSessionDialog.js:108 | ||||
| msgctxt "title" | ||||
| msgid "Restart & Install Upgrade" | ||||
| msgstr "Reiniciar & instalar atualizações" | ||||
| @@ -1398,7 +1264,7 @@ msgstr "Reiniciar & instalar atualizações" | ||||
| #. Translators: This is the text displayed for system upgrades in the | ||||
| #. shut down dialog. First %s gets replaced with the distro name and | ||||
| #. second %s with the distro version to upgrade to | ||||
| #: js/ui/endSessionDialog.js:111 | ||||
| #: js/ui/endSessionDialog.js:113 | ||||
| #, javascript-format | ||||
| msgid "" | ||||
| "%s %s will be installed after restart. Upgrade installation can take a long " | ||||
| @@ -1408,16 +1274,16 @@ msgstr "" | ||||
| "pode levar um longo tempo: certifique-se de que fez cópia de segurança (back " | ||||
| "up) e que o computador esteja ligado na tomada." | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:259 | ||||
| #: js/ui/endSessionDialog.js:261 | ||||
| msgid "Running on battery power: Please plug in before installing updates." | ||||
| msgstr "" | ||||
| "Funcionando na bateria: conecte na tomada antes de instalar atualizações." | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:268 | ||||
| #: js/ui/endSessionDialog.js:270 | ||||
| msgid "Some applications are busy or have unsaved work" | ||||
| msgstr "Alguns aplicativos estão ocupados ou possuem trabalhos não salvos" | ||||
|  | ||||
| #: js/ui/endSessionDialog.js:273 | ||||
| #: js/ui/endSessionDialog.js:275 | ||||
| msgid "Other users are logged in" | ||||
| msgstr "Outros usuários estão com sessão aberta" | ||||
|  | ||||
| @@ -1433,24 +1299,24 @@ msgstr "%s (remoto)" | ||||
| msgid "%s (console)" | ||||
| msgstr "%s (console)" | ||||
|  | ||||
| #: js/ui/extensionDownloader.js:181 | ||||
| #: js/ui/extensionDownloader.js:185 | ||||
| msgid "Install" | ||||
| msgstr "Instalar" | ||||
|  | ||||
| #: js/ui/extensionDownloader.js:187 | ||||
| #: js/ui/extensionDownloader.js:191 | ||||
| msgid "Install Extension" | ||||
| msgstr "Instalar extensão" | ||||
|  | ||||
| #: js/ui/extensionDownloader.js:188 | ||||
| #: js/ui/extensionDownloader.js:192 | ||||
| #, javascript-format | ||||
| msgid "Download and install “%s” from extensions.gnome.org?" | ||||
| msgstr "Baixar e instalar “%s” de extensions.gnome.org?" | ||||
|  | ||||
| #: js/ui/extensionSystem.js:228 | ||||
| #: js/ui/extensionSystem.js:233 | ||||
| msgid "Extension Updates Available" | ||||
| msgstr "Atualizações de extensões disponíveis" | ||||
|  | ||||
| #: js/ui/extensionSystem.js:229 | ||||
| #: js/ui/extensionSystem.js:234 | ||||
| msgid "Extension updates are ready to be installed." | ||||
| msgstr "Atualizações de extensões estão prontas para serem instaladas." | ||||
|  | ||||
| @@ -1599,11 +1465,11 @@ msgstr "Ver fonte" | ||||
| msgid "Web Page" | ||||
| msgstr "Página web" | ||||
|  | ||||
| #: js/ui/main.js:274 | ||||
| #: js/ui/main.js:277 | ||||
| msgid "Logged in as a privileged user" | ||||
| msgstr "Sessão aberta como um usuário privilegiado" | ||||
|  | ||||
| #: js/ui/main.js:275 | ||||
| #: js/ui/main.js:278 | ||||
| msgid "" | ||||
| "Running a session as a privileged user should be avoided for security " | ||||
| "reasons. If possible, you should log in as a normal user." | ||||
| @@ -1611,15 +1477,15 @@ msgstr "" | ||||
| "Usar uma sessão como um usuário privilegiado deve ser evitado por motivos de " | ||||
| "segurança. Se possível, você deve abrir uma sessão como um usuário normal." | ||||
|  | ||||
| #: js/ui/main.js:281 | ||||
| #: js/ui/main.js:317 | ||||
| msgid "Screen Lock disabled" | ||||
| msgstr "Bloqueio de tela desabilitado" | ||||
|  | ||||
| #: js/ui/main.js:282 | ||||
| #: js/ui/main.js:318 | ||||
| msgid "Screen Locking requires the GNOME display manager." | ||||
| msgstr "O bloqueio de tela requer o gerenciador de exibição do GNOME." | ||||
|  | ||||
| #: js/ui/messageTray.js:1554 | ||||
| #: js/ui/messageTray.js:1551 | ||||
| msgid "System Information" | ||||
| msgstr "Informações do sistema" | ||||
|  | ||||
| @@ -1826,13 +1692,13 @@ msgid "The PIM must be a number or empty." | ||||
| msgstr "O PIM deve ser um número ou vazio." | ||||
|  | ||||
| #. Translators: %s is the Disks application | ||||
| #: js/ui/shellMountOperation.js:469 | ||||
| #: js/ui/shellMountOperation.js:465 | ||||
| #, javascript-format | ||||
| msgid "Unable to start %s" | ||||
| msgstr "Não foi possível iniciar o %s" | ||||
|  | ||||
| #. Translators: %s is the Disks application | ||||
| #: js/ui/shellMountOperation.js:471 | ||||
| #: js/ui/shellMountOperation.js:467 | ||||
| #, javascript-format | ||||
| msgid "Couldn’t find the %s application" | ||||
| msgstr "Não foi possível localizar o aplicativo %s" | ||||
| @@ -1985,13 +1851,13 @@ msgstr "<desconhecido>" | ||||
| #: js/ui/status/network.js:420 js/ui/status/network.js:1317 | ||||
| #, javascript-format | ||||
| msgid "%s Off" | ||||
| msgstr "%s desligado" | ||||
| msgstr "%s desligada" | ||||
|  | ||||
| #. Translators: %s is a network identifier | ||||
| #: js/ui/status/network.js:423 | ||||
| #, javascript-format | ||||
| msgid "%s Connected" | ||||
| msgstr "Conectado a %s" | ||||
| msgstr "Conectado à %s" | ||||
|  | ||||
| # Não gerenciável para transmitir a ideia que o Networkmanager não consegue gerenciar o dispositivo --Enrico | ||||
| #. Translators: this is for network devices that are physically present but are not | ||||
| @@ -2012,7 +1878,7 @@ msgstr "Desconectando de %s" | ||||
| #: js/ui/status/network.js:438 js/ui/status/network.js:1309 | ||||
| #, javascript-format | ||||
| msgid "%s Connecting" | ||||
| msgstr "Conectando a %s" | ||||
| msgstr "Conectando à %s" | ||||
|  | ||||
| #. Translators: this is for network connections that require some kind of key or password; %s is a network identifier | ||||
| #: js/ui/status/network.js:441 | ||||
| @@ -2309,11 +2175,11 @@ msgstr "Erro de autorização de thunderbolt" | ||||
| msgid "Could not authorize the Thunderbolt device: %s" | ||||
| msgstr "Não foi possível autorizar o dispositivo Thunderbolt: %s" | ||||
|  | ||||
| #: js/ui/status/volume.js:150 | ||||
| #: js/ui/status/volume.js:151 | ||||
| msgid "Volume changed" | ||||
| msgstr "Volume alterado" | ||||
|  | ||||
| #: js/ui/status/volume.js:221 | ||||
| #: js/ui/status/volume.js:222 | ||||
| msgid "Volume" | ||||
| msgstr "Volume" | ||||
|  | ||||
| @@ -2347,23 +2213,23 @@ msgstr "Interna apenas" | ||||
|  | ||||
| #. Translators: This is a time format for a date in | ||||
| #. long format | ||||
| #: js/ui/unlockDialog.js:370 | ||||
| #: js/ui/unlockDialog.js:371 | ||||
| msgid "%A %B %-d" | ||||
| msgstr "%A, %-d de %B" | ||||
|  | ||||
| #: js/ui/unlockDialog.js:376 | ||||
| #: js/ui/unlockDialog.js:377 | ||||
| msgid "Swipe up to unlock" | ||||
| msgstr "Deslize para desbloquear" | ||||
|  | ||||
| #: js/ui/unlockDialog.js:377 | ||||
| #: js/ui/unlockDialog.js:378 | ||||
| msgid "Click or press a key to unlock" | ||||
| msgstr "Clique ou pressione uma tecla para desbloquear" | ||||
|  | ||||
| #: js/ui/unlockDialog.js:549 | ||||
| #: js/ui/unlockDialog.js:550 | ||||
| msgid "Unlock Window" | ||||
| msgstr "Desbloquear janela" | ||||
|  | ||||
| #: js/ui/unlockDialog.js:558 | ||||
| #: js/ui/unlockDialog.js:559 | ||||
| msgid "Log in as another user" | ||||
| msgstr "Iniciar sessão como outro usuário" | ||||
|  | ||||
| @@ -2523,6 +2389,136 @@ msgstr "A senha não pode estar em branco" | ||||
| msgid "Authentication dialog was dismissed by the user" | ||||
| msgstr "O diálogo de autenticação foi descartado pelo usuário" | ||||
|  | ||||
| #: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:5 | ||||
| #: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:4 | ||||
| #: subprojects/extensions-app/js/main.js:182 | ||||
| #: subprojects/extensions-app/data/ui/extensions-window.ui:61 | ||||
| msgid "Extensions" | ||||
| msgstr "Extensões" | ||||
|  | ||||
| #: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:6 | ||||
| #: subprojects/extensions-app/js/main.js:183 | ||||
| msgid "Manage your GNOME Extensions" | ||||
| msgstr "Gerenciar suas extensões do GNOME" | ||||
|  | ||||
| #: subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in:35 | ||||
| msgid "" | ||||
| "GNOME Extensions handles updating extensions, configuring extension " | ||||
| "preferences and removing or disabling unwanted extensions." | ||||
| msgstr "" | ||||
| "GNOME Extensões lida com a atualização da extensões, configuração das " | ||||
| "preferências de extensões e remoção ou desabilitação de extensões " | ||||
| "indesejadas." | ||||
|  | ||||
| #: subprojects/extensions-app/data/org.gnome.Extensions.desktop.in.in:7 | ||||
| msgid "Configure GNOME Shell Extensions" | ||||
| msgstr "Configurar extensões do Shell do GNOME" | ||||
|  | ||||
| #: subprojects/extensions-app/js/main.js:144 | ||||
| #, javascript-format | ||||
| msgid "Remove “%s”?" | ||||
| msgstr "Remover “%s”?" | ||||
|  | ||||
| #: subprojects/extensions-app/js/main.js:145 | ||||
| msgid "" | ||||
| "If you remove the extension, you need to return to download it if you want " | ||||
| "to enable it again" | ||||
| msgstr "" | ||||
| "Se você remover a extensão, você precisa voltar a baixá-la se você quiser " | ||||
| "habilitá-la novamente" | ||||
|  | ||||
| #: subprojects/extensions-app/js/main.js:149 | ||||
| msgid "Remove" | ||||
| msgstr "Remover" | ||||
|  | ||||
| #: subprojects/extensions-app/js/main.js:181 | ||||
| msgid "translator-credits" | ||||
| msgstr "Rafael Fontenelle <rafaelff@gnome.org>" | ||||
|  | ||||
| #: subprojects/extensions-app/js/main.js:316 | ||||
| #, javascript-format | ||||
| msgid "%d extension will be updated on next login." | ||||
| msgid_plural "%d extensions will be updated on next login." | ||||
| msgstr[0] "%d extensão será atualizada na próxima sessão." | ||||
| msgstr[1] "%d extensões serão atualizadas na próxima sessão." | ||||
|  | ||||
| #: subprojects/extensions-app/data/ui/extension-row.ui:100 | ||||
| #: subprojects/extensions-tool/src/command-create.c:211 | ||||
| #: subprojects/extensions-tool/src/main.c:173 | ||||
| msgid "Description" | ||||
| msgstr "Descrição" | ||||
|  | ||||
| #: subprojects/extensions-app/data/ui/extension-row.ui:123 | ||||
| #: subprojects/extensions-tool/src/main.c:185 | ||||
| msgid "Version" | ||||
| msgstr "Versão" | ||||
|  | ||||
| #: subprojects/extensions-app/data/ui/extension-row.ui:151 | ||||
| msgid "Author" | ||||
| msgstr "Autor" | ||||
|  | ||||
| #: subprojects/extensions-app/data/ui/extension-row.ui:175 | ||||
| msgid "Website" | ||||
| msgstr "Site" | ||||
|  | ||||
| #: subprojects/extensions-app/data/ui/extension-row.ui:192 | ||||
| msgid "Remove…" | ||||
| msgstr "Remover…" | ||||
|  | ||||
| #: subprojects/extensions-app/data/ui/extensions-window.ui:8 | ||||
| msgid "Help" | ||||
| msgstr "Ajuda" | ||||
|  | ||||
| #: subprojects/extensions-app/data/ui/extensions-window.ui:12 | ||||
| msgid "About Extensions" | ||||
| msgstr "Sobre as Extensões" | ||||
|  | ||||
| #: subprojects/extensions-app/data/ui/extensions-window.ui:27 | ||||
| msgid "" | ||||
| "To find and add extensions, visit <a href=\"https://extensions.gnome.org" | ||||
| "\">extensions.gnome.org</a>." | ||||
| msgstr "" | ||||
| "Para encontrar e adicionar extensões, visite <a href=\"https://extensions." | ||||
| "gnome.org\">extensions.gnome.org</a>." | ||||
|  | ||||
| #: subprojects/extensions-app/data/ui/extensions-window.ui:35 | ||||
| msgid "Warning" | ||||
| msgstr "Aviso" | ||||
|  | ||||
| #: subprojects/extensions-app/data/ui/extensions-window.ui:46 | ||||
| msgid "" | ||||
| "Extensions can cause system issues, including performance problems. If you " | ||||
| "encounter problems with your system, it is recommended to disable all " | ||||
| "extensions." | ||||
| msgstr "" | ||||
| "Extensões podem causar problemas no sistema, incluindo problemas de " | ||||
| "desempenho. Se você encontrar problemas com o seu sistema, é recomendável " | ||||
| "desativar todas as extensões." | ||||
|  | ||||
| #: subprojects/extensions-app/data/ui/extensions-window.ui:134 | ||||
| msgid "Manually Installed" | ||||
| msgstr "Instalada manualmente" | ||||
|  | ||||
| #: subprojects/extensions-app/data/ui/extensions-window.ui:158 | ||||
| msgid "Built-In" | ||||
| msgstr "Interna" | ||||
|  | ||||
| #: subprojects/extensions-app/data/ui/extensions-window.ui:199 | ||||
| msgid "No Installed Extensions" | ||||
| msgstr "Nenhuma extensão instalada" | ||||
|  | ||||
| #: subprojects/extensions-app/data/ui/extensions-window.ui:235 | ||||
| msgid "" | ||||
| "We’re very sorry, but it was not possible to get the list of installed " | ||||
| "extensions. Make sure you are logged into GNOME and try again." | ||||
| msgstr "" | ||||
| "Sentimos muito, mas não foi possível obter a lista de extensões instaladas. " | ||||
| "Certifique-se de estar em uma sessão do GNOME e tente novamente." | ||||
|  | ||||
| #: subprojects/extensions-app/data/ui/extensions-window.ui:288 | ||||
| msgid "Log Out…" | ||||
| msgstr "Encerrar sessão…" | ||||
|  | ||||
| #. Translators: a file path to an extension directory | ||||
| #: subprojects/extensions-tool/src/command-create.c:125 | ||||
| #, c-format | ||||
| @@ -2860,6 +2856,9 @@ msgstr[1] "%u entradas" | ||||
| msgid "System Sounds" | ||||
| msgstr "Sons do sistema" | ||||
|  | ||||
| #~ msgid "Copy Error" | ||||
| #~ msgstr "Copiar erro" | ||||
|  | ||||
| #~| msgid "Username: " | ||||
| #~ msgid "Username…" | ||||
| #~ msgstr "Nome de usuário…" | ||||
|   | ||||
							
								
								
									
										1935
									
								
								po/zh_CN.po
									
									
									
									
									
								
							
							
						
						
									
										1935
									
								
								po/zh_CN.po
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -45,199 +45,155 @@ struct _ClientData | ||||
|   gulong backend_died_id; | ||||
| }; | ||||
|  | ||||
| struct _CalendarSourceData | ||||
| { | ||||
|   ECalClientSourceType source_type; | ||||
|   CalendarSources *sources; | ||||
|   guint            changed_signal; | ||||
|  | ||||
|   /* ESource -> EClient */ | ||||
|   GHashTable      *clients; | ||||
|  | ||||
|   guint            timeout_id; | ||||
|  | ||||
|   guint            loaded : 1; | ||||
| }; | ||||
|  | ||||
| typedef struct _CalendarSourcesPrivate CalendarSourcesPrivate; | ||||
|  | ||||
| struct _CalendarSources | ||||
| { | ||||
|   GObject                 parent; | ||||
|   CalendarSourcesPrivate *priv; | ||||
|  | ||||
|   ESourceRegistryWatcher *registry_watcher; | ||||
|   gulong                  filter_id; | ||||
|   gulong                  appeared_id; | ||||
|   gulong                  disappeared_id; | ||||
|  | ||||
|   GMutex                  clients_lock; | ||||
|   GHashTable             *clients; /* ESource -> ClientData */ | ||||
| }; | ||||
|  | ||||
| struct _CalendarSourcesPrivate | ||||
| { | ||||
|   ESourceRegistry    *registry; | ||||
|   gulong              source_added_id; | ||||
|   gulong              source_changed_id; | ||||
|   gulong              source_removed_id; | ||||
|  | ||||
|   CalendarSourceData  appointment_sources; | ||||
|   CalendarSourceData  task_sources; | ||||
| }; | ||||
|  | ||||
| G_DEFINE_TYPE_WITH_PRIVATE (CalendarSources, calendar_sources, G_TYPE_OBJECT) | ||||
|  | ||||
| static void calendar_sources_finalize   (GObject             *object); | ||||
|  | ||||
| static void backend_died_cb (EClient *client, CalendarSourceData *source_data); | ||||
| static void calendar_sources_registry_source_changed_cb (ESourceRegistry *registry, | ||||
|                                                          ESource         *source, | ||||
|                                                          CalendarSources *sources); | ||||
| static void calendar_sources_registry_source_removed_cb (ESourceRegistry *registry, | ||||
|                                                          ESource         *source, | ||||
|                                                          CalendarSources *sources); | ||||
| G_DEFINE_TYPE (CalendarSources, calendar_sources, G_TYPE_OBJECT) | ||||
|  | ||||
| enum | ||||
| { | ||||
|   APPOINTMENT_SOURCES_CHANGED, | ||||
|   TASK_SOURCES_CHANGED, | ||||
|   CLIENT_APPEARED, | ||||
|   CLIENT_DISAPPEARED, | ||||
|   LAST_SIGNAL | ||||
| }; | ||||
| static guint signals [LAST_SIGNAL] = { 0, }; | ||||
|  | ||||
| static GObjectClass    *parent_class = NULL; | ||||
| static CalendarSources *calendar_sources_singleton = NULL; | ||||
| static void | ||||
| calendar_sources_client_connected_cb (GObject *source_object, | ||||
|                                       GAsyncResult *result, | ||||
|                                       gpointer user_data) | ||||
| { | ||||
|   CalendarSources *sources = CALENDAR_SOURCES (source_object); | ||||
|   ESource *source = user_data; | ||||
|   EClient *client; | ||||
|   g_autoptr (GError) error = NULL; | ||||
|  | ||||
|   /* The calendar_sources_connect_client_sync() already stored the 'client' | ||||
|    * into the sources->clients */ | ||||
|   client = calendar_sources_connect_client_finish (sources, result, &error); | ||||
|   if (error) | ||||
|     { | ||||
|       g_warning ("Could not load source '%s': %s", | ||||
|                  e_source_get_uid (source), | ||||
|                  error->message); | ||||
|     } | ||||
|    else | ||||
|     { | ||||
|       g_signal_emit (sources, signals[CLIENT_APPEARED], 0, client, NULL); | ||||
|     } | ||||
|  | ||||
|   g_clear_object (&client); | ||||
|   g_clear_object (&source); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| registry_watcher_filter_cb (ESourceRegistryWatcher *watcher, | ||||
|                             ESource *source, | ||||
|                             CalendarSources *sources) | ||||
| { | ||||
|   return e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR) && | ||||
|          e_source_selectable_get_selected (e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR)); | ||||
| } | ||||
|  | ||||
| static void | ||||
| registry_watcher_source_appeared_cb (ESourceRegistryWatcher *watcher, | ||||
|                                      ESource *source, | ||||
|                                      CalendarSources *sources) | ||||
| { | ||||
|   ECalClientSourceType source_type; | ||||
|  | ||||
|   if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) | ||||
|     source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS; | ||||
|   else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST)) | ||||
|     source_type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS; | ||||
|   else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) | ||||
|     source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS; | ||||
|   else | ||||
|     g_return_if_reached (); | ||||
|  | ||||
|   calendar_sources_connect_client (sources, source, source_type, 30, NULL, calendar_sources_client_connected_cb, g_object_ref (source)); | ||||
| } | ||||
|  | ||||
| static void | ||||
| registry_watcher_source_disappeared_cb (ESourceRegistryWatcher *watcher, | ||||
|                                         ESource *source, | ||||
|                                         CalendarSources *sources) | ||||
| { | ||||
|   gboolean emit; | ||||
|  | ||||
|   g_mutex_lock (&sources->clients_lock); | ||||
|  | ||||
|   emit = g_hash_table_remove (sources->clients, source); | ||||
|  | ||||
|   g_mutex_unlock (&sources->clients_lock); | ||||
|  | ||||
|   if (emit) | ||||
|     g_signal_emit (sources, signals[CLIENT_DISAPPEARED], 0, e_source_get_uid (source), NULL); | ||||
| } | ||||
|  | ||||
| static void | ||||
| client_data_free (ClientData *data) | ||||
| { | ||||
|   g_clear_signal_handler (&data->backend_died_id, data->client); | ||||
|   g_signal_handler_disconnect (data->client, data->backend_died_id); | ||||
|   g_object_unref (data->client); | ||||
|   g_slice_free (ClientData, data); | ||||
| } | ||||
|  | ||||
| static void | ||||
| calendar_sources_class_init (CalendarSourcesClass *klass) | ||||
| { | ||||
|   GObjectClass *gobject_class = (GObjectClass *) klass; | ||||
|  | ||||
|   parent_class = g_type_class_peek_parent (klass); | ||||
|  | ||||
|   gobject_class->finalize = calendar_sources_finalize; | ||||
|  | ||||
|   signals [APPOINTMENT_SOURCES_CHANGED] = | ||||
|     g_signal_new ("appointment-sources-changed", | ||||
| 		  G_TYPE_FROM_CLASS (gobject_class), | ||||
| 		  G_SIGNAL_RUN_LAST, | ||||
| 		  0, | ||||
| 		  NULL, | ||||
| 		  NULL, | ||||
|                   NULL, | ||||
| 		  G_TYPE_NONE, | ||||
| 		  0); | ||||
|  | ||||
|   signals [TASK_SOURCES_CHANGED] = | ||||
|     g_signal_new ("task-sources-changed", | ||||
| 		  G_TYPE_FROM_CLASS (gobject_class), | ||||
| 		  G_SIGNAL_RUN_LAST, | ||||
| 		  0, | ||||
| 		  NULL, | ||||
| 		  NULL, | ||||
|                   NULL, | ||||
| 		  G_TYPE_NONE, | ||||
| 		  0); | ||||
| } | ||||
|  | ||||
| static void | ||||
| calendar_sources_init (CalendarSources *sources) | ||||
| calendar_sources_constructed (GObject *object) | ||||
| { | ||||
|   CalendarSources *sources = CALENDAR_SOURCES (object); | ||||
|   ESourceRegistry *registry = NULL; | ||||
|   GError *error = NULL; | ||||
|   GDBusConnection *session_bus; | ||||
|   GVariant *result; | ||||
|  | ||||
|   sources->priv = calendar_sources_get_instance_private (sources); | ||||
|  | ||||
|   /* WORKAROUND: the hardcoded timeout for e_source_registry_new_sync() | ||||
|      (and other library calls that eventually call g_dbus_proxy_new[_sync]()) | ||||
|      is 25 seconds. This has been shown to be too small for | ||||
|      evolution-source-registry in certain cases (slow disk, concurrent IO, | ||||
|      many configured sources), so we first ensure that the service | ||||
|      starts with a manual call and a higher timeout. | ||||
|  | ||||
|      HACK: every time the DBus API is bumped in e-d-s we need | ||||
|      to update this! | ||||
|   */ | ||||
|   session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); | ||||
|   if (session_bus == NULL) | ||||
|     { | ||||
|       g_error ("Failed to connect to the session bus: %s", error->message); | ||||
|     } | ||||
|  | ||||
|   result = g_dbus_connection_call_sync (session_bus, "org.freedesktop.DBus", | ||||
|                                         "/", "org.freedesktop.DBus", | ||||
|                                         "StartServiceByName", | ||||
|                                         g_variant_new ("(su)", | ||||
|                                                        "org.gnome.evolution.dataserver.Sources5", | ||||
|                                                        0), | ||||
|                                         NULL, | ||||
|                                         G_DBUS_CALL_FLAGS_NONE, | ||||
|                                         60 * 1000, | ||||
|                                         NULL, &error); | ||||
|   if (result != NULL) | ||||
|     { | ||||
|       g_variant_unref (result); | ||||
|       sources->priv->registry = e_source_registry_new_sync (NULL, &error); | ||||
|     } | ||||
|   G_OBJECT_CLASS (calendar_sources_parent_class)->constructed (object); | ||||
|  | ||||
|   registry = e_source_registry_new_sync (NULL, &error); | ||||
|   if (error != NULL) | ||||
|     { | ||||
|       /* Any error is fatal, but we don't want to crash gnome-shell-calendar-server | ||||
|          because of e-d-s problems. So just exit here. | ||||
|       */ | ||||
|       g_warning ("Failed to start evolution-source-registry: %s", error->message); | ||||
|       exit(EXIT_FAILURE); | ||||
|       exit (EXIT_FAILURE); | ||||
|     } | ||||
|  | ||||
|   g_object_unref (session_bus); | ||||
|   g_return_if_fail (registry != NULL); | ||||
|  | ||||
|   sources->priv->source_added_id   = g_signal_connect (sources->priv->registry, | ||||
|                                                        "source-added", | ||||
|                                                        G_CALLBACK (calendar_sources_registry_source_changed_cb), | ||||
|                                                        sources); | ||||
|   sources->priv->source_changed_id = g_signal_connect (sources->priv->registry, | ||||
|                                                        "source-changed", | ||||
|                                                        G_CALLBACK (calendar_sources_registry_source_changed_cb), | ||||
|                                                        sources); | ||||
|   sources->priv->source_removed_id = g_signal_connect (sources->priv->registry, | ||||
|                                                        "source-removed", | ||||
|                                                        G_CALLBACK (calendar_sources_registry_source_removed_cb), | ||||
|                                                        sources); | ||||
|   sources->registry_watcher = e_source_registry_watcher_new (registry, NULL); | ||||
|  | ||||
|   sources->priv->appointment_sources.source_type    = E_CAL_CLIENT_SOURCE_TYPE_EVENTS; | ||||
|   sources->priv->appointment_sources.sources        = sources; | ||||
|   sources->priv->appointment_sources.changed_signal = signals [APPOINTMENT_SOURCES_CHANGED]; | ||||
|   sources->priv->appointment_sources.clients        = g_hash_table_new_full ((GHashFunc) e_source_hash, | ||||
|                                                                              (GEqualFunc) e_source_equal, | ||||
|                                                                              (GDestroyNotify) g_object_unref, | ||||
|                                                                              (GDestroyNotify) client_data_free); | ||||
|   sources->priv->appointment_sources.timeout_id     = 0; | ||||
|   g_clear_object (®istry); | ||||
|  | ||||
|   sources->priv->task_sources.source_type    = E_CAL_CLIENT_SOURCE_TYPE_TASKS; | ||||
|   sources->priv->task_sources.sources        = sources; | ||||
|   sources->priv->task_sources.changed_signal = signals [TASK_SOURCES_CHANGED]; | ||||
|   sources->priv->task_sources.clients        = g_hash_table_new_full ((GHashFunc) e_source_hash, | ||||
|                                                                       (GEqualFunc) e_source_equal, | ||||
|                                                                       (GDestroyNotify) g_object_unref, | ||||
|                                                                       (GDestroyNotify) client_data_free); | ||||
|   sources->priv->task_sources.timeout_id     = 0; | ||||
| } | ||||
|   sources->clients = g_hash_table_new_full ((GHashFunc) e_source_hash, | ||||
|                                             (GEqualFunc) e_source_equal, | ||||
|                                             (GDestroyNotify) g_object_unref, | ||||
|                                             (GDestroyNotify) client_data_free); | ||||
|   sources->filter_id = g_signal_connect (sources->registry_watcher, | ||||
|                                          "filter", | ||||
|                                          G_CALLBACK (registry_watcher_filter_cb), | ||||
|                                          sources); | ||||
|   sources->appeared_id = g_signal_connect (sources->registry_watcher, | ||||
|                                            "appeared", | ||||
|                                            G_CALLBACK (registry_watcher_source_appeared_cb), | ||||
|                                            sources); | ||||
|   sources->disappeared_id = g_signal_connect (sources->registry_watcher, | ||||
|                                               "disappeared", | ||||
|                                               G_CALLBACK (registry_watcher_source_disappeared_cb), | ||||
|                                               sources); | ||||
|  | ||||
| static void | ||||
| calendar_sources_finalize_source_data (CalendarSources    *sources, | ||||
| 				       CalendarSourceData *source_data) | ||||
| { | ||||
|   if (source_data->loaded) | ||||
|     { | ||||
|       g_hash_table_destroy (source_data->clients); | ||||
|       source_data->clients = NULL; | ||||
|  | ||||
|       g_clear_handle_id (&source_data->timeout_id, g_source_remove); | ||||
|  | ||||
|       source_data->loaded = FALSE; | ||||
|     } | ||||
|   e_source_registry_watcher_reclaim (sources->registry_watcher); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -245,28 +201,67 @@ calendar_sources_finalize (GObject *object) | ||||
| { | ||||
|   CalendarSources *sources = CALENDAR_SOURCES (object); | ||||
|  | ||||
|   if (sources->priv->registry) | ||||
|   g_clear_pointer (&sources->clients, g_hash_table_destroy); | ||||
|  | ||||
|   if (sources->registry_watcher) | ||||
|     { | ||||
|       g_clear_signal_handler (&sources->priv->source_added_id, | ||||
|                               sources->priv->registry); | ||||
|       g_clear_signal_handler (&sources->priv->source_changed_id, | ||||
|                               sources->priv->registry); | ||||
|       g_clear_signal_handler (&sources->priv->source_removed_id, | ||||
|                               sources->priv->registry); | ||||
|       g_object_unref (sources->priv->registry); | ||||
|       g_signal_handler_disconnect (sources->registry_watcher, | ||||
|                                    sources->filter_id); | ||||
|       g_signal_handler_disconnect (sources->registry_watcher, | ||||
|                                    sources->appeared_id); | ||||
|       g_signal_handler_disconnect (sources->registry_watcher, | ||||
|                                    sources->disappeared_id); | ||||
|       g_clear_object (&sources->registry_watcher); | ||||
|     } | ||||
|   sources->priv->registry = NULL; | ||||
|  | ||||
|   calendar_sources_finalize_source_data (sources, &sources->priv->appointment_sources); | ||||
|   calendar_sources_finalize_source_data (sources, &sources->priv->task_sources); | ||||
|   g_mutex_clear (&sources->clients_lock); | ||||
|  | ||||
|   if (G_OBJECT_CLASS (parent_class)->finalize) | ||||
|     G_OBJECT_CLASS (parent_class)->finalize (object); | ||||
|   G_OBJECT_CLASS (calendar_sources_parent_class)->finalize (object); | ||||
| } | ||||
|  | ||||
| static void | ||||
| calendar_sources_class_init (CalendarSourcesClass *klass) | ||||
| { | ||||
|   GObjectClass *gobject_class = (GObjectClass *) klass; | ||||
|  | ||||
|   gobject_class->constructed = calendar_sources_constructed; | ||||
|   gobject_class->finalize = calendar_sources_finalize; | ||||
|  | ||||
|   signals [CLIENT_APPEARED] = | ||||
|     g_signal_new ("client-appeared", | ||||
|                   G_TYPE_FROM_CLASS (gobject_class), | ||||
|                   G_SIGNAL_RUN_LAST, | ||||
|                   0, | ||||
|                   NULL, | ||||
|                   NULL, | ||||
|                   NULL, | ||||
|                   G_TYPE_NONE, | ||||
|                   1, | ||||
|                   E_TYPE_CAL_CLIENT); | ||||
|  | ||||
|   signals [CLIENT_DISAPPEARED] = | ||||
|     g_signal_new ("client-disappeared", | ||||
|                   G_TYPE_FROM_CLASS (gobject_class), | ||||
|                   G_SIGNAL_RUN_LAST, | ||||
|                   0, | ||||
|                   NULL, | ||||
|                   NULL, | ||||
|                   NULL, | ||||
|                   G_TYPE_NONE, | ||||
|                   1, | ||||
|                   G_TYPE_STRING); /* ESource::uid of the disappeared client */ | ||||
| } | ||||
|  | ||||
| static void | ||||
| calendar_sources_init (CalendarSources *sources) | ||||
| { | ||||
|   g_mutex_init (&sources->clients_lock); | ||||
| } | ||||
|  | ||||
| CalendarSources * | ||||
| calendar_sources_get (void) | ||||
| { | ||||
|   static CalendarSources *calendar_sources_singleton = NULL; | ||||
|   gpointer singleton_location = &calendar_sources_singleton; | ||||
|  | ||||
|   if (calendar_sources_singleton) | ||||
| @@ -274,85 +269,70 @@ calendar_sources_get (void) | ||||
|  | ||||
|   calendar_sources_singleton = g_object_new (CALENDAR_TYPE_SOURCES, NULL); | ||||
|   g_object_add_weak_pointer (G_OBJECT (calendar_sources_singleton), | ||||
| 			     singleton_location); | ||||
|                              singleton_location); | ||||
|  | ||||
|   return calendar_sources_singleton; | ||||
| } | ||||
|  | ||||
| /* The clients are just created here but not loaded */ | ||||
| static void | ||||
| create_client_for_source (ESource              *source, | ||||
| 		          ECalClientSourceType  source_type, | ||||
| 		          CalendarSourceData   *source_data) | ||||
| ESourceRegistry * | ||||
| calendar_sources_get_registry (CalendarSources *sources) | ||||
| { | ||||
|   ClientData *data; | ||||
|   EClient *client; | ||||
|   GError *error = NULL; | ||||
|  | ||||
|   client = g_hash_table_lookup (source_data->clients, source); | ||||
|   g_return_if_fail (client == NULL); | ||||
|  | ||||
|   client = e_cal_client_connect_sync (source, source_type, -1, NULL, &error); | ||||
|   if (!client) | ||||
|     { | ||||
|       g_warning ("Could not load source '%s': %s", | ||||
| 		 e_source_get_uid (source), | ||||
| 		 error->message); | ||||
|       g_clear_error(&error); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|   data = g_slice_new0 (ClientData); | ||||
|   data->client = E_CAL_CLIENT (client);  /* takes ownership */ | ||||
|   data->backend_died_id = g_signal_connect (client, | ||||
|                                             "backend-died", | ||||
|                                             G_CALLBACK (backend_died_cb), | ||||
|                                             source_data); | ||||
|  | ||||
|   g_hash_table_insert (source_data->clients, g_object_ref (source), data); | ||||
| } | ||||
|  | ||||
| static inline void | ||||
| debug_dump_ecal_list (GHashTable *clients) | ||||
| { | ||||
| #ifdef CALENDAR_ENABLE_DEBUG | ||||
|   GList *list, *link; | ||||
|  | ||||
|   dprintf ("Loaded clients:\n"); | ||||
|   list = g_hash_table_get_keys (clients); | ||||
|   for (link = list; link != NULL; link = g_list_next (link)) | ||||
|     { | ||||
|       ESource *source = E_SOURCE (link->data); | ||||
|  | ||||
|       dprintf ("  %s %s\n", | ||||
| 	       e_source_get_uid (source), | ||||
| 	       e_source_get_display_name (source)); | ||||
|     } | ||||
|   g_list_free (list); | ||||
| #endif | ||||
|   return e_source_registry_watcher_get_registry (sources->registry_watcher); | ||||
| } | ||||
|  | ||||
| static void | ||||
| calendar_sources_load_esource_list (ESourceRegistry *registry, | ||||
|                                     CalendarSourceData *source_data); | ||||
|  | ||||
| static gboolean | ||||
| backend_restart (gpointer data) | ||||
| gather_event_clients_cb (gpointer key, | ||||
|                          gpointer value, | ||||
|                          gpointer user_data) | ||||
| { | ||||
|   CalendarSourceData *source_data = data; | ||||
|   ESourceRegistry *registry; | ||||
|   GSList **plist = user_data; | ||||
|   ClientData *cd = value; | ||||
|  | ||||
|   registry = source_data->sources->priv->registry; | ||||
|   calendar_sources_load_esource_list (registry, source_data); | ||||
|   g_signal_emit (source_data->sources, source_data->changed_signal, 0); | ||||
|   if (cd) | ||||
|     *plist = g_slist_prepend (*plist, g_object_ref (cd->client)); | ||||
| } | ||||
|  | ||||
|   source_data->timeout_id = 0; | ||||
|      | ||||
|   return FALSE; | ||||
| GSList * | ||||
| calendar_sources_ref_clients (CalendarSources *sources) | ||||
| { | ||||
|   GSList *list = NULL; | ||||
|  | ||||
|   g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL); | ||||
|  | ||||
|   g_mutex_lock (&sources->clients_lock); | ||||
|   g_hash_table_foreach (sources->clients, gather_event_clients_cb, &list); | ||||
|   g_mutex_unlock (&sources->clients_lock); | ||||
|  | ||||
|   return list; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| calendar_sources_has_clients (CalendarSources *sources) | ||||
| { | ||||
|   GHashTableIter iter; | ||||
|   gpointer value; | ||||
|   gboolean has = FALSE; | ||||
|  | ||||
|   g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), FALSE); | ||||
|  | ||||
|   g_mutex_lock (&sources->clients_lock); | ||||
|  | ||||
|   g_hash_table_iter_init (&iter, sources->clients); | ||||
|   while (!has && g_hash_table_iter_next (&iter, NULL, &value)) | ||||
|    { | ||||
|      ClientData *cd = value; | ||||
|  | ||||
|      has = cd != NULL; | ||||
|    } | ||||
|  | ||||
|   g_mutex_unlock (&sources->clients_lock); | ||||
|  | ||||
|   return has; | ||||
| } | ||||
|  | ||||
| static void | ||||
| backend_died_cb (EClient *client, CalendarSourceData *source_data) | ||||
| backend_died_cb (EClient *client, | ||||
|                  CalendarSources *sources) | ||||
| { | ||||
|   ESource *source; | ||||
|   const char *display_name; | ||||
| @@ -360,196 +340,167 @@ backend_died_cb (EClient *client, CalendarSourceData *source_data) | ||||
|   source = e_client_get_source (client); | ||||
|   display_name = e_source_get_display_name (source); | ||||
|   g_warning ("The calendar backend for '%s' has crashed.", display_name); | ||||
|   g_hash_table_remove (source_data->clients, source); | ||||
|  | ||||
|   g_clear_handle_id (&source_data->timeout_id, g_source_remove); | ||||
|  | ||||
|   source_data->timeout_id = g_timeout_add_seconds (2, backend_restart, | ||||
| 		  				   source_data); | ||||
|   g_source_set_name_by_id (source_data->timeout_id, "[gnome-shell] backend_restart"); | ||||
|   g_mutex_lock (&sources->clients_lock); | ||||
|   g_hash_table_remove (sources->clients, source); | ||||
|   g_mutex_unlock (&sources->clients_lock); | ||||
| } | ||||
|  | ||||
| static void | ||||
| calendar_sources_load_esource_list (ESourceRegistry *registry, | ||||
|                                     CalendarSourceData *source_data) | ||||
| static EClient * | ||||
| calendar_sources_connect_client_sync (CalendarSources *sources, | ||||
|                                       ESource *source, | ||||
|                                       ECalClientSourceType source_type, | ||||
|                                       guint32 wait_for_connected_seconds, | ||||
|                                       GCancellable *cancellable, | ||||
|                                       GError **error) | ||||
| { | ||||
|   GList   *list, *link; | ||||
|   const gchar *extension_name; | ||||
|   EClient *client = NULL; | ||||
|   ClientData *client_data; | ||||
|  | ||||
|   switch (source_data->source_type) | ||||
|   g_mutex_lock (&sources->clients_lock); | ||||
|   client_data = g_hash_table_lookup (sources->clients, source); | ||||
|   if (client_data) | ||||
|      client = E_CLIENT (g_object_ref (client_data->client)); | ||||
|   g_mutex_unlock (&sources->clients_lock); | ||||
|  | ||||
|   if (client) | ||||
|     return client; | ||||
|  | ||||
|   client = e_cal_client_connect_sync (source, source_type, wait_for_connected_seconds, cancellable, error); | ||||
|   if (!client) | ||||
|     return NULL; | ||||
|  | ||||
|   g_mutex_lock (&sources->clients_lock); | ||||
|   client_data = g_hash_table_lookup (sources->clients, source); | ||||
|   if (client_data) | ||||
|     { | ||||
|       case E_CAL_CLIENT_SOURCE_TYPE_EVENTS: | ||||
|         extension_name = E_SOURCE_EXTENSION_CALENDAR; | ||||
|         break; | ||||
|       case E_CAL_CLIENT_SOURCE_TYPE_TASKS: | ||||
|         extension_name = E_SOURCE_EXTENSION_TASK_LIST; | ||||
|         break; | ||||
|       default: | ||||
|         g_return_if_reached (); | ||||
|       g_clear_object (&client); | ||||
|       client = E_CLIENT (g_object_ref (client_data->client)); | ||||
|     } | ||||
|  | ||||
|   list = e_source_registry_list_sources (registry, extension_name); | ||||
|  | ||||
|   for (link = list; link != NULL; link = g_list_next (link)) | ||||
|    else | ||||
|     { | ||||
|       ESource *source = E_SOURCE (link->data); | ||||
|       ESourceSelectable *extension; | ||||
|       gboolean show_source; | ||||
|       client_data = g_slice_new0 (ClientData); | ||||
|       client_data->client = E_CAL_CLIENT (g_object_ref (client)); | ||||
|       client_data->backend_died_id = g_signal_connect (client, | ||||
|                                                        "backend-died", | ||||
|                                                        G_CALLBACK (backend_died_cb), | ||||
|                                                        sources); | ||||
|  | ||||
|       extension = e_source_get_extension (source, extension_name); | ||||
|       show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension); | ||||
|  | ||||
|       if (show_source) | ||||
|         create_client_for_source (source, source_data->source_type, source_data); | ||||
|       g_hash_table_insert (sources->clients, g_object_ref (source), client_data); | ||||
|     } | ||||
|   g_mutex_unlock (&sources->clients_lock); | ||||
|  | ||||
|   debug_dump_ecal_list (source_data->clients); | ||||
|  | ||||
|   g_list_free_full (list, g_object_unref); | ||||
|   return client; | ||||
| } | ||||
|  | ||||
| typedef struct _AsyncContext { | ||||
|   ESource *source; | ||||
|   ECalClientSourceType source_type; | ||||
|   guint32 wait_for_connected_seconds; | ||||
| } AsyncContext; | ||||
|  | ||||
| static void | ||||
| calendar_sources_registry_source_changed_cb (ESourceRegistry *registry, | ||||
|                                              ESource         *source, | ||||
|                                              CalendarSources *sources) | ||||
| async_context_free (gpointer ptr) | ||||
| { | ||||
|   if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) | ||||
|   AsyncContext *ctx = ptr; | ||||
|  | ||||
|   if (ctx) | ||||
|     { | ||||
|       CalendarSourceData *source_data; | ||||
|       ESourceSelectable *extension; | ||||
|       gboolean have_client; | ||||
|       gboolean show_source; | ||||
|  | ||||
|       source_data = &sources->priv->appointment_sources; | ||||
|       extension = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR); | ||||
|       have_client = (g_hash_table_lookup (source_data->clients, source) != NULL); | ||||
|       show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension); | ||||
|  | ||||
|       if (!show_source && have_client) | ||||
|         { | ||||
|           g_hash_table_remove (source_data->clients, source); | ||||
|           g_signal_emit (sources, source_data->changed_signal, 0); | ||||
|         } | ||||
|       if (show_source && !have_client) | ||||
|         { | ||||
|           create_client_for_source (source, source_data->source_type, source_data); | ||||
|           g_signal_emit (sources, source_data->changed_signal, 0); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) | ||||
|     { | ||||
|       CalendarSourceData *source_data; | ||||
|       ESourceSelectable *extension; | ||||
|       gboolean have_client; | ||||
|       gboolean show_source; | ||||
|  | ||||
|       source_data = &sources->priv->task_sources; | ||||
|       extension = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST); | ||||
|       have_client = (g_hash_table_lookup (source_data->clients, source) != NULL); | ||||
|       show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension); | ||||
|  | ||||
|       if (!show_source && have_client) | ||||
|         { | ||||
|           g_hash_table_remove (source_data->clients, source); | ||||
|           g_signal_emit (sources, source_data->changed_signal, 0); | ||||
|         } | ||||
|       if (show_source && !have_client) | ||||
|         { | ||||
|           create_client_for_source (source, source_data->source_type, source_data); | ||||
|           g_signal_emit (sources, source_data->changed_signal, 0); | ||||
|         } | ||||
|       g_clear_object (&ctx->source); | ||||
|       g_slice_free (AsyncContext, ctx); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| calendar_sources_registry_source_removed_cb (ESourceRegistry *registry, | ||||
|                                              ESource         *source, | ||||
|                                              CalendarSources *sources) | ||||
| calendar_sources_connect_client_thread (GTask *task, | ||||
|                                         gpointer source_object, | ||||
|                                         gpointer task_data, | ||||
|                                         GCancellable *cancellable) | ||||
| { | ||||
|   if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) | ||||
|   CalendarSources *sources = source_object; | ||||
|   AsyncContext *ctx = task_data; | ||||
|   EClient *client; | ||||
|   GError *local_error = NULL; | ||||
|  | ||||
|   client = calendar_sources_connect_client_sync (sources, ctx->source, ctx->source_type, | ||||
|                                                  ctx->wait_for_connected_seconds, cancellable, &local_error); | ||||
|   if (!client) | ||||
|     { | ||||
|       CalendarSourceData *source_data; | ||||
|  | ||||
|       source_data = &sources->priv->appointment_sources; | ||||
|       g_hash_table_remove (source_data->clients, source); | ||||
|       g_signal_emit (sources, source_data->changed_signal, 0); | ||||
|     } | ||||
|  | ||||
|   if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) | ||||
|     { | ||||
|       CalendarSourceData *source_data; | ||||
|  | ||||
|       source_data = &sources->priv->task_sources; | ||||
|       g_hash_table_remove (source_data->clients, source); | ||||
|       g_signal_emit (sources, source_data->changed_signal, 0); | ||||
|       if (local_error) | ||||
|         g_task_return_error (task, local_error); | ||||
|       else | ||||
|         g_task_return_pointer (task, NULL, NULL); | ||||
|     } else { | ||||
|       g_task_return_pointer (task, client, g_object_unref); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| ensure_appointment_sources (CalendarSources *sources) | ||||
| void | ||||
| calendar_sources_connect_client (CalendarSources *sources, | ||||
|                                  ESource *source, | ||||
|                                  ECalClientSourceType source_type, | ||||
|                                  guint32 wait_for_connected_seconds, | ||||
|                                  GCancellable *cancellable, | ||||
|                                  GAsyncReadyCallback callback, | ||||
|                                  gpointer user_data) | ||||
| { | ||||
|   if (!sources->priv->appointment_sources.loaded) | ||||
|   AsyncContext *ctx; | ||||
|   g_autoptr (GTask) task = NULL; | ||||
|  | ||||
|   ctx = g_slice_new0 (AsyncContext); | ||||
|   ctx->source = g_object_ref (source); | ||||
|   ctx->source_type = source_type; | ||||
|   ctx->wait_for_connected_seconds = wait_for_connected_seconds; | ||||
|  | ||||
|   task = g_task_new (sources, cancellable, callback, user_data); | ||||
|   g_task_set_source_tag (task, calendar_sources_connect_client); | ||||
|   g_task_set_task_data (task, ctx, async_context_free); | ||||
|  | ||||
|   g_task_run_in_thread (task, calendar_sources_connect_client_thread); | ||||
| } | ||||
|  | ||||
| EClient * | ||||
| calendar_sources_connect_client_finish (CalendarSources *sources, | ||||
|                                         GAsyncResult *result, | ||||
|                                         GError **error) | ||||
| { | ||||
|   g_return_val_if_fail (g_task_is_valid (result, sources), NULL); | ||||
|   g_return_val_if_fail (g_async_result_is_tagged (result, calendar_sources_connect_client), NULL); | ||||
|  | ||||
|   return g_task_propagate_pointer (G_TASK (result), error); | ||||
| } | ||||
|  | ||||
|  | ||||
| void | ||||
| print_debug (const gchar *format, | ||||
|              ...) | ||||
| { | ||||
|   g_autofree char *s = NULL; | ||||
|   g_autofree char *timestamp = NULL; | ||||
|   va_list ap; | ||||
|   g_autoptr (GDateTime) now = NULL; | ||||
|   static volatile gsize once_init_value = 0; | ||||
|   static gboolean show_debug = FALSE; | ||||
|   static guint pid = 0; | ||||
|  | ||||
|   if (g_once_init_enter (&once_init_value)) | ||||
|     { | ||||
|       calendar_sources_load_esource_list (sources->priv->registry, | ||||
|                                           &sources->priv->appointment_sources); | ||||
|       sources->priv->appointment_sources.loaded = TRUE; | ||||
|       show_debug = (g_getenv ("CALENDAR_SERVER_DEBUG") != NULL); | ||||
|       pid = getpid (); | ||||
|       g_once_init_leave (&once_init_value, 1); | ||||
|     } | ||||
| } | ||||
|  | ||||
| GList * | ||||
| calendar_sources_get_appointment_clients (CalendarSources *sources) | ||||
| { | ||||
|   GList *list, *link; | ||||
|  | ||||
|   g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL); | ||||
|  | ||||
|   ensure_appointment_sources (sources); | ||||
|  | ||||
|   list = g_hash_table_get_values (sources->priv->appointment_sources.clients); | ||||
|  | ||||
|   for (link = list; link != NULL; link = g_list_next (link)) | ||||
|     link->data = ((ClientData *) link->data)->client; | ||||
|  | ||||
|   return list; | ||||
| } | ||||
|  | ||||
| static void | ||||
| ensure_task_sources (CalendarSources *sources) | ||||
| { | ||||
|   if (!sources->priv->task_sources.loaded) | ||||
|     { | ||||
|       calendar_sources_load_esource_list (sources->priv->registry, | ||||
|                                           &sources->priv->task_sources); | ||||
|       sources->priv->task_sources.loaded = TRUE; | ||||
|     } | ||||
| } | ||||
|  | ||||
| GList * | ||||
| calendar_sources_get_task_clients (CalendarSources *sources) | ||||
| { | ||||
|   GList *list, *link; | ||||
|  | ||||
|   g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL); | ||||
|  | ||||
|   ensure_task_sources (sources); | ||||
|  | ||||
|   list = g_hash_table_get_values (sources->priv->task_sources.clients); | ||||
|  | ||||
|   for (link = list; link != NULL; link = g_list_next (link)) | ||||
|     link->data = ((ClientData *) link->data)->client; | ||||
|  | ||||
|   return list; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| calendar_sources_has_sources (CalendarSources *sources) | ||||
| { | ||||
|   g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), FALSE); | ||||
|  | ||||
|   ensure_appointment_sources (sources); | ||||
|   ensure_task_sources (sources); | ||||
|  | ||||
|   return g_hash_table_size (sources->priv->appointment_sources.clients) > 0 || | ||||
|     g_hash_table_size (sources->priv->task_sources.clients) > 0; | ||||
|  | ||||
|   if (!show_debug) | ||||
|     goto out; | ||||
|  | ||||
|   now = g_date_time_new_now_local (); | ||||
|   timestamp = g_date_time_format (now, "%H:%M:%S"); | ||||
|  | ||||
|   va_start (ap, format); | ||||
|   s = g_strdup_vprintf (format, ap); | ||||
|   va_end (ap); | ||||
|  | ||||
|   g_print ("gnome-shell-calendar-server[%d]: %s.%03d: %s\n", | ||||
|            pid, timestamp, g_date_time_get_microsecond (now), s); | ||||
|  out: | ||||
|   ; | ||||
| } | ||||
|   | ||||
| @@ -26,17 +26,38 @@ | ||||
|  | ||||
| #include <glib-object.h> | ||||
|  | ||||
| #define EDS_DISABLE_DEPRECATED | ||||
| G_GNUC_BEGIN_IGNORE_DEPRECATIONS | ||||
| #include <libedataserver/libedataserver.h> | ||||
| #include <libecal/libecal.h> | ||||
| G_GNUC_END_IGNORE_DEPRECATIONS | ||||
|  | ||||
| G_BEGIN_DECLS | ||||
|  | ||||
| #define CALENDAR_TYPE_SOURCES (calendar_sources_get_type ()) | ||||
| G_DECLARE_FINAL_TYPE (CalendarSources, calendar_sources, | ||||
|                       CALENDAR, SOURCES, GObject) | ||||
|  | ||||
| CalendarSources *calendar_sources_get                     (void); | ||||
| GList           *calendar_sources_get_appointment_clients (CalendarSources *sources); | ||||
| GList           *calendar_sources_get_task_clients        (CalendarSources *sources); | ||||
| CalendarSources *calendar_sources_get                (void); | ||||
| ESourceRegistry *calendar_sources_get_registry       (CalendarSources *sources); | ||||
| GSList          *calendar_sources_ref_clients        (CalendarSources *sources); | ||||
| gboolean         calendar_sources_has_clients        (CalendarSources *sources); | ||||
|  | ||||
| gboolean         calendar_sources_has_sources             (CalendarSources *sources); | ||||
| void             calendar_sources_connect_client     (CalendarSources *sources, | ||||
|                                                       ESource *source, | ||||
|                                                       ECalClientSourceType source_type, | ||||
|                                                       guint32 wait_for_connected_seconds, | ||||
|                                                       GCancellable *cancellable, | ||||
|                                                       GAsyncReadyCallback callback, | ||||
|                                                       gpointer user_data); | ||||
| EClient         *calendar_sources_connect_client_finish | ||||
|                                                      (CalendarSources *sources, | ||||
|                                                       GAsyncResult *result, | ||||
|                                                       GError **error); | ||||
|  | ||||
| /* Set the environment variable CALENDAR_SERVER_DEBUG to show debug */ | ||||
| void            print_debug                          (const gchar *str, | ||||
|                                                       ...) G_GNUC_PRINTF (1, 2); | ||||
|  | ||||
| G_END_DECLS | ||||
|  | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -109,17 +109,11 @@ load_folder (GHashTable *folders, | ||||
|  | ||||
|   while ((name = g_dir_read_name (dir))) | ||||
|     { | ||||
|       g_autofree gchar *stripped_name = NULL; | ||||
|       g_autofree gchar *filename = NULL; | ||||
|       g_autoptr(GKeyFile) keyfile = NULL; | ||||
|  | ||||
|       if (!g_str_has_suffix (name, ".directory")) | ||||
|         continue; | ||||
|  | ||||
|       stripped_name = g_strndup (name, strlen (name) - strlen (".directory")); | ||||
|  | ||||
|       /* First added wins */ | ||||
|       if (g_hash_table_contains (folders, stripped_name)) | ||||
|       if (g_hash_table_contains (folders, name)) | ||||
|         continue; | ||||
|  | ||||
|       filename = g_build_filename (path, name, NULL); | ||||
| @@ -134,8 +128,7 @@ load_folder (GHashTable *folders, | ||||
|                                                      NULL, NULL); | ||||
|  | ||||
|           if (translated != NULL) | ||||
|             g_hash_table_insert (folders, g_steal_pointer (&stripped_name), | ||||
|                                  translated); | ||||
|             g_hash_table_insert (folders, g_strdup (name), translated); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -218,10 +218,17 @@ window_backed_app_get_icon (ShellApp *app, | ||||
|  | ||||
|   if (meta_window_get_client_type (window) == META_WINDOW_CLIENT_TYPE_X11) | ||||
|     { | ||||
|       widget = st_texture_cache_bind_cairo_surface_property (st_texture_cache_get_default (), | ||||
|                                                              G_OBJECT (window), | ||||
|                                                              "icon", | ||||
|                                                              scaled_size); | ||||
|       StWidget *texture_actor; | ||||
|  | ||||
|       texture_actor = | ||||
|         st_texture_cache_bind_cairo_surface_property (st_texture_cache_get_default (), | ||||
|                                                       G_OBJECT (window), | ||||
|                                                       "icon", | ||||
|                                                       scaled_size); | ||||
|  | ||||
|       widget = g_object_new (ST_TYPE_BIN, | ||||
|                              "child", texture_actor, | ||||
|                              NULL); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
| @@ -1152,6 +1159,10 @@ shell_app_get_pids (ShellApp *app) | ||||
|     { | ||||
|       MetaWindow *window = iter->data; | ||||
|       int pid = meta_window_get_pid (window); | ||||
|  | ||||
|       if (pid < 1) | ||||
|         continue; | ||||
|  | ||||
|       /* Note in the (by far) common case, app will only have one pid, so | ||||
|        * we'll hit the first element, so don't worry about O(N^2) here. | ||||
|        */ | ||||
|   | ||||
| @@ -57,6 +57,7 @@ struct _ShellGlobal { | ||||
|  | ||||
|   ClutterStage *stage; | ||||
|  | ||||
|   MetaBackend *backend; | ||||
|   MetaDisplay *meta_display; | ||||
|   MetaWorkspaceManager *workspace_manager; | ||||
|   Display *xdisplay; | ||||
| @@ -95,6 +96,7 @@ enum { | ||||
|   PROP_0, | ||||
|  | ||||
|   PROP_SESSION_MODE, | ||||
|   PROP_BACKEND, | ||||
|   PROP_DISPLAY, | ||||
|   PROP_WORKSPACE_MANAGER, | ||||
|   PROP_SCREEN_WIDTH, | ||||
| @@ -230,6 +232,9 @@ shell_global_get_property(GObject         *object, | ||||
|     case PROP_SESSION_MODE: | ||||
|       g_value_set_string (value, shell_global_get_session_mode (global)); | ||||
|       break; | ||||
|     case PROP_BACKEND: | ||||
|       g_value_set_object (value, global->backend); | ||||
|       break; | ||||
|     case PROP_DISPLAY: | ||||
|       g_value_set_object (value, global->meta_display); | ||||
|       break; | ||||
| @@ -472,6 +477,13 @@ shell_global_class_init (ShellGlobalClass *klass) | ||||
|                                                      "Screen height, in pixels", | ||||
|                                                      0, G_MAXINT, 1, | ||||
|                                                      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); | ||||
|   g_object_class_install_property (gobject_class, | ||||
|                                    PROP_BACKEND, | ||||
|                                    g_param_spec_object ("backend", | ||||
|                                                         "Backend", | ||||
|                                                         "MetaBackend object", | ||||
|                                                         META_TYPE_BACKEND, | ||||
|                                                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); | ||||
|   g_object_class_install_property (gobject_class, | ||||
|                                    PROP_DISPLAY, | ||||
|                                    g_param_spec_object ("display", | ||||
| @@ -947,6 +959,7 @@ _shell_global_set_plugin (ShellGlobal *global, | ||||
|   g_return_if_fail (SHELL_IS_GLOBAL (global)); | ||||
|   g_return_if_fail (global->plugin == NULL); | ||||
|  | ||||
|   global->backend = meta_get_backend (); | ||||
|   global->plugin = plugin; | ||||
|   global->wm = shell_wm_new (plugin); | ||||
|  | ||||
| @@ -1599,10 +1612,8 @@ shell_global_end_work (ShellGlobal *global) | ||||
|  * Idle means here no animations, no redrawing, and no ongoing background | ||||
|  * work. Since there is currently no way to hook into the Clutter master | ||||
|  * clock and know when is running, the implementation here is somewhat | ||||
|  * approximation. Animations done through the shell's Tweener module will | ||||
|  * be handled properly, but other animations may be detected as terminating | ||||
|  * early if they can be drawn fast enough so that the event loop goes idle | ||||
|  * between frames. | ||||
|  * approximation. Animations may be detected as terminating early if they | ||||
|  * can be drawn fast enough so that the event loop goes idle between frames. | ||||
|  * | ||||
|  * The intent of this function is for performance measurement runs | ||||
|  * where a number of actions should be run serially and each action is | ||||
|   | ||||
| @@ -638,6 +638,20 @@ shell_util_check_cloexec_fds (void) | ||||
|   g_info ("Open fd CLOEXEC check complete"); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * shell_util_get_uid: | ||||
|  * | ||||
|  * A wrapper around getuid() so that it can be used from JavaScript. This | ||||
|  * function will always succeed. | ||||
|  * | ||||
|  * Returns: the real user ID of the calling process | ||||
|  */ | ||||
| gint | ||||
| shell_util_get_uid (void) | ||||
| { | ||||
|   return getuid (); | ||||
| } | ||||
|  | ||||
| static void | ||||
| on_systemd_call_cb (GObject      *source, | ||||
|                     GAsyncResult *res, | ||||
|   | ||||
| @@ -78,6 +78,8 @@ gboolean shell_util_has_x11_display_extension (MetaDisplay *display, | ||||
|  | ||||
| char *shell_util_get_translated_folder_name (const char *name); | ||||
|  | ||||
| gint shell_util_get_uid (void); | ||||
|  | ||||
| G_END_DECLS | ||||
|  | ||||
| #endif /* __SHELL_UTIL_H__ */ | ||||
|   | ||||
| @@ -343,7 +343,7 @@ get_app_from_window_pid (ShellWindowTracker  *tracker, | ||||
|  | ||||
|   pid = meta_window_get_pid (window); | ||||
|  | ||||
|   if (pid == -1) | ||||
|   if (pid < 1) | ||||
|     return NULL; | ||||
|  | ||||
|   result = shell_window_tracker_get_app_from_pid (tracker, pid); | ||||
|   | ||||
| @@ -291,7 +291,6 @@ cr_term_to_string (CRTerm const * a_this) | ||||
|         for (cur = a_this; cur; cur = cur->next) { | ||||
|                 if ((cur->content.str == NULL) | ||||
|                     && (cur->content.num == NULL) | ||||
|                     && (cur->content.str == NULL) | ||||
|                     && (cur->content.rgb == NULL)) | ||||
|                         continue; | ||||
|  | ||||
| @@ -485,7 +484,6 @@ cr_term_one_to_string (CRTerm const * a_this) | ||||
|  | ||||
|         if ((a_this->content.str == NULL) | ||||
|             && (a_this->content.num == NULL) | ||||
|             && (a_this->content.str == NULL) | ||||
|             && (a_this->content.rgb == NULL)) | ||||
|                 return NULL ; | ||||
|  | ||||
|   | ||||
| @@ -314,7 +314,7 @@ st_entry_get_preferred_width (ClutterActor *actor, | ||||
| { | ||||
|   StEntryPrivate *priv = ST_ENTRY_PRIV (actor); | ||||
|   StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); | ||||
|   gfloat hint_w, icon_w; | ||||
|   gfloat hint_w, hint_min_w, icon_w; | ||||
|  | ||||
|   st_theme_node_adjust_for_height (theme_node, &for_height); | ||||
|  | ||||
| @@ -324,10 +324,11 @@ st_entry_get_preferred_width (ClutterActor *actor, | ||||
|  | ||||
|   if (priv->hint_actor) | ||||
|     { | ||||
|       clutter_actor_get_preferred_width (priv->hint_actor, -1, NULL, &hint_w); | ||||
|       clutter_actor_get_preferred_width (priv->hint_actor, -1, | ||||
|                                          &hint_min_w, &hint_w); | ||||
|  | ||||
|       if (min_width_p && hint_w > *min_width_p) | ||||
|         *min_width_p = hint_w; | ||||
|       if (min_width_p && hint_min_w > *min_width_p) | ||||
|         *min_width_p = hint_min_w; | ||||
|  | ||||
|       if (natural_width_p && hint_w > *natural_width_p) | ||||
|         *natural_width_p = hint_w; | ||||
| @@ -422,7 +423,7 @@ st_entry_allocate (ClutterActor          *actor, | ||||
|   StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); | ||||
|   ClutterActorBox content_box, child_box, icon_box, hint_box; | ||||
|   gfloat icon_w, icon_h; | ||||
|   gfloat hint_w, hint_h; | ||||
|   gfloat hint_w, hint_min_w, hint_h; | ||||
|   gfloat entry_h, min_h, pref_h, avail_h; | ||||
|   ClutterActor *left_icon, *right_icon; | ||||
|   gboolean is_rtl; | ||||
| @@ -488,9 +489,11 @@ st_entry_allocate (ClutterActor          *actor, | ||||
|       /* now allocate the hint actor */ | ||||
|       hint_box = child_box; | ||||
|  | ||||
|       clutter_actor_get_preferred_width (priv->hint_actor, -1, NULL, &hint_w); | ||||
|       clutter_actor_get_preferred_width (priv->hint_actor, -1, &hint_min_w, &hint_w); | ||||
|       clutter_actor_get_preferred_height (priv->hint_actor, -1, NULL, &hint_h); | ||||
|  | ||||
|       hint_w = CLAMP (hint_w, hint_min_w, child_box.x2 - child_box.x1); | ||||
|  | ||||
|       if (is_rtl) | ||||
|         hint_box.x1 = hint_box.x2 - hint_w; | ||||
|       else | ||||
|   | ||||
| @@ -59,7 +59,6 @@ struct _StIconPrivate | ||||
|   gint             theme_icon_size; /* icon size from theme node */ | ||||
|   gint             icon_size;       /* icon size we are using */ | ||||
|   GIcon           *fallback_gicon; | ||||
|   GIcon           *default_gicon; | ||||
|  | ||||
|   CoglPipeline    *shadow_pipeline; | ||||
|   StShadow        *shadow_spec; | ||||
| @@ -73,6 +72,8 @@ static gboolean st_icon_update_icon_size (StIcon *icon); | ||||
| static void st_icon_update_shadow_pipeline (StIcon *icon); | ||||
| static void st_icon_clear_shadow_pipeline (StIcon *icon); | ||||
|  | ||||
| static GIcon *default_gicon = NULL; | ||||
|  | ||||
| #define IMAGE_MISSING_ICON_NAME "image-missing" | ||||
| #define DEFAULT_ICON_SIZE 48 | ||||
|  | ||||
| @@ -168,7 +169,6 @@ st_icon_dispose (GObject *gobject) | ||||
|  | ||||
|   g_clear_object (&priv->gicon); | ||||
|   g_clear_object (&priv->fallback_gicon); | ||||
|   g_clear_object (&priv->default_gicon); | ||||
|   g_clear_pointer (&priv->shadow_pipeline, cogl_object_unref); | ||||
|   g_clear_pointer (&priv->shadow_spec, st_shadow_unref); | ||||
|  | ||||
| @@ -295,14 +295,15 @@ st_icon_init (StIcon *self) | ||||
| { | ||||
|   ClutterLayoutManager *layout_manager; | ||||
|  | ||||
|   if (G_UNLIKELY (default_gicon == NULL)) | ||||
|     default_gicon = g_themed_icon_new (IMAGE_MISSING_ICON_NAME); | ||||
|  | ||||
|   self->priv = st_icon_get_instance_private (self); | ||||
|  | ||||
|   layout_manager = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_FILL, | ||||
|                                            CLUTTER_BIN_ALIGNMENT_FILL); | ||||
|   clutter_actor_set_layout_manager (CLUTTER_ACTOR (self), layout_manager); | ||||
|  | ||||
|   self->priv->default_gicon = g_themed_icon_new (IMAGE_MISSING_ICON_NAME); | ||||
|  | ||||
|   self->priv->icon_size = DEFAULT_ICON_SIZE; | ||||
|   self->priv->prop_icon_size = -1; | ||||
|  | ||||
| @@ -419,7 +420,10 @@ st_icon_update (StIcon *icon) | ||||
|     } | ||||
|  | ||||
|   if (priv->gicon == NULL && priv->fallback_gicon == NULL) | ||||
|     return; | ||||
|     { | ||||
|       g_clear_pointer (&priv->icon_texture, clutter_actor_destroy); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|   if (!st_widget_get_resource_scale (ST_WIDGET (icon), &resource_scale)) | ||||
|     return; | ||||
| @@ -453,7 +457,7 @@ st_icon_update (StIcon *icon) | ||||
|   if (priv->pending_texture == NULL) | ||||
|     priv->pending_texture = st_texture_cache_load_gicon (cache, | ||||
|                                                          theme_node, | ||||
|                                                          priv->default_gicon, | ||||
|                                                          default_gicon, | ||||
|                                                          priv->icon_size, | ||||
|                                                          paint_scale, | ||||
|                                                          resource_scale); | ||||
|   | ||||
| @@ -517,7 +517,9 @@ _st_create_shadow_pipeline_from_actor (StShadow     *shadow_spec, | ||||
|  | ||||
|       clutter_actor_set_opacity_override (actor, 255); | ||||
|  | ||||
|       paint_context = clutter_paint_context_new_for_framebuffer (fb); | ||||
|       paint_context = | ||||
|         clutter_paint_context_new_for_framebuffer (fb, NULL, | ||||
|                                                    CLUTTER_PAINT_FLAG_NONE); | ||||
|       clutter_actor_paint (actor, paint_context); | ||||
|       clutter_paint_context_destroy (paint_context); | ||||
|  | ||||
|   | ||||
| @@ -297,6 +297,9 @@ on_custom_stylesheets_changed (StTheme        *theme, | ||||
|   GHashTableIter iter; | ||||
|   StThemeNode *node; | ||||
|  | ||||
|   if (context->root_node) | ||||
|     _st_theme_node_reset_for_stylesheet_change (context->root_node); | ||||
|  | ||||
|   g_hash_table_iter_init (&iter, context->nodes); | ||||
|  | ||||
|   while (g_hash_table_iter_next (&iter, (gpointer *) &node, NULL)) | ||||
| @@ -466,3 +469,19 @@ st_theme_context_intern_node (StThemeContext *context, | ||||
|   g_hash_table_add (context->nodes, g_object_ref (node)); | ||||
|   return node; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * st_theme_context_get_scale_factor: | ||||
|  * @context: a #StThemeContext | ||||
|  * | ||||
|  * Return the current scale factor of @context. | ||||
|  * | ||||
|  * Return value: a scale factor | ||||
|  */ | ||||
| int | ||||
| st_theme_context_get_scale_factor (StThemeContext *context) | ||||
| { | ||||
|   g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), -1); | ||||
|  | ||||
|   return context->scale_factor; | ||||
| } | ||||
|   | ||||
| @@ -58,6 +58,8 @@ StThemeNode *               st_theme_context_get_root_node  (StThemeContext | ||||
| StThemeNode *               st_theme_context_intern_node    (StThemeContext             *context, | ||||
|                                                              StThemeNode                *node); | ||||
|  | ||||
| int st_theme_context_get_scale_factor (StThemeContext *context); | ||||
|  | ||||
| G_END_DECLS | ||||
|  | ||||
| #endif /* __ST_THEME_CONTEXT_H__ */ | ||||
|   | ||||
| @@ -643,15 +643,13 @@ create_cairo_pattern_of_background_image (StThemeNode *node, | ||||
|   gdouble background_image_width, background_image_height; | ||||
|   gdouble x, y; | ||||
|   gdouble scale_w, scale_h; | ||||
|   int scale_factor; | ||||
|  | ||||
|   file = st_theme_node_get_background_image (node); | ||||
|  | ||||
|   texture_cache = st_texture_cache_get_default (); | ||||
|  | ||||
|   g_object_get (node->context, "scale-factor", &scale_factor, NULL); | ||||
|   surface = st_texture_cache_load_file_to_cairo_surface (texture_cache, file, | ||||
|                                                          scale_factor, | ||||
|                                                          node->cached_scale_factor, | ||||
|                                                          resource_scale); | ||||
|  | ||||
|   if (surface == NULL) | ||||
| @@ -1374,7 +1372,6 @@ st_theme_node_load_border_image (StThemeNode *node, | ||||
|     { | ||||
|       StBorderImage *border_image; | ||||
|       GFile *file; | ||||
|       int scale_factor; | ||||
|  | ||||
|       border_image = st_theme_node_get_border_image (node); | ||||
|       if (border_image == NULL) | ||||
| @@ -1382,10 +1379,9 @@ st_theme_node_load_border_image (StThemeNode *node, | ||||
|  | ||||
|       file = st_border_image_get_file (border_image); | ||||
|  | ||||
|       g_object_get (node->context, "scale-factor", &scale_factor, NULL); | ||||
|  | ||||
|       node->border_slices_texture = st_texture_cache_load_file_to_cogl_texture (st_texture_cache_get_default (), | ||||
|                                                                                 file, scale_factor, | ||||
|                                                                                 file, | ||||
|                                                                                 node->cached_scale_factor, | ||||
|                                                                                 resource_scale); | ||||
|       if (node->border_slices_texture == NULL) | ||||
|         goto out; | ||||
| @@ -1413,17 +1409,15 @@ st_theme_node_load_background_image (StThemeNode *node, | ||||
|     { | ||||
|       GFile *background_image; | ||||
|       StShadow *background_image_shadow_spec; | ||||
|       int scale_factor; | ||||
|  | ||||
|       background_image = st_theme_node_get_background_image (node); | ||||
|       if (background_image == NULL) | ||||
|         goto out; | ||||
|  | ||||
|       g_object_get (node->context, "scale-factor", &scale_factor, NULL); | ||||
|  | ||||
|       background_image_shadow_spec = st_theme_node_get_background_image_shadow (node); | ||||
|       node->background_texture = st_texture_cache_load_file_to_cogl_texture (st_texture_cache_get_default (), | ||||
|                                                                              background_image, scale_factor, | ||||
|                                                                              background_image, | ||||
|                                                                              node->cached_scale_factor, | ||||
|                                                                              resource_scale); | ||||
|       if (node->background_texture == NULL) | ||||
|         goto out; | ||||
|   | ||||
| @@ -117,6 +117,8 @@ struct _StThemeNode { | ||||
|   CoglPipeline *color_pipeline; | ||||
|  | ||||
|   StThemeNodePaintState cached_state; | ||||
|  | ||||
|   int cached_scale_factor; | ||||
| }; | ||||
|  | ||||
| void _st_theme_node_ensure_background (StThemeNode *node); | ||||
|   | ||||
| @@ -225,6 +225,7 @@ st_theme_node_new (StThemeContext    *context, | ||||
|   node->element_classes = split_on_whitespace (element_class); | ||||
|   node->pseudo_classes = split_on_whitespace (pseudo_class); | ||||
|   node->inline_style = g_strdup (inline_style); | ||||
|   node->cached_scale_factor = st_theme_context_get_scale_factor (context); | ||||
|  | ||||
|   return node; | ||||
| } | ||||
| @@ -345,6 +346,7 @@ st_theme_node_equal (StThemeNode *node_a, StThemeNode *node_b) | ||||
|       node_a->context != node_b->context || | ||||
|       node_a->theme != node_b->theme || | ||||
|       node_a->element_type != node_b->element_type || | ||||
|       node_a->cached_scale_factor != node_b->cached_scale_factor || | ||||
|       g_strcmp0 (node_a->element_id, node_b->element_id) || | ||||
|       g_strcmp0 (node_a->inline_style, node_b->inline_style)) | ||||
|     return FALSE; | ||||
| @@ -396,6 +398,7 @@ st_theme_node_hash (StThemeNode *node) | ||||
|   hash = hash * 33 + GPOINTER_TO_UINT (node->context); | ||||
|   hash = hash * 33 + GPOINTER_TO_UINT (node->theme); | ||||
|   hash = hash * 33 + ((guint) node->element_type); | ||||
|   hash = hash * 33 + ((guint) node->cached_scale_factor); | ||||
|  | ||||
|   if (node->element_id != NULL) | ||||
|     hash = hash * 33 + g_str_hash (node->element_id); | ||||
| @@ -975,9 +978,7 @@ get_length_from_term (StThemeNode *node, | ||||
|   } type = ABSOLUTE; | ||||
|  | ||||
|   double multiplier = 1.0; | ||||
|   int scale_factor; | ||||
|  | ||||
|   g_object_get (node->context, "scale-factor", &scale_factor, NULL); | ||||
|  | ||||
|   if (term->type != TERM_NUMBER) | ||||
|     { | ||||
| @@ -992,7 +993,7 @@ get_length_from_term (StThemeNode *node, | ||||
|     { | ||||
|     case NUM_LENGTH_PX: | ||||
|       type = ABSOLUTE; | ||||
|       multiplier = 1 * scale_factor; | ||||
|       multiplier = 1 * node->cached_scale_factor; | ||||
|       break; | ||||
|     case NUM_LENGTH_PT: | ||||
|       type = POINTS; | ||||
| @@ -1123,14 +1124,10 @@ get_length_from_term_int (StThemeNode *node, | ||||
| { | ||||
|   double value; | ||||
|   GetFromTermResult result; | ||||
|   int scale_factor; | ||||
|  | ||||
|   result = get_length_from_term (node, term, use_parent_font, &value); | ||||
|   if (result == VALUE_FOUND) | ||||
|     { | ||||
|       g_object_get (node->context, "scale-factor", &scale_factor, NULL); | ||||
|       *length = (int) ((value / scale_factor) + 0.5) * scale_factor; | ||||
|     } | ||||
|     *length = (int) ((value / node->cached_scale_factor) + 0.5) * node->cached_scale_factor; | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| @@ -3012,7 +3009,6 @@ StBorderImage * | ||||
| st_theme_node_get_border_image (StThemeNode *node) | ||||
| { | ||||
|   int i; | ||||
|   int scale_factor; | ||||
|  | ||||
|   if (node->border_image_computed) | ||||
|     return node->border_image; | ||||
| @@ -3021,7 +3017,6 @@ st_theme_node_get_border_image (StThemeNode *node) | ||||
|   node->border_image_computed = TRUE; | ||||
|  | ||||
|   ensure_properties (node); | ||||
|   g_object_get (node->context, "scale-factor", &scale_factor, NULL); | ||||
|  | ||||
|   for (i = node->n_properties - 1; i >= 0; i--) | ||||
|     { | ||||
| @@ -3126,7 +3121,7 @@ st_theme_node_get_border_image (StThemeNode *node) | ||||
|  | ||||
|           node->border_image = st_border_image_new (file, | ||||
|                                                     border_top, border_right, border_bottom, border_left, | ||||
|                                                     scale_factor); | ||||
|                                                     node->cached_scale_factor); | ||||
|  | ||||
|           g_object_unref (file); | ||||
|  | ||||
| @@ -3967,6 +3962,9 @@ st_theme_node_geometry_equal (StThemeNode *node, | ||||
|  | ||||
|   g_return_val_if_fail (ST_IS_THEME_NODE (other), FALSE); | ||||
|  | ||||
|   if (node->cached_scale_factor != other->cached_scale_factor) | ||||
|     return FALSE; | ||||
|  | ||||
|   _st_theme_node_ensure_geometry (node); | ||||
|   _st_theme_node_ensure_geometry (other); | ||||
|  | ||||
|   | ||||
| @@ -546,7 +546,8 @@ main (int argc, char **argv) | ||||
|  | ||||
|   meta_test_init (); | ||||
|  | ||||
|   chdir (cwd); | ||||
|   if (chdir (cwd) < 0) | ||||
|     g_error ("chdir('%s') failed: %s", cwd, g_strerror (errno)); | ||||
|  | ||||
|   /* Make sure our assumptions about resolution are correct */ | ||||
|   g_object_set (clutter_settings_get_default (), "font-dpi", -1, NULL); | ||||
|   | ||||
| @@ -5,6 +5,14 @@ used as a stand-alone project as well. | ||||
|  | ||||
| Bugs should be reported to the GNOME [bug tracking system][bug-tracker]. | ||||
|  | ||||
| ## Installation | ||||
| If Extensions is not already installed on your GNOME system, we | ||||
| recommend getting it from [flathub]. | ||||
|  | ||||
| <a href='https://flathub.org/apps/details/org.gnome.Extensions'> | ||||
|   <img width='240' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/> | ||||
| </a> | ||||
|  | ||||
| ## Building | ||||
| Before the project can be built stand-alone, the po directory has | ||||
| to be populated with translations (from gnome-shell). | ||||
| @@ -20,4 +28,5 @@ License, version 2 or later. See the [COPYING][license] file for details. | ||||
|  | ||||
| [logo]: logo.png | ||||
| [bug-tracker]: https://gitlab.gnome.org/GNOME/gnome-shell/issues | ||||
| [flathub]: https://flathub.org | ||||
| [license]: COPYING | ||||
|   | ||||
| @@ -6,6 +6,7 @@ gnome.compile_resources( | ||||
|   install_dir: pkgdatadir | ||||
| ) | ||||
|  | ||||
| desktop_file = app_id + '.desktop' | ||||
| desktopconf = configuration_data() | ||||
| # We substitute in bindir so it works as an autostart | ||||
| # file when built in a non-system prefix | ||||
| @@ -15,17 +16,25 @@ desktopconf.set('prgname', prgname) | ||||
|  | ||||
| i18n.merge_file('desktop', | ||||
|   input: configure_file( | ||||
|     input: app_id + '.desktop.in.in', | ||||
|     output: app_id + '.desktop.in', | ||||
|     input: desktop_file + '.in.in', | ||||
|     output: desktop_file + '.in', | ||||
|     configuration: desktopconf | ||||
|   ), | ||||
|   output: app_id + '.desktop', | ||||
|   output: desktop_file, | ||||
|   po_dir: po_dir, | ||||
|   install: true, | ||||
|   install_dir: desktopdir, | ||||
|   type: 'desktop' | ||||
| ) | ||||
|  | ||||
| if (desktop_file_validate.found()) | ||||
|   test('Validating ' + desktop_file, | ||||
|     desktop_file_validate, | ||||
|     args: [desktop_file], | ||||
|     workdir: meson.current_build_dir() | ||||
|   ) | ||||
| endif | ||||
|  | ||||
| configure_file( | ||||
|   input: app_id + '.service.in', | ||||
|   output: app_id + '.service', | ||||
|   | ||||
| @@ -14,6 +14,7 @@ | ||||
|   <url type="translate">https://wiki.gnome.org/TranslationProject</url> | ||||
|  | ||||
|   <project_group>GNOME</project_group> | ||||
|   <developer_name>The GNOME Project</developer_name> | ||||
|  | ||||
|   <launchable type="desktop-id">org.gnome.Extensions.desktop</launchable> | ||||
|  | ||||
|   | ||||
| @@ -6,5 +6,5 @@ Icon=@app_id@ | ||||
| Comment=Configure GNOME Shell Extensions | ||||
| Exec=@bindir@/@prgname@ | ||||
| DBusActivatable=true | ||||
| Categories=GNOME;GTK; | ||||
| Categories=GNOME;GTK;Utility; | ||||
| OnlyShowIn=GNOME; | ||||
|   | ||||
| @@ -476,7 +476,7 @@ var ExtensionRow = GObject.registerClass({ | ||||
| function initEnvironment() { | ||||
|     // Monkey-patch in a "global" object that fakes some Shell utilities | ||||
|     // that ExtensionUtils depends on. | ||||
|     window.global = { | ||||
|     globalThis.global = { | ||||
|         log(...args) { | ||||
|             print(args.join(', ')); | ||||
|         }, | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| project('gnome-extensions-app', | ||||
|   version: '3.37.0', | ||||
|   meson_version: '>= 0.47.0', | ||||
|   meson_version: '>= 0.53.0', | ||||
|   license: 'GPLv2+' | ||||
| ) | ||||
|  | ||||
| @@ -37,6 +37,7 @@ servicedir = join_paths(datadir, 'dbus-1', 'services') | ||||
|  | ||||
| gjs = find_program('gjs') | ||||
| appstream_util = find_program('appstream-util', required: false) | ||||
| desktop_file_validate = find_program('desktop-file-validate', required: false) | ||||
|  | ||||
| subdir('data') | ||||
| subdir('js') | ||||
| @@ -64,8 +65,6 @@ if not meson.is_subproject() | ||||
|     'debug': get_option('debug'), | ||||
|   } | ||||
|  | ||||
|   if meson.version().version_compare('>= 0.53.0') | ||||
|     summary(summary_dirs, section: 'Directories') | ||||
|     summary(summary_build, section: 'Build Configuration') | ||||
|   endif | ||||
|   summary(summary_dirs, section: 'Directories') | ||||
|   summary(summary_build, section: 'Build Configuration') | ||||
| endif | ||||
|   | ||||
| @@ -44,6 +44,14 @@ __gnome_extensions() { | ||||
|   esac | ||||
|  | ||||
|   case "$COMMAND" in | ||||
|     create) | ||||
|       case "$prev" in | ||||
|         --template) | ||||
|           COMPREPLY=($(compgen -W "`gnome-extensions create --list-templates`" -- "$2")) | ||||
|           return 0 | ||||
|           ;; | ||||
|       esac | ||||
|       ;; | ||||
|     pack) | ||||
|       case "$prev" in | ||||
|         --podir|--out-dir|-o) | ||||
|   | ||||
| @@ -42,6 +42,13 @@ DESCRIPTION | ||||
| *gnome-extensions* is a utility that makes some common GNOME extensions | ||||
| operations available on the command line. | ||||
|  | ||||
| COMMON OPTIONS | ||||
| -------------- | ||||
| All commands except for *help* and *version* handle the following options: | ||||
|  | ||||
| *--quiet*, *-q*:: | ||||
| Do not print error messages | ||||
|  | ||||
| COMMANDS | ||||
| -------- | ||||
| *help* ['COMMAND']:: | ||||
| @@ -121,6 +128,9 @@ Creates a new extension from a template. | ||||
|   *--uuid*='UUID'::: | ||||
|     Set the unique extension ID in the metadata to 'UUID' | ||||
|  | ||||
|   *--template*='TEMPLATE'::: | ||||
|     Use 'TEMPLATE' as base for the new extension | ||||
|  | ||||
|   *-i*::: | ||||
|   *--interactive*::: | ||||
|     Prompt for any extension metadata that hasn't been provided | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| project('gnome-extensions-tool', 'c', | ||||
|   version: '3.37.0', | ||||
|   meson_version: '>= 0.47.0', | ||||
|   meson_version: '>= 0.53.0', | ||||
|   license: 'GPLv2+' | ||||
| ) | ||||
|  | ||||
| @@ -35,11 +35,13 @@ cc = meson.get_compiler('c') | ||||
|  | ||||
| bash_completion = dependency('bash-completion', required: get_option('bash_completion')) | ||||
|  | ||||
| po_dir = meson.source_root() + '/po' | ||||
|  | ||||
| subdir('src') | ||||
|  | ||||
| if bash_completion.found() | ||||
|   install_data('completion/bash/gnome-extensions', | ||||
|     install_dir: bash_completion.get_pkgconfig_variable('completionsdir') | ||||
|     install_dir: bash_completion.get_pkgconfig_variable('completionsdir', define_variable: ['datadir', datadir]) | ||||
|   ) | ||||
| endif | ||||
|  | ||||
| @@ -72,9 +74,7 @@ if not meson.is_subproject() | ||||
|     'bash_completion': bash_completion.found(), | ||||
|   } | ||||
|  | ||||
|   if meson.version().version_compare('>= 0.53.0') | ||||
|     summary(summary_dirs, section: 'Directories') | ||||
|     summary(summary_build, section: 'Build Configuration') | ||||
|     summary(summary_options, section: 'Build Options') | ||||
|   endif | ||||
|   summary(summary_dirs, section: 'Directories') | ||||
|   summary(summary_build, section: 'Build Configuration') | ||||
|   summary(summary_options, section: 'Build Options') | ||||
| endif | ||||
|   | ||||
| @@ -18,14 +18,22 @@ | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  */ | ||||
|  | ||||
| #define _GNU_SOURCE /* for strcasestr */ | ||||
| #include <string.h> | ||||
|  | ||||
| #include <glib/gi18n.h> | ||||
| #include <gio/gio.h> | ||||
| #include <gio/gdesktopappinfo.h> | ||||
| #include <gio/gunixinputstream.h> | ||||
|  | ||||
| #include "commands.h" | ||||
| #include "common.h" | ||||
| #include "config.h" | ||||
|  | ||||
| #define TEMPLATES_PATH "/org/gnome/extensions-tool/templates" | ||||
| #define TEMPLATE_KEY "Path" | ||||
| #define SORT_DATA "desktop-id" | ||||
|  | ||||
| static char * | ||||
| get_shell_version (GError **error) | ||||
| { | ||||
| @@ -48,6 +56,66 @@ get_shell_version (GError **error) | ||||
|   return g_strjoinv (".", split_version); | ||||
| } | ||||
|  | ||||
| static GDesktopAppInfo * | ||||
| load_app_info_from_resource (const char *uri) | ||||
| { | ||||
|   g_autoptr (GFile) file = NULL; | ||||
|   g_autofree char *contents = NULL; | ||||
|   g_autoptr (GKeyFile) keyfile = NULL; | ||||
|  | ||||
|   file = g_file_new_for_uri (uri); | ||||
|   if (!g_file_load_contents (file, NULL, &contents, NULL, NULL, NULL)) | ||||
|     return NULL; | ||||
|  | ||||
|   keyfile = g_key_file_new (); | ||||
|   if (!g_key_file_load_from_data (keyfile, contents, -1, G_KEY_FILE_NONE, NULL)) | ||||
|     return NULL; | ||||
|  | ||||
|   return g_desktop_app_info_new_from_keyfile (keyfile); | ||||
| } | ||||
|  | ||||
| static int | ||||
| sort_func (gconstpointer a, gconstpointer b) | ||||
| { | ||||
|   GObject *info1 = *((GObject **) a); | ||||
|   GObject *info2 = *((GObject **) b); | ||||
|   const char *desktop1 = g_object_get_data (info1, SORT_DATA); | ||||
|   const char *desktop2 = g_object_get_data (info2, SORT_DATA); | ||||
|  | ||||
|   return g_strcmp0 (desktop1, desktop2); | ||||
| } | ||||
|  | ||||
| static GPtrArray * | ||||
| get_templates (void) | ||||
| { | ||||
|   g_auto (GStrv) children = NULL; | ||||
|   GPtrArray *templates = g_ptr_array_new_with_free_func (g_object_unref); | ||||
|   char **s; | ||||
|  | ||||
|   children = g_resources_enumerate_children (TEMPLATES_PATH, 0, NULL); | ||||
|  | ||||
|   for (s = children; *s; s++) | ||||
|     { | ||||
|       g_autofree char *uri = NULL; | ||||
|       GDesktopAppInfo *info; | ||||
|  | ||||
|       if (!g_str_has_suffix (*s, ".desktop")) | ||||
|         continue; | ||||
|  | ||||
|       uri = g_strdup_printf ("resource://" TEMPLATES_PATH "/%s", *s); | ||||
|       info = load_app_info_from_resource (uri); | ||||
|       if (!info) | ||||
|         continue; | ||||
|  | ||||
|       g_object_set_data_full (G_OBJECT (info), SORT_DATA, g_strdup (*s), g_free); | ||||
|       g_ptr_array_add (templates, info); | ||||
|     } | ||||
|  | ||||
|   g_ptr_array_sort (templates, sort_func); | ||||
|  | ||||
|   return templates; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| create_metadata (GFile       *target_dir, | ||||
|                  const char  *uuid, | ||||
| @@ -85,21 +153,30 @@ create_metadata (GFile       *target_dir, | ||||
| } | ||||
|  | ||||
|  | ||||
| #define TEMPLATE_PATH "/org/gnome/extensions-tool/template" | ||||
| static gboolean | ||||
| copy_extension_template (GFile *target_dir, GError **error) | ||||
| copy_extension_template (const char *template, GFile *target_dir, GError **error) | ||||
| { | ||||
|   g_auto (GStrv) templates = NULL; | ||||
|   g_autofree char *path = NULL; | ||||
|   char **s; | ||||
|  | ||||
|   templates = g_resources_enumerate_children (TEMPLATE_PATH, 0, NULL); | ||||
|   path = g_strdup_printf (TEMPLATES_PATH "/%s", template); | ||||
|   templates = g_resources_enumerate_children (path, 0, NULL); | ||||
|  | ||||
|   if (templates == NULL) | ||||
|     { | ||||
|       g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, | ||||
|                    "No template %s", template); | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   for (s = templates; *s; s++) | ||||
|     { | ||||
|       g_autoptr (GFile) target = NULL; | ||||
|       g_autoptr (GFile) source = NULL; | ||||
|       g_autofree char *uri = NULL; | ||||
|  | ||||
|       uri = g_strdup_printf ("resource://%s/%s", TEMPLATE_PATH, *s); | ||||
|       uri = g_strdup_printf ("resource://%s/%s", path, *s); | ||||
|       source = g_file_new_for_uri (uri); | ||||
|       target = g_file_get_child (target_dir, *s); | ||||
|  | ||||
| @@ -134,11 +211,14 @@ launch_extension_source (GFile *dir, GError **error) | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| create_extension (const char *uuid, const char *name, const char *description) | ||||
| create_extension (const char *uuid, const char *name, const char *description, const char *template) | ||||
| { | ||||
|   g_autoptr (GFile) dir = NULL; | ||||
|   g_autoptr (GError) error = NULL; | ||||
|  | ||||
|   if (template == NULL) | ||||
|     template = "plain"; | ||||
|  | ||||
|   dir = g_file_new_build_filename (g_get_user_data_dir (), | ||||
|                                    "gnome-shell", | ||||
|                                    "extensions", | ||||
| @@ -157,7 +237,7 @@ create_extension (const char *uuid, const char *name, const char *description) | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   if (!copy_extension_template (dir, &error)) | ||||
|   if (!copy_extension_template (template, dir, &error)) | ||||
|     { | ||||
|       g_printerr ("%s\n", error->message); | ||||
|       return FALSE; | ||||
| @@ -173,14 +253,15 @@ create_extension (const char *uuid, const char *name, const char *description) | ||||
| } | ||||
|  | ||||
| static void | ||||
| prompt_metadata (char **uuid, char **name, char **description) | ||||
| prompt_metadata (char **uuid, char **name, char **description, char **template) | ||||
| { | ||||
|   g_autoptr (GInputStream) stdin = NULL; | ||||
|   g_autoptr (GDataInputStream) istream = NULL; | ||||
|  | ||||
|   if ((uuid == NULL || *uuid != NULL) && | ||||
|       (name == NULL || *name != NULL) && | ||||
|       (description == NULL || *description != NULL)) | ||||
|       (description == NULL || *description != NULL) && | ||||
|       (template == NULL || *template != NULL)) | ||||
|     return; | ||||
|  | ||||
|   stdin = g_unix_input_stream_new (0, FALSE); | ||||
| @@ -188,43 +269,127 @@ prompt_metadata (char **uuid, char **name, char **description) | ||||
|  | ||||
|   if (name != NULL && *name == NULL) | ||||
|     { | ||||
|       char *line; | ||||
|       char *line = NULL; | ||||
|  | ||||
|       g_print ( | ||||
|         _("Name should be a very short (ideally descriptive) string.\n" | ||||
|           "Examples are: %s"), | ||||
|         "“Click To Focus”, “Adblock”, “Shell Window Shrinker”\n"); | ||||
|       g_print ("%s: ", _("Name")); | ||||
|  | ||||
|       line = g_data_input_stream_read_line_utf8 (istream, NULL, NULL, NULL); | ||||
|       while (line == NULL) | ||||
|         { | ||||
|           g_print ("%s: ", _("Name")); | ||||
|  | ||||
|           line = g_data_input_stream_read_line_utf8 (istream, NULL, NULL, NULL); | ||||
|         } | ||||
|       *name = g_strdelimit (line, "\n", '\0'); | ||||
|  | ||||
|       g_print ("\n"); | ||||
|     } | ||||
|  | ||||
|   if (description != NULL && *description == NULL) | ||||
|     { | ||||
|       char *line; | ||||
|       char *line = NULL; | ||||
|  | ||||
|       g_print ( | ||||
|         _("Description is a single-sentence explanation of what your extension does.\n" | ||||
|           "Examples are: %s"), | ||||
|         "“Make windows visible on click”, “Block advertisement popups”, “Animate windows shrinking on minimize”\n"); | ||||
|       g_print ("%s: ", _("Description")); | ||||
|  | ||||
|       line = g_data_input_stream_read_line_utf8 (istream, NULL, NULL, NULL); | ||||
|       while (line == NULL) | ||||
|         { | ||||
|           g_print ("%s: ", _("Description")); | ||||
|  | ||||
|           line = g_data_input_stream_read_line_utf8 (istream, NULL, NULL, NULL); | ||||
|         } | ||||
|       *description = g_strdelimit (line, "\n", '\0'); | ||||
|  | ||||
|       g_print ("\n"); | ||||
|     } | ||||
|  | ||||
|   if (uuid != NULL && *uuid == NULL) | ||||
|     { | ||||
|       char *line; | ||||
|       char *line = NULL; | ||||
|  | ||||
|       g_print ( | ||||
|         _("UUID is a globally-unique identifier for your extension.\n" | ||||
|           "This should be in the format of an email address (clicktofocus@janedoe.example.com)\n")); | ||||
|       g_print ("UUID: "); | ||||
|  | ||||
|       line = g_data_input_stream_read_line_utf8 (istream, NULL, NULL, NULL); | ||||
|       while (line == NULL) | ||||
|         { | ||||
|           g_print ("UUID: "); | ||||
|  | ||||
|           line = g_data_input_stream_read_line_utf8 (istream, NULL, NULL, NULL); | ||||
|         } | ||||
|       *uuid = g_strdelimit (line, "\n", '\0'); | ||||
|  | ||||
|       g_print ("\n"); | ||||
|     } | ||||
|  | ||||
|   if (template != NULL && *template == NULL) | ||||
|     { | ||||
|       g_autoptr (GPtrArray) templates = get_templates (); | ||||
|  | ||||
|       if (templates->len == 1) | ||||
|         { | ||||
|           GDesktopAppInfo *info = g_ptr_array_index (templates, 0); | ||||
|           *template = g_desktop_app_info_get_string (info, TEMPLATE_KEY); | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           int i; | ||||
|  | ||||
|           g_print (_("Choose one of the available templates:\n")); | ||||
|           for (i = 0; i < templates->len; i++) | ||||
|             { | ||||
|               GAppInfo *info = g_ptr_array_index (templates, i); | ||||
|               g_print ("%d) %-10s  –  %s\n", | ||||
|                        i + 1, | ||||
|                        g_app_info_get_name (info), | ||||
|                        g_app_info_get_description (info)); | ||||
|             } | ||||
|  | ||||
|           while (*template == NULL) | ||||
|             { | ||||
|               g_autofree char *line = NULL; | ||||
|  | ||||
|               g_print ("%s [1-%d]: ", _("Template"), templates->len); | ||||
|  | ||||
|               line = g_data_input_stream_read_line_utf8 (istream, NULL, NULL, NULL); | ||||
|  | ||||
|               if (line == NULL) | ||||
|                 continue; | ||||
|  | ||||
|               if (g_ascii_isdigit (*line)) | ||||
|                 { | ||||
|                   long i = strtol (line, NULL, 10); | ||||
|  | ||||
|                   if (i > 0 && i <= templates->len) | ||||
|                     { | ||||
|                       GDesktopAppInfo *info; | ||||
|  | ||||
|                       info = g_ptr_array_index (templates, i - 1); | ||||
|                       *template = | ||||
|                         g_desktop_app_info_get_string (info, TEMPLATE_KEY); | ||||
|                     } | ||||
|                 } | ||||
|               else | ||||
|                 { | ||||
|                   for (i = 0; i < templates->len; i++) | ||||
|                     { | ||||
|                       GDesktopAppInfo *info = g_ptr_array_index (templates, i); | ||||
|                       g_autofree char *cur_template = NULL; | ||||
|  | ||||
|                       cur_template = | ||||
|                         g_desktop_app_info_get_string (info, TEMPLATE_KEY); | ||||
|  | ||||
|                       if (strcasestr (cur_template, line) != NULL) | ||||
|                         *template = g_steal_pointer (&cur_template); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|           g_print ("\n"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -236,7 +401,9 @@ handle_create (int argc, char *argv[], gboolean do_help) | ||||
|   g_autofree char *name = NULL; | ||||
|   g_autofree char *description = NULL; | ||||
|   g_autofree char *uuid = NULL; | ||||
|   g_autofree char *template = NULL; | ||||
|   gboolean interactive = FALSE; | ||||
|   gboolean list_templates = FALSE; | ||||
|   GOptionEntry entries[] = { | ||||
|     { .long_name = "uuid", | ||||
|       .arg = G_OPTION_ARG_STRING, .arg_data = &uuid, | ||||
| @@ -250,6 +417,13 @@ handle_create (int argc, char *argv[], gboolean do_help) | ||||
|       .arg_description = _("DESCRIPTION"), | ||||
|       .arg = G_OPTION_ARG_STRING, .arg_data = &description, | ||||
|       .description = _("A short description of what the extension does") }, | ||||
|     { .long_name = "template", | ||||
|       .arg = G_OPTION_ARG_STRING, .arg_data = &template, | ||||
|       .arg_description = _("TEMPLATE"), | ||||
|       .description = _("The template to use for the new extension") }, | ||||
|     { .long_name = "list-templates", | ||||
|       .arg = G_OPTION_ARG_NONE, .arg_data = &list_templates, | ||||
|       .flags = G_OPTION_FLAG_HIDDEN }, | ||||
|     { .long_name = "interactive", .short_name = 'i', | ||||
|       .arg = G_OPTION_ARG_NONE, .arg_data = &interactive, | ||||
|       .description = _("Enter extension information interactively") }, | ||||
| @@ -262,6 +436,7 @@ handle_create (int argc, char *argv[], gboolean do_help) | ||||
|   g_option_context_set_help_enabled (context, FALSE); | ||||
|   g_option_context_set_summary (context, _("Create a new extension")); | ||||
|   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); | ||||
|   g_option_context_add_group (context, get_option_group ()); | ||||
|  | ||||
|   if (do_help) | ||||
|     { | ||||
| @@ -281,8 +456,24 @@ handle_create (int argc, char *argv[], gboolean do_help) | ||||
|       return 1; | ||||
|     } | ||||
|  | ||||
|   if (list_templates) | ||||
|     { | ||||
|       g_autoptr (GPtrArray) templates = get_templates (); | ||||
|       int i; | ||||
|  | ||||
|       for (i = 0; i < templates->len; i++) | ||||
|         { | ||||
|           GDesktopAppInfo *info = g_ptr_array_index (templates, i); | ||||
|           g_autofree char *template = NULL; | ||||
|  | ||||
|           template = g_desktop_app_info_get_string (info, TEMPLATE_KEY); | ||||
|           g_print ("%s\n", template); | ||||
|         } | ||||
|       return 0; | ||||
|     } | ||||
|  | ||||
|   if (interactive) | ||||
|     prompt_metadata (&uuid, &name, &description); | ||||
|     prompt_metadata (&uuid, &name, &description, &template); | ||||
|  | ||||
|   if (uuid == NULL || name == NULL || description == NULL) | ||||
|     { | ||||
| @@ -290,5 +481,5 @@ handle_create (int argc, char *argv[], gboolean do_help) | ||||
|       return 1; | ||||
|     } | ||||
|  | ||||
|   return create_extension (uuid, name, description) ? 0 : 2; | ||||
|   return create_extension (uuid, name, description, template) ? 0 : 2; | ||||
| } | ||||
|   | ||||
| @@ -26,7 +26,37 @@ | ||||
| #include "config.h" | ||||
|  | ||||
| static gboolean | ||||
| disable_extension (const char *uuid) | ||||
| disable_extension_dbus (GDBusProxy *proxy, | ||||
|                         const char *uuid) | ||||
| { | ||||
|   g_autoptr (GVariant) response = NULL; | ||||
|   g_autoptr (GError) error = NULL; | ||||
|   gboolean success = FALSE; | ||||
|  | ||||
|   response = g_dbus_proxy_call_sync (proxy, | ||||
|                                      "DisableExtension", | ||||
|                                      g_variant_new ("(s)", uuid), | ||||
|                                      0, | ||||
|                                      -1, | ||||
|                                      NULL, | ||||
|                                      &error); | ||||
|  | ||||
|   if (response == NULL) | ||||
|     { | ||||
|       g_printerr (_("Failed to connect to GNOME Shell\n")); | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   g_variant_get (response, "(b)", &success); | ||||
|  | ||||
|   if (!success) | ||||
|     g_printerr (_("Extension “%s” does not exist\n"), uuid); | ||||
|  | ||||
|   return success; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| disable_extension_gsettings (const char *uuid) | ||||
| { | ||||
|   g_autoptr(GSettings) settings = get_shell_settings (); | ||||
|  | ||||
| @@ -37,6 +67,20 @@ disable_extension (const char *uuid) | ||||
|          settings_list_add (settings, "disabled-extensions", uuid); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| disable_extension (const char *uuid) | ||||
| { | ||||
|   g_autoptr (GDBusProxy) proxy = NULL; | ||||
|   g_autoptr (GError) error = NULL; | ||||
|  | ||||
|   proxy = get_shell_proxy (&error); | ||||
|  | ||||
|   if (proxy != NULL) | ||||
|     return disable_extension_dbus (proxy, uuid); | ||||
|   else | ||||
|     return disable_extension_gsettings (uuid); | ||||
| } | ||||
|  | ||||
| int | ||||
| handle_disable (int argc, char *argv[], gboolean do_help) | ||||
| { | ||||
| @@ -56,6 +100,7 @@ handle_disable (int argc, char *argv[], gboolean do_help) | ||||
|   g_option_context_set_help_enabled (context, FALSE); | ||||
|   g_option_context_set_summary (context, _("Disable an extension")); | ||||
|   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); | ||||
|   g_option_context_add_group (context, get_option_group()); | ||||
|  | ||||
|   if (do_help) | ||||
|     { | ||||
|   | ||||
| @@ -26,7 +26,37 @@ | ||||
| #include "config.h" | ||||
|  | ||||
| static gboolean | ||||
| enable_extension (const char *uuid) | ||||
| enable_extension_dbus (GDBusProxy *proxy, | ||||
|                        const char *uuid) | ||||
| { | ||||
|   g_autoptr (GVariant) response = NULL; | ||||
|   g_autoptr (GError) error = NULL; | ||||
|   gboolean success = FALSE; | ||||
|  | ||||
|   response = g_dbus_proxy_call_sync (proxy, | ||||
|                                      "EnableExtension", | ||||
|                                      g_variant_new ("(s)", uuid), | ||||
|                                      0, | ||||
|                                      -1, | ||||
|                                      NULL, | ||||
|                                      &error); | ||||
|  | ||||
|   if (response == NULL) | ||||
|     { | ||||
|       g_printerr (_("Failed to connect to GNOME Shell\n")); | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   g_variant_get (response, "(b)", &success); | ||||
|  | ||||
|   if (!success) | ||||
|     g_printerr (_("Extension “%s” does not exist\n"), uuid); | ||||
|  | ||||
|   return success; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| enable_extension_gsettings (const char *uuid) | ||||
| { | ||||
|   g_autoptr(GSettings) settings = get_shell_settings (); | ||||
|  | ||||
| @@ -37,6 +67,20 @@ enable_extension (const char *uuid) | ||||
|          settings_list_remove (settings, "disabled-extensions", uuid); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| enable_extension (const char *uuid) | ||||
| { | ||||
|   g_autoptr (GDBusProxy) proxy = NULL; | ||||
|   g_autoptr (GError) error = NULL; | ||||
|  | ||||
|   proxy = get_shell_proxy (&error); | ||||
|  | ||||
|   if (proxy != NULL) | ||||
|     return enable_extension_dbus (proxy, uuid); | ||||
|   else | ||||
|     return enable_extension_gsettings (uuid); | ||||
| } | ||||
|  | ||||
| int | ||||
| handle_enable (int argc, char *argv[], gboolean do_help) | ||||
| { | ||||
| @@ -56,6 +100,7 @@ handle_enable (int argc, char *argv[], gboolean do_help) | ||||
|   g_option_context_set_help_enabled (context, FALSE); | ||||
|   g_option_context_set_summary (context, _("Enable an extension")); | ||||
|   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); | ||||
|   g_option_context_add_group (context, get_option_group()); | ||||
|  | ||||
|   if (do_help) | ||||
|     { | ||||
|   | ||||
| @@ -46,13 +46,19 @@ show_extension_info (const char *uuid) | ||||
|                                      NULL, | ||||
|                                      &error); | ||||
|   if (response == NULL) | ||||
|     return FALSE; | ||||
|     { | ||||
|       g_printerr (_("Failed to connect to GNOME Shell\n")); | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   asv = g_variant_get_child_value (response, 0); | ||||
|   info = g_variant_dict_new (asv); | ||||
|  | ||||
|   if (!g_variant_dict_contains (info, "uuid")) | ||||
|     return FALSE; | ||||
|     { | ||||
|       g_printerr (_("Extension “%s” doesn't exist\n"), uuid); | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   print_extension_info (info, DISPLAY_DETAILED); | ||||
|  | ||||
| @@ -78,6 +84,7 @@ handle_info (int argc, char *argv[], gboolean do_help) | ||||
|   g_option_context_set_help_enabled (context, FALSE); | ||||
|   g_option_context_set_summary (context, _("Show extensions info")); | ||||
|   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); | ||||
|   g_option_context_add_group (context, get_option_group()); | ||||
|  | ||||
|   if (do_help) | ||||
|     { | ||||
|   | ||||
| @@ -183,6 +183,7 @@ handle_install (int argc, char *argv[], gboolean do_help) | ||||
|   g_option_context_set_help_enabled (context, FALSE); | ||||
|   g_option_context_set_summary (context, _("Install an extension bundle")); | ||||
|   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); | ||||
|   g_option_context_add_group (context, get_option_group()); | ||||
|  | ||||
|   if (do_help) | ||||
|     { | ||||
|   | ||||
| @@ -60,7 +60,10 @@ list_extensions (ListFilterFlags filter, DisplayFormat format) | ||||
|                                      NULL, | ||||
|                                      &error); | ||||
|   if (response == NULL) | ||||
|     return FALSE; | ||||
|     { | ||||
|       g_printerr (_("Failed to connect to GNOME Shell\n")); | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   extensions = g_variant_get_child_value (response, 0); | ||||
|  | ||||
| @@ -150,6 +153,7 @@ handle_list (int argc, char *argv[], gboolean do_help) | ||||
|   g_option_context_set_help_enabled (context, FALSE); | ||||
|   g_option_context_set_summary (context, _("List installed extensions")); | ||||
|   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); | ||||
|   g_option_context_add_group (context, get_option_group()); | ||||
|  | ||||
|   if (do_help) | ||||
|     { | ||||
|   | ||||
| @@ -480,6 +480,7 @@ handle_pack (int argc, char *argv[], gboolean do_help) | ||||
|   g_option_context_set_help_enabled (context, FALSE); | ||||
|   g_option_context_set_summary (context, _("Create an extension bundle")); | ||||
|   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); | ||||
|   g_option_context_add_group (context, get_option_group()); | ||||
|  | ||||
|   if (do_help) | ||||
|     { | ||||
|   | ||||
| @@ -29,9 +29,7 @@ static gboolean | ||||
| launch_extension_prefs (const char *uuid) | ||||
| { | ||||
|   g_autoptr (GDBusProxy) proxy = NULL; | ||||
|   g_autoptr (GVariant) response = NULL; | ||||
|   g_autoptr (GVariant) asv = NULL; | ||||
|   g_autoptr (GVariantDict) info = NULL; | ||||
|   g_autoptr (GVariant) info = NULL; | ||||
|   g_autoptr (GError) error = NULL; | ||||
|   gboolean has_prefs; | ||||
|  | ||||
| @@ -39,25 +37,16 @@ launch_extension_prefs (const char *uuid) | ||||
|   if (proxy == NULL) | ||||
|     return FALSE; | ||||
|  | ||||
|   response = g_dbus_proxy_call_sync (proxy, | ||||
|                                      "GetExtensionInfo", | ||||
|                                      g_variant_new ("(s)", uuid), | ||||
|                                      0, | ||||
|                                      -1, | ||||
|                                      NULL, | ||||
|                                      &error); | ||||
|   if (response == NULL) | ||||
|   info = get_extension_property (proxy, uuid, "hasPrefs"); | ||||
|   if (info == NULL) | ||||
|     return FALSE; | ||||
|  | ||||
|   asv = g_variant_get_child_value (response, 0); | ||||
|   info = g_variant_dict_new (asv); | ||||
|  | ||||
|   if (!g_variant_dict_contains (info, "uuid")) | ||||
|     return FALSE; | ||||
|  | ||||
|   g_variant_dict_lookup (info, "hasPrefs", "b", &has_prefs); | ||||
|   has_prefs = g_variant_get_boolean (info); | ||||
|   if (!has_prefs) | ||||
|     return FALSE; | ||||
|     { | ||||
|       g_printerr (_("Extension “%s” doesn't have preferences\n"), uuid); | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   g_dbus_proxy_call_sync (proxy, | ||||
|                           "OpenExtensionPrefs", | ||||
| @@ -89,6 +78,7 @@ handle_prefs (int argc, char *argv[], gboolean do_help) | ||||
|   g_option_context_set_help_enabled (context, FALSE); | ||||
|   g_option_context_set_summary (context, _("Opens extension preferences")); | ||||
|   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); | ||||
|   g_option_context_add_group (context, get_option_group()); | ||||
|  | ||||
|   if (do_help) | ||||
|     { | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| /* command-reset.c | ||||
|   g_option_context_add_group (context, get_option_group()); | ||||
|  * | ||||
|  * Copyright 2019 Florian Müllner <fmuellner@gnome.org> | ||||
|  * | ||||
| @@ -56,6 +57,7 @@ handle_reset (int argc, char *argv[], gboolean do_help) | ||||
|   g_option_context_set_help_enabled (context, FALSE); | ||||
|   g_option_context_set_summary (context, _("Reset an extension")); | ||||
|   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); | ||||
|   g_option_context_add_group (context, get_option_group()); | ||||
|  | ||||
|   if (do_help) | ||||
|     { | ||||
|   | ||||
| @@ -29,14 +29,27 @@ static gboolean | ||||
| uninstall_extension (const char *uuid) | ||||
| { | ||||
|   g_autoptr (GDBusProxy) proxy = NULL; | ||||
|   g_autoptr (GVariant) info = NULL; | ||||
|   g_autoptr (GVariant) response = NULL; | ||||
|   g_autoptr (GError) error = NULL; | ||||
|   gboolean success = FALSE; | ||||
|   double type; | ||||
|  | ||||
|   proxy = get_shell_proxy (&error); | ||||
|   if (proxy == NULL) | ||||
|     return FALSE; | ||||
|  | ||||
|   info = get_extension_property (proxy, uuid, "type"); | ||||
|   if (info == NULL) | ||||
|     return FALSE; | ||||
|  | ||||
|   type = g_variant_get_double (info); | ||||
|   if (type == TYPE_SYSTEM) | ||||
|     { | ||||
|       g_printerr (_("Cannot uninstall system extensions\n")); | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   response = g_dbus_proxy_call_sync (proxy, | ||||
|                                      "UninstallExtension", | ||||
|                                      g_variant_new ("(s)", uuid), | ||||
| @@ -44,11 +57,12 @@ uninstall_extension (const char *uuid) | ||||
|                                      -1, | ||||
|                                      NULL, | ||||
|                                      &error); | ||||
|   if (response == NULL) | ||||
|     return FALSE; | ||||
|  | ||||
|   g_variant_get (response, "(b)", &success); | ||||
|  | ||||
|   if (!success) | ||||
|     g_printerr (_("Failed to uninstall “%s”\n"), uuid); | ||||
|  | ||||
|   return success; | ||||
| } | ||||
|  | ||||
| @@ -71,6 +85,7 @@ handle_uninstall (int argc, char *argv[], gboolean do_help) | ||||
|   g_option_context_set_help_enabled (context, FALSE); | ||||
|   g_option_context_set_summary (context, _("Uninstall an extension")); | ||||
|   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); | ||||
|   g_option_context_add_group (context, get_option_group()); | ||||
|  | ||||
|   if (do_help) | ||||
|     { | ||||
|   | ||||
| @@ -45,6 +45,8 @@ typedef enum { | ||||
|   DISPLAY_DETAILED | ||||
| } DisplayFormat; | ||||
|  | ||||
| GOptionGroup *get_option_group (void); | ||||
|  | ||||
| void show_help (GOptionContext *context, | ||||
|                 const char     *message); | ||||
|  | ||||
| @@ -52,6 +54,10 @@ void print_extension_info (GVariantDict  *info, | ||||
|                            DisplayFormat  format); | ||||
|  | ||||
| GDBusProxy *get_shell_proxy (GError **error); | ||||
| GVariant   *get_extension_property (GDBusProxy *proxy, | ||||
|                                     const char *uuid, | ||||
|                                     const char *property); | ||||
|  | ||||
| GSettings  *get_shell_settings (void); | ||||
|  | ||||
| gboolean settings_list_add (GSettings  *settings, | ||||
|   | ||||
| @@ -1,7 +1,11 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <gresources> | ||||
|   <gresource prefix="/org/gnome/extensions-tool"> | ||||
|     <file>template/extension.js</file> | ||||
|     <file>template/stylesheet.css</file> | ||||
|     <file>templates/00-plain.desktop</file> | ||||
|     <file>templates/indicator.desktop</file> | ||||
|     <file>templates/indicator/extension.js</file> | ||||
|     <file>templates/indicator/stylesheet.css</file> | ||||
|     <file>templates/plain/extension.js</file> | ||||
|     <file>templates/plain/stylesheet.css</file> | ||||
|   </gresource> | ||||
| </gresources> | ||||
|   | ||||
| @@ -49,6 +49,39 @@ extension_state_to_string (ExtensionState state) | ||||
|   return "UNKNOWN"; | ||||
| } | ||||
|  | ||||
| static void | ||||
| print_nothing (const char *message) | ||||
| { | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| quiet_cb (const gchar  *option_name, | ||||
|           const gchar  *value, | ||||
|           gpointer      data, | ||||
|           GError      **error) | ||||
| { | ||||
|   g_set_printerr_handler (print_nothing); | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| GOptionGroup * | ||||
| get_option_group () | ||||
| { | ||||
|   GOptionEntry entries[] = { | ||||
|     { .long_name = "quiet", .short_name = 'q', | ||||
|       .description = _("Do not print error messages"), | ||||
|       .arg = G_OPTION_ARG_CALLBACK, .arg_data = &quiet_cb, | ||||
|       .flags = G_OPTION_FLAG_NO_ARG | G_OPTION_FLAG_IN_MAIN }, | ||||
|     { NULL } | ||||
|   }; | ||||
|   GOptionGroup *group; | ||||
|  | ||||
|   group = g_option_group_new ("Common", "common options", "common options", NULL, NULL); | ||||
|   g_option_group_add_entries (group, entries); | ||||
|  | ||||
|   return group; | ||||
| } | ||||
|  | ||||
| void | ||||
| show_help (GOptionContext *context, const char *message) | ||||
| { | ||||
| @@ -91,6 +124,41 @@ get_shell_settings (void) | ||||
|   return g_settings_new_full (schema, NULL, NULL); | ||||
| } | ||||
|  | ||||
| GVariant * | ||||
| get_extension_property (GDBusProxy *proxy, | ||||
|                         const char *uuid, | ||||
|                         const char *property) | ||||
| { | ||||
|   g_autoptr (GVariant) response = NULL; | ||||
|   g_autoptr (GVariant) asv = NULL; | ||||
|   g_autoptr (GVariantDict) info = NULL; | ||||
|   g_autoptr (GError) error = NULL; | ||||
|  | ||||
|   response = g_dbus_proxy_call_sync (proxy, | ||||
|                                      "GetExtensionInfo", | ||||
|                                      g_variant_new ("(s)", uuid), | ||||
|                                      0, | ||||
|                                      -1, | ||||
|                                      NULL, | ||||
|                                      &error); | ||||
|   if (response == NULL) | ||||
|     { | ||||
|       g_printerr (_("Failed to connect to GNOME Shell")); | ||||
|       return NULL; | ||||
|     } | ||||
|  | ||||
|   asv = g_variant_get_child_value (response, 0); | ||||
|   info = g_variant_dict_new (asv); | ||||
|  | ||||
|   if (!g_variant_dict_contains (info, "uuid")) | ||||
|     { | ||||
|       g_printerr (_("Extension “%s” doesn't exist\n"), uuid); | ||||
|       return NULL; | ||||
|     } | ||||
|  | ||||
|   return g_variant_dict_lookup_value (info, property, NULL); | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| settings_list_add (GSettings  *settings, | ||||
|                    const char *key, | ||||
|   | ||||
| @@ -22,9 +22,12 @@ sources = [ | ||||
|   'main.c' | ||||
| ] | ||||
|  | ||||
| subdir('templates') | ||||
|  | ||||
| resources = gnome.compile_resources('resources', | ||||
|   'gnome-extensions-tool.gresource.xml', | ||||
|   source_dir: '.' | ||||
|   source_dir: ['.', meson.current_build_dir()], | ||||
|   dependencies: template_deps, | ||||
| ) | ||||
|  | ||||
| executable('gnome-extensions', | ||||
|   | ||||
| @@ -0,0 +1,5 @@ | ||||
| [Desktop Entry] | ||||
| Type=Application | ||||
| Name=Plain | ||||
| Comment=An empty extension | ||||
| Path=plain | ||||
| @@ -0,0 +1,5 @@ | ||||
| [Desktop Entry] | ||||
| Type=Application | ||||
| Name=Indicator | ||||
| Comment=Add an icon to the top bar | ||||
| Path=indicator | ||||
| @@ -0,0 +1,74 @@ | ||||
| /* extension.js | ||||
|  * | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  */ | ||||
|  | ||||
| /* exported init */ | ||||
|  | ||||
| const GETTEXT_DOMAIN = 'my-indicator-extension'; | ||||
|  | ||||
| const { GObject, St } = imports.gi; | ||||
|  | ||||
| const Gettext = imports.gettext.domain(GETTEXT_DOMAIN); | ||||
| const _ = Gettext.gettext; | ||||
|  | ||||
| const ExtensionUtils = imports.misc.extensionUtils; | ||||
| const Main = imports.ui.main; | ||||
| const PanelMenu = imports.ui.panelMenu; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
|  | ||||
| const Indicator = GObject.registerClass( | ||||
| class Indicator extends PanelMenu.Button { | ||||
|     _init() { | ||||
|         super._init(0.0, _('My Shiny Indicator')); | ||||
|  | ||||
|         let box = new St.BoxLayout({ style_class: 'panel-status-menu-box' }); | ||||
|         box.add_child(new St.Icon({ | ||||
|             icon_name: 'face-smile-symbolic', | ||||
|             style_class: 'system-status-icon', | ||||
|         })); | ||||
|         box.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM)); | ||||
|         this.add_child(box); | ||||
|  | ||||
|         let item = new PopupMenu.PopupMenuItem(_('Show Notification')); | ||||
|         item.connect('activate', () => { | ||||
|             Main.notify(_('Whatʼs up, folks?')); | ||||
|         }); | ||||
|         this.menu.addMenuItem(item); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| class Extension { | ||||
|     constructor(uuid) { | ||||
|         this._uuid = uuid; | ||||
|  | ||||
|         ExtensionUtils.initTranslations(GETTEXT_DOMAIN); | ||||
|     } | ||||
|  | ||||
|     enable() { | ||||
|         this._indicator = new Indicator(); | ||||
|         Main.panel.addToStatusArea(this._uuid, this._indicator); | ||||
|     } | ||||
|  | ||||
|     disable() { | ||||
|         this._indicator.destroy(); | ||||
|         this._indicator = null; | ||||
|     } | ||||
| } | ||||
|  | ||||
| function init(meta) { | ||||
|     return new Extension(meta.uuid); | ||||
| } | ||||
							
								
								
									
										13
									
								
								subprojects/extensions-tool/src/templates/meson.build
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								subprojects/extensions-tool/src/templates/meson.build
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| template_metas = [ | ||||
|   '00-plain.desktop', | ||||
|   'indicator.desktop', | ||||
| ] | ||||
| template_deps = [] | ||||
| foreach template : template_metas | ||||
|   template_deps += i18n.merge_file(template, | ||||
|     input: template + '.in', | ||||
|     output: template, | ||||
|     po_dir: po_dir, | ||||
|     type: 'desktop', | ||||
|   ) | ||||
| endforeach | ||||
| @@ -0,0 +1 @@ | ||||
| /* Add your custom extension styling here */ | ||||
| @@ -1,6 +1,6 @@ | ||||
| project('shew', 'c', | ||||
|   version: '3.37.0', | ||||
|   meson_version: '>= 0.47.0', | ||||
|   meson_version: '>= 0.53.0', | ||||
|   license: 'LGPLv2+', | ||||
| ) | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user