Compare commits
	
		
			8 Commits
		
	
	
		
			wip/media-
			...
			screen-shi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 6e448a2711 | ||
|   | 1d484e2278 | ||
|   | 0b7ca098ad | ||
|   | 5cb9aa9cf3 | ||
|   | 0171e561f2 | ||
|   | 82f7431a28 | ||
|   | 29958df7e7 | ||
|   | 2f990346df | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -16,7 +16,6 @@ config.log | ||||
| config.status | ||||
| config | ||||
| configure | ||||
| data/50-gnome-shell-*.xml | ||||
| data/gnome-shell.desktop | ||||
| data/gnome-shell.desktop.in | ||||
| data/gnome-shell-extension-prefs.desktop | ||||
| @@ -45,7 +44,6 @@ intltool-merge.in | ||||
| intltool-update.in | ||||
| libtool | ||||
| m4/ | ||||
| man/gnome-shell.1 | ||||
| omf.make | ||||
| po/*.gmo | ||||
| po/gnome-shell.pot | ||||
|   | ||||
							
								
								
									
										331
									
								
								HACKING
									
									
									
									
									
								
							
							
						
						
									
										331
									
								
								HACKING
									
									
									
									
									
								
							| @@ -1,331 +0,0 @@ | ||||
| Coding guide | ||||
| ============ | ||||
|  | ||||
| Our goal is to have all JavaScript code in GNOME follow a consistent style. In | ||||
| a dynamic language like JavaScript, it is essential to be rigorous about style | ||||
| (and unit tests), or you rapidly end up with a spaghetti-code mess. | ||||
|  | ||||
| A quick note | ||||
| ------------ | ||||
|  | ||||
| Life isn't fun if you can't break the rules. If a rule seems unnecessarily | ||||
| restrictive while you're coding, ignore it, and let the patch reviewer decide | ||||
| what to do. | ||||
|  | ||||
| Indentation and whitespace | ||||
| -------------------------- | ||||
|  | ||||
| Use four-space indents. Braces are on the same line as their associated | ||||
| statements.  You should only omit braces if *both* sides of the statement are | ||||
| on one line. | ||||
|  | ||||
| * One space after the `function` keyword.  No space between the function name | ||||
| * in a declaration or a call.  One space before the parens in the `if` | ||||
| * statements, or `while`, or `for` loops. | ||||
|  | ||||
|     function foo(a, b) { | ||||
|         let bar; | ||||
|  | ||||
|         if (a > b) | ||||
|             bar = do_thing(a); | ||||
|         else | ||||
|             bar = do_thing(b); | ||||
|  | ||||
|         if (var == 5) { | ||||
|             for (let i = 0; i < 10; i++) { | ||||
|                 print(i); | ||||
|             } | ||||
|         } else { | ||||
|             print(20); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| Semicolons | ||||
| ---------- | ||||
|  | ||||
| JavaScript allows omitting semicolons at the end of lines, but don't. Always | ||||
| end statements with a semicolon. | ||||
|  | ||||
| js2-mode | ||||
| -------- | ||||
|  | ||||
| If using Emacs, do not use js2-mode. It is outdated and hasn't worked for a | ||||
| while. emacs now has a built-in JavaScript mode, js-mode, based on | ||||
| espresso-mode. It is the de facto emacs mode for JavaScript. | ||||
|  | ||||
| File naming and creation | ||||
| ------------------------ | ||||
|  | ||||
| For JavaScript files, use lowerCamelCase-style names, with a `.js` extension. | ||||
|  | ||||
| We only use C where gjs/gobject-introspection is not available for the task, or | ||||
| where C would be cleaner. To work around limitations in | ||||
| gjs/gobject-introspection itself, add a new method in `shell-util.[ch]`. | ||||
|  | ||||
| Like many other GNOME projects, we prefix our C source filenames with the | ||||
| library name followed by a dash, e.g. `shell-app-system.c`. Create a | ||||
| `-private.h` header when you want to share code internally in the | ||||
| library. These headers are not installed, distributed or introspected. | ||||
|  | ||||
| Imports | ||||
| ------- | ||||
|  | ||||
| Use UpperCamelCase when importing modules to distinguish them from ordinary | ||||
| variables, e.g. | ||||
|  | ||||
|     const GLib = imports.gi.GLib; | ||||
|  | ||||
| Imports should be categorized into one of two places. The top-most import block | ||||
| should contain only "environment imports". These are either modules from | ||||
| gobject-introspection or modules added by gjs itself. | ||||
|  | ||||
| The second block of imports should contain only "application imports". These | ||||
| are the JS code that is in the gnome-shell codebase, | ||||
| e.g. `imports.ui.popupMenu`. | ||||
|  | ||||
| Each import block should be sorted alphabetically. Don't import modules you | ||||
| don't use. | ||||
|  | ||||
|     const GLib = imports.gi.GLib; | ||||
|     const Gio = imports.gi.Gio; | ||||
|     const Lang = imports.lang; | ||||
|     const St = imports.gi.St; | ||||
|  | ||||
|     const Main = imports.ui.main; | ||||
|     const Params = imports.misc.params; | ||||
|     const Tweener = imports.ui.tweener; | ||||
|     const Util = imports.misc.util; | ||||
|  | ||||
| The alphabetical ordering should be done independently of the location of the | ||||
| location. Never reference `imports` in actual code. | ||||
|  | ||||
| Constants | ||||
| --------- | ||||
|  | ||||
| We use CONSTANTS_CASE to define constants. All constants should be directly | ||||
| under the imports: | ||||
|  | ||||
|     const MY_DBUS_INTERFACE = 'org.my.Interface'; | ||||
|  | ||||
| Variable declaration | ||||
| -------------------- | ||||
|  | ||||
| Always use either `const` or `let` when defining a variable. | ||||
|  | ||||
|     // Iterating over an array | ||||
|     for (let i = 0; i < arr.length; ++i) { | ||||
|         let item = arr[i]; | ||||
|     } | ||||
|  | ||||
|     // Iterating over an object's properties | ||||
|     for (let prop in someobj) { | ||||
|         ... | ||||
|     } | ||||
|  | ||||
| If you use "var" then the variable is added to function scope, not block scope. | ||||
| See [What's new in JavaScript 1.7](https://developer.mozilla.org/en/JavaScript/New_in_JavaScript/1.7#Block_scope_with_let_%28Merge_into_let_Statement%29) | ||||
|  | ||||
| Classes | ||||
| ------- | ||||
|  | ||||
| There are many approaches to classes in JavaScript. We use our own class framework | ||||
| (sigh), which is built in gjs. The advantage is that it supports inheriting from | ||||
| GObjects, although this feature isn't used very often in the Shell itself. | ||||
|  | ||||
|     const IconLabelMenuItem = new Lang.Class({ | ||||
|         Name: 'IconLabelMenuItem', | ||||
|         Extends: PopupMenu.PopupMenuBaseItem, | ||||
|  | ||||
|         _init: function(icon, label) { | ||||
|             this.parent({ reactive: false }); | ||||
|             this.addActor(icon); | ||||
|             this.addActor(label); | ||||
|         }, | ||||
|  | ||||
|         open: function() { | ||||
|             log("menu opened!"); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
| * 'Name' is required. 'Extends' is optional. If you leave it out, you will | ||||
|   automatically inherit from Object. | ||||
|  | ||||
| * Leave a blank line between the "class header" (Name, Extends, and other | ||||
|   things)  and the "class body" (methods). Leave a blank line between each | ||||
|   method. | ||||
|  | ||||
| * No space before the colon, one space after. | ||||
|  | ||||
| * No trailing comma after the last item. | ||||
|  | ||||
| * Make sure to use a semicolon after the closing paren to the class. It's | ||||
|   still a giant function call, even though it may resemble a more | ||||
|   conventional syntax. | ||||
|  | ||||
| GObject Introspection | ||||
| --------------------- | ||||
|  | ||||
| GObject Introspection is a powerful feature that allows us to have native | ||||
| bindings for almost any library built around GObject. If a library requires | ||||
| you to inherit from a type to use it, you can do so: | ||||
|  | ||||
|     const MyClutterActor = new Lang.Class({ | ||||
|         Name: 'MyClutterActor', | ||||
|         Extends: Clutter.Actor, | ||||
|  | ||||
|         vfunc_get_preferred_width: function(actor, forHeight) { | ||||
|              return [100, 100]; | ||||
|         }, | ||||
|  | ||||
|         vfunc_get_preferred_height: function(actor, forWidth) { | ||||
|              return [100, 100]; | ||||
|         }, | ||||
|  | ||||
|         vfunc_paint: function(actor) { | ||||
|              let alloc = this.get_allocation_box(); | ||||
|              Cogl.set_source_color4ub(255, 0, 0, 255); | ||||
|              Cogl.rectangle(alloc.x1, alloc.y1, | ||||
|                             alloc.x2, alloc.y2); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
| Translatable strings, `environment.js` | ||||
| -------------------------------------- | ||||
|  | ||||
| We use gettext to translate the GNOME Shell into all the languages that GNOME | ||||
| supports. The `gettext` function is aliased globally as `_`, you do not need to | ||||
| explicitly import it. This is done through some magic in the | ||||
| [environment.js](http://git.gnome.org/browse/gnome-shell/tree/js/ui/environment.js) | ||||
| file. If you can't find a method that's used, it's probably either in gjs itself | ||||
| or installed on the global object from the Environment. | ||||
|  | ||||
| Use 'single quotes' for programming strings that should not be translated | ||||
| and "double quotes" for strings that the user may see. This allows us to | ||||
| quickly find untranslated or mistranslated strings by grepping through the | ||||
| sources for double quotes without a gettext call around them. | ||||
|  | ||||
| `actor` and `_delegate` | ||||
| ----------------------- | ||||
|  | ||||
| gjs allows us to set so-called "expando properties" on introspected objects, | ||||
| allowing us to treat them like any other. Because the Shell was built before | ||||
| you could inherit from GTypes natively in JS, we usually have a wrapper class | ||||
| that has a property called `actor`. We call this wrapper class the "delegate". | ||||
|  | ||||
| We sometimes use expando properties to set a property called `_delegate` on | ||||
| the actor itself: | ||||
|  | ||||
|     const MyClass = new Lang.Class({ | ||||
|         Name: 'MyClass', | ||||
|  | ||||
|         _init: function() { | ||||
|             this.actor = new St.Button({ text: "This is a button" }); | ||||
|             this.actor._delegate = this; | ||||
|  | ||||
|             this.actor.connect('clicked', Lang.bind(this, this._onClicked)); | ||||
|         }, | ||||
|  | ||||
|         _onClicked: function(actor) { | ||||
|             actor.set_label("You clicked the button!"); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
| The 'delegate' property is important for anything which trying to get the | ||||
| delegate object from an associated actor. For instance, the drag and drop | ||||
| system calls the `handleDragOver` function on the delegate of a "drop target" | ||||
| when the user drags an item over it. If you do not set the `_delegate` | ||||
| property, your actor will not be able to be dropped onto. | ||||
|  | ||||
| Functional style | ||||
| ---------------- | ||||
|  | ||||
| JavaScript Array objects offer a lot of common functional programming | ||||
| capabilities such as forEach, map, filter and so on. You can use these when | ||||
| they make sense, but please don't have a spaghetti mess of function programming | ||||
| messed in a procedural style. Use your best judgment. | ||||
|  | ||||
| Closures | ||||
| -------- | ||||
|  | ||||
| `this` will not be captured in a closure, it is relative to how the closure is | ||||
| invoked, not to the value of this where the closure is created, because "this" | ||||
| is a keyword with a value passed in at function invocation time, it is not a | ||||
| variable that can be captured in closures. | ||||
|  | ||||
| All closures should be wrapped with a Lang.bind. | ||||
|  | ||||
|     const Lang = imports.lang; | ||||
|  | ||||
|     let closure = Lang.bind(this, function() { this._fnorbate(); }); | ||||
|  | ||||
| A more realistic example would be connecting to a signal on a method of a | ||||
| prototype: | ||||
|  | ||||
|     const Lang = imports.lang; | ||||
|     const FnorbLib = imports.fborbLib; | ||||
|  | ||||
|     const MyClass = new Lang.Class({ | ||||
|         _init: function() { | ||||
|             let fnorb = new FnorbLib.Fnorb(); | ||||
|             fnorb.connect('frobate', Lang.bind(this, this._onFnorbFrobate)); | ||||
|         }, | ||||
|  | ||||
|         _onFnorbFrobate: function(fnorb) { | ||||
|             this._updateFnorb(); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
| Object literal syntax | ||||
| --------------------- | ||||
|  | ||||
| In JavaScript, these are equivalent: | ||||
|  | ||||
|     foo = { 'bar': 42 }; | ||||
|     foo = { bar: 42 }; | ||||
|  | ||||
| and so are these: | ||||
|  | ||||
|     var b = foo['bar']; | ||||
|     var b = foo.bar; | ||||
|  | ||||
| If your usage of an object is like an object, then you're defining "member | ||||
| variables." For member variables, use the no-quotes no-brackets syntax: `{ bar: | ||||
| 42 }` `foo.bar`. | ||||
|  | ||||
| If your usage of an object is like a hash table (and thus conceptually the keys | ||||
| can have special chars in them), don't use quotes, but use brackets: `{ bar: 42 | ||||
| }`, `foo['bar']`. | ||||
|  | ||||
| Getters, setters, and Tweener | ||||
| ----------------------------- | ||||
|  | ||||
| Getters and setters should be used when you are dealing with an API that is | ||||
| designed around setting properties, like Tweener. If you want to animate an | ||||
| arbitrary property, create a getter and setter, and use Tweener to animate the | ||||
| property. | ||||
|  | ||||
|     const ANIMATION_TIME = 2000; | ||||
|  | ||||
|     const MyClass = new Lang.Class({ | ||||
|         Name: 'MyClass', | ||||
|  | ||||
|         _init: function() { | ||||
|             this.actor = new St.BoxLayout(); | ||||
|             this._position = 0; | ||||
|         }, | ||||
|  | ||||
|         get position() { | ||||
|             return this._position; | ||||
|         }, | ||||
|  | ||||
|         set position(value) { | ||||
|             this._position = value; | ||||
|             this.actor.set_position(value, value); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     let myThing = new MyClass(); | ||||
|     Tweener.addTween(myThing, | ||||
|                      { position: 100, | ||||
|                        time: ANIMATION_TIME, | ||||
|                        transition: 'easeOutQuad' }); | ||||
| @@ -1,11 +1,7 @@ | ||||
| # Point to our macro directory and pick up user flags from the environment | ||||
| ACLOCAL_AMFLAGS  = -I m4 ${ACLOCAL_FLAGS} | ||||
|  | ||||
| SUBDIRS = data js src browser-plugin tests po docs | ||||
|  | ||||
| if ENABLE_MAN | ||||
| SUBDIRS += man | ||||
| endif | ||||
| SUBDIRS = data js src browser-plugin tests po man docs | ||||
|  | ||||
| EXTRA_DIST =		\ | ||||
| 	.project	\ | ||||
| @@ -17,7 +13,6 @@ EXTRA_DIST =		\ | ||||
| DIST_EXCLUDE =					\ | ||||
| 	.gitignore				\ | ||||
| 	gnome-shell.doap			\ | ||||
| 	HACKING					\ | ||||
| 	MAINTAINERS				\ | ||||
| 	tools/build/* | ||||
|  | ||||
| @@ -25,4 +20,4 @@ distcheck-hook: | ||||
| 	@echo "Checking disted files against files in git" | ||||
| 	@$(srcdir)/tools/check-for-missing.py $(srcdir) $(distdir) $(DIST_EXCLUDE) | ||||
|  | ||||
| DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc --enable-man | ||||
| DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc | ||||
|   | ||||
							
								
								
									
										324
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										324
									
								
								NEWS
									
									
									
									
									
								
							| @@ -1,327 +1,3 @@ | ||||
| 3.7.1 | ||||
| ===== | ||||
| * Add shortcut to open application view directly [Jeremy; #685738] | ||||
| * Expose '<Super>F10' shortcut in System Settings [Florian; #672909] | ||||
| * Clean up timestamp format in chat notifications [Carlos; #680989] | ||||
| * loginScreen: Add support for 'disable-restart-buttons' [Florian; #686247] | ||||
| * Update textures automatically on file changes [Florian; #679268] | ||||
| * Implement org.gnome.ScreenSaver.GetActiveTime [Giovanni; #686064] | ||||
| * Add missing translations for GSetting schema [Giovanni; #686413] | ||||
| * Hide workspace switcher completely when it's not necessary [Seif; #686483] | ||||
| * Explicitly load gnome-screensaver when not running GDM [Tim; #683060] | ||||
| * Port to GnomeIdleMonitor [Jasper; #682224] | ||||
| * Set Empathy as preferred handler when delegating channels [Xavier; #686296] | ||||
| * Allow testing GDM login dialog from the session [Giovanni; #683725] | ||||
| * Use all available space for windows in window picker [Jasper, Pierre-Eric; | ||||
|   #582650] | ||||
| * Use logind for suspend if available [Florian; #686482] | ||||
| * Misc. fixes and cleanups [Jasper, Florian, Adel, Rui; #677426, #680426, | ||||
|   #686233, #686241, #686318, #686240, #686484, #686002, #684650, #686487] | ||||
|  | ||||
| Contributors: | ||||
|   Jeremy Bicha, Giovanni Campagna, Xavier Claessens, Adel Gadllah, Seif Lotfy, | ||||
|   Tim Lunn, Rui Matos, Florian Müllner, Pierre-Eric Pelloux-Prayer, | ||||
|   Carlos Soriano, Jasper St. Pierre | ||||
|  | ||||
| Translations: | ||||
|   Andika Triwidada [id], Matej Urbančič [sl], Ihar Hrachyshka [be], | ||||
|   Daniel Mustieles [es], Fran Diéguez [gl], Takayuki KUSANO [ja], | ||||
|   Мирослав Николић [sr, sr@latin], Dušan Kazik [sk], Tom Tryfonidis [el] | ||||
|  | ||||
| 3.6.1 | ||||
| ===== | ||||
| * dash: Make padding even on the top/bottom of the dash [Jasper; #684619] | ||||
| * Fix a crash when dragging search results [Jasper; #684888] | ||||
| * workspaceThumbnail: Fix dragging with static workspaces [Jasper; #684641] | ||||
| * Really hide 'Show Keyboard Layout' on the lock screen [Matthias] | ||||
| * Misc. improvements to jhbuild setup [Owen; #685352, #685353, #685354, #685355] | ||||
| * Show message tray in Ctrl+Alt+Tab outside of the overview [Jasper, Florian; | ||||
|   #684633, #685914] | ||||
| * Disable hotplug sniffer on remote filesystems [Jasper; #684093] | ||||
| * userMenu: Remove 'Switch Session' item [Florian; #685062] | ||||
| * unlockDialog: Make prompt entry insensitive while logging in [Jasper; #685444] | ||||
| * messageTray: Don't animate desktop clone for failed grabs [Jasper; #685342] | ||||
| * Fix crash on dragging windows between workspaces [Ryan; #681399] | ||||
| * userMenu: Ignore 'lock-enabled' setting for user switching [Florian; #685536] | ||||
| * gdm: Fix key-focus on first user [Adel; #684650] | ||||
| * Make grid button insensitive when dragging non-favorites [Jasper; #685313] | ||||
| * Calendar: hide all actions when on the login screen [Matthias; #685142] | ||||
| * Adapt unlock dialog layout for the login screen [Florian; #685201] | ||||
| * Make focus-follows-mouse work better with Shell UI [Florian; #678169] | ||||
| * Improve look of screen shield [Jasper; #685919] | ||||
| * Fix keynav in the login screen [Florian; #684730] | ||||
| * dateMenu: Hide "Open Calendar" item if calendar unavailable [Florian; #686050] | ||||
| * unlockDialog: Reset UI on verification failure [Giovanni; #685441] | ||||
| * Show unlock dialog on primary monitor when using keynav [Giovanni; #685855] | ||||
| * Fix height changes of entries when entering text [Florian; #685534] | ||||
| * Fix show-apps label after successful drags [Florian; #684627] | ||||
| * Misc. bugfixes and cleanups [Jasper, Olivier, Florian, Owen, Adel, Tanner, Tim, Matthias; #685434, #685511, #685466, #685341, #685156, #681159, #673189, #686016, 684869, #686079, #686063 | ||||
|  | ||||
| Contributors: | ||||
| Jasper St. Pierre | ||||
| Matthias Clasen | ||||
| Owen Taylor | ||||
| Olivier Blin | ||||
| Florian Müllner | ||||
| Ryan Lortie | ||||
| Adel Gadllah | ||||
| Tanner Doshier | ||||
| Tim Lunn | ||||
| Giovanni Campagna | ||||
|  | ||||
| Translations: | ||||
|   Tobias Endrigkeit [de], Rudolfs Mazurs [lv], Ask H. Larsen [da], | ||||
|   Shankar Prasad [kn], Changwoo Ryu [ko], Chris Leonard [en_GB], | ||||
|   Arash Mousavi [fa], Theppitak Karoonboonyanan [th], Seán de Búrca [ga], | ||||
|   Yaron Shahrabani [he], Alexander Shopov [bg], Žygimantas Beručka [lt], | ||||
|   Milo Casagrande [it], Kjartan Maraas [nb], Kris Thomsen [da], | ||||
|   Aurimas Černius [lt], Yuri Myasoedov [ru], Мирослав Николић [sr], | ||||
|   Marek Černocký [cs], Gabor Kelemen [hu], Ihar Hrachyshka [be], | ||||
|   Chao-Hsiung Liao [zh_HK, zh_TW], Eleanor Chen [zh_CN], | ||||
|   Carles Ferrando [ca@valencia], Vicent Cubells [ca], Daniel Korostil [uk], | ||||
|   Alexandre Franke [fr], Piotr Drąg [pl] | ||||
|  | ||||
| 3.6.0 | ||||
| ===== | ||||
| * keyboard: Make input source items accessible [Florian; #684462] | ||||
| * Don't show network dialogs in the lock screen [Giovanni; #684384] | ||||
| * popupMenu: Fix initial visibility of settings items [Florian; #684473] | ||||
| * userMenu: Close menu immediately on user/session switch [Florian; #684459] | ||||
| * Fix alignment of search section headers in RTL locales [Florian; #684379] | ||||
| * screenShield: Fix unlock animation [Florian; #684591] | ||||
| * Don't open the tray from a dwell while in a modal grab [Jasper; #684458] | ||||
| * userMenu: Fix texture updates on icon changes [Florian; #679268] | ||||
| * Fix a11y support in the login screen [Florian, Ray; #684727, #684728, #684748] | ||||
| * Make On-Screen-Keyboard usable with new message tray [Giovanni, Florian; | ||||
|   #683546] | ||||
| * Fix initial visibility of input volume in lock-screen [Florian; #684611] | ||||
|  | ||||
| Contributors: | ||||
|   Giovanni Campagna, Florian Müllner, Jasper St. Pierre, Ray Strode | ||||
|  | ||||
| Translations: | ||||
|   Matej Urbančič [sl], Dr.T.Vasudevan [ta], Piotr Drąg [pl], A S Alam [pa], | ||||
|   Alexander Shopov [bg], Nilamdyuti Goswami [as], Chandan Kumar [hi], | ||||
|   Khaled Hosny [ar], Ibrahim Saed [ar], Sandeep Sheshrao Shedmake [mr], | ||||
|   Tom Tryfonidis [el], Theppitak Karoonboonyanan [th], Alexandre Franke [fr], | ||||
|   Fran Diéguez [gl], Gabor Kelemen [hu], Ani Peter [ml], Daniel Mustieles [es], | ||||
|   Мирослав Николић [sr, sr@latin], Duarte Loreto [pt], ManojKumar Giri [or], | ||||
|   Ihar Hrachyshka [be], Aurimas Černius [lt], Djavan Fagundes [pt_BR], | ||||
|   Changwoo Ryu [ko], Bruce Cowan [en_GB], Kris Thomsen [da], Gil Forcada [ca], | ||||
|   Yaron Shahrabani [he], Milo Casagrande [it], Ville-Pekka Vainio [fi], | ||||
|   YunQiang Su [zh_CN], Carles Ferrando [ca@valencia], Mario Blättermann [de], | ||||
|   Rajesh Ranjan [hi], Yuri Myasoedov [ru], Rūdolfs Mazurs [lv], | ||||
|   Jiro Matsuzawa [ja], Mattias Põldaru [et], Timur Zhamakeev [ky], | ||||
|   Petr Kovar [cs], Chao-Hsiung Liao [zh_HK,zh_TW], Andika Triwidada [id] | ||||
|  | ||||
| 3.5.92 | ||||
| ====== | ||||
| * Login/UnlockDialog: Don't reset immediately if auth fails [Giovanni; #682544] | ||||
| * Allow changing session mode at runtime [Jasper, Giovanni; #683156] | ||||
| * Add zoom out animation on login [Jasper; #683170] | ||||
| * Bluetooth: don't restrict the length of non numeric PINs [Giovanni; #683356] | ||||
| * Force chat notification to stay open when focusing entry [Debarshi; #682236] | ||||
| * Make sure the screen is fully locked before suspending [Giovanni; #683448] | ||||
| * st-texture-cache: Fix a case of distorted textures [Florian; #683483] | ||||
| * popupSubMenu: Fix padding for non-scrolled submenus [Florian; #683009] | ||||
| * popupMenu: Fix width changes on submenu open/close [Florian; #683485] | ||||
| * boxpointer: Avoid malformed boxpointer arrow [Debarshi; #680077] | ||||
| * Change stage background color to grey [Adel; #683514] | ||||
| * messageTray: Update style of summary counters [Debarshi; #682891] | ||||
| * Don't fail if a legacy tray icon has no WM_CLASS [Giovanni; #683724] | ||||
| * PolkitAgent: Fix a crash if there is no avatar [Giovanni; #683707] | ||||
| * Hide the a11y menu in the lock screen, but show it in the login screen | ||||
|   [Giovanni; #682542] | ||||
| * Fix show-apps button dropping off the dash [Florian; #683340] | ||||
| * Fix committing strings to shell entries from input method [Florian; #658325] | ||||
| * Make IBus display strings consistent with control-center [Rui; #683124] | ||||
| * Fix missing short codes for some input sources [Rui; #683613] | ||||
| * Remove support for long-press from entry context menus [Jasper; #683509] | ||||
| * screenShield: Add box-shadow to the shield [Florian] | ||||
| * Don't show a right-click menu for the hotplug source [Jasper; #683438] | ||||
| * Fix extension styling [Giovanni; #682128] | ||||
| * Fix on-screen keyboard not working with system-modal dialogs | ||||
|   [Giovanni; #664309] | ||||
| * Fix insensitive styling for popup menu items [Giovanni; #683988] | ||||
| * Disable the message tray dwell when the user is interacting [Owen; #683811] | ||||
| * Animate going from the unlock dialog to the lock screen [Giovanni; #681143] | ||||
| * Autostart fprintd when necessary [Ray; #683131] | ||||
| * UnlockDialog: Allow typing before the first PAM question [Giovanni; #681576] | ||||
| * Make Return key dismiss screenshield [Ray; #683889] | ||||
| * Fix keyboard navigation in the message tray [Florian; #682243] | ||||
| * Remove the places & devices search provider [Giovanni; #683506] | ||||
| * Enable hot corner while the message tray is up [Florian; #682255] | ||||
| * Port screen recorder to new GStreamer vp8enc API [Adel; #684206] | ||||
| * Fix fish flickering [Giovanni; #684154] | ||||
| * Fix extension ordering with !important [Jasper; #684163] | ||||
| * Allow the shell to run without the screenshield [Giovanni; #683060] | ||||
| * Add menu items for IBus Anthy's InputMode, TypingMode [Rui; #682314] | ||||
| * Improve transition to the login dialog [Jasper; #682428] | ||||
| * Keep unlock dialog around until shield animation ends [Florian; #684342] | ||||
| * Expose shell keybindings in System Settings [Florian; #671010] | ||||
| * Misc. bugfixes and cleanups [Debarshi, Florian, Giovanni, Jasper, Rico, Rui; | ||||
|   #672790, #677434, #683305, #683357, #683369, #683377, #683378, #683400, | ||||
|   #683449, #683472, #683482, #683487, #683488, #683526, #683529, #683546, | ||||
|   #683583, #683628, #683705, #683982, #683989, #684035, #684036, #684040, | ||||
|   #684162, #684214, #684343] | ||||
|  | ||||
| Contributors: | ||||
|   Giovanni Campagna, Adel Gadllah, Rui Matos, Florian Müllner, Debarshi Ray, | ||||
|   Jasper St. Pierre, Ray Strode, Owen Taylor, Rico Tzschichholz | ||||
|  | ||||
| Translations: | ||||
|   Gabor Kelemen [hu], Piotr Drąg [pl], Khaled Hosny [ar], | ||||
|   Мирослав Николић [sr, sr@latin], Chao-Hsiung Liao [zh_HK, zh_TW], | ||||
|   Bruce Cowan [en_GB], Dirgita [id], Tom Tryfonidis [el], Timo Jyrinki [fi], | ||||
|   Adorilson Bezerra [pt_BR], Arash Mousavi [fa], Matej Urbančič [sl], | ||||
|   Christian Kirbach [de], Yaron Shahrabani [he], Ihar Hrachyshka [be], | ||||
|   Changwoo Ryu [ko], Duarte Loreto [pt], Theppitak Karoonboonyanan [th], | ||||
|   Nilamdyuti Goswami [as], Sandeep Sheshrao Shedmake [mr], | ||||
|   Alexandre Franke [fr], Ivaylo Valkov [bg], tuhaihe [zh_CN], | ||||
|   Yuri Myasoedov [ru], Aurimas Černius [lt], Andika Triwidada [id], | ||||
|   Rajesh Ranjan [hi], Sweta Kothari [gu], Daniel Mustieles [es], | ||||
|   Fran Diéguez [gl], Praveen Illa [te] | ||||
|  | ||||
| 3.5.91 | ||||
| ====== | ||||
| * Improve modal dialog styling of network secret prompts [Jasper; #682412] | ||||
| * Fix visibility of non-active workspaces during overview transition | ||||
|   [Florian; #682002] | ||||
| * Improve scrollbar theming [Cosimo; #682476] | ||||
| * Make sure the app menu remains hidden in locked state [Florian; #682475] | ||||
| * Add tooltip to show-applications icon [Jasper; #682445] | ||||
| * Do not add duplicate remote search providers [Florian; #682470] | ||||
| * Handle 'popup-menu' signal on summary items [Florian; #682486] | ||||
| * Fix dwelling during mouse-down [Owen; #682385] | ||||
| * Set label actor for endSessionDialog.ListItem [Alejandro; #677503] | ||||
| * Don't match on comments when searching applications [Florian; #682529] | ||||
| * Make workspace selector more similar to the mockup [Stefano; #662087] | ||||
| * Fix extension installation and reloading [Jasper; #682578] | ||||
| * Hide removable devices in the lock screen [Giovanni; #681143] | ||||
| * Reset cancellable after hitting Escape on login screen [Alban; #681537] | ||||
| * Fix suspend from the user menu [Giovanni; #682746] | ||||
| * Set label actor for summary items in message tray [Alejandro; #677229] | ||||
| * Set label for the "Show applications" dash button [Alejandro; #682366] | ||||
| * Load extensions as late as possible [Jasper; #682822] | ||||
| * Improve mount operation dialogs [Jon; #682645] | ||||
| * Remove "Connect to ..." item from places search [Florian; #682817] | ||||
| * Don't auto-expand notifications with actions [Giovanni; #682738] | ||||
| * Add a new lock screen menu to combine volume network and power | ||||
|   [Giovanni; #682540] | ||||
| * Add support for pre-edit to StIMText [Daiki; #664041] | ||||
| * Remove StIconType [Jasper, Florian, Rui, Giovanni, Debarshi; #682540] | ||||
| * Use monitor geometry for dwelling [Florian; #683044] | ||||
| * Add support for surrounding-text to StIMText [Daiki; #683015] | ||||
| * Improve the placement and style of the "No results" text [Jasper; #683135] | ||||
| * Remove broken network device activation policy [Giovanni; #683136] | ||||
| * Hide power status icon when no battery is present [Tim; #683080] | ||||
| * Ensure summary items are square and have spacing [Debarshi; #682248] | ||||
| * Fix close buttons overlapping screen edge [Debarshi; #682343] | ||||
| * Escape the tray when a legacy icon is clicked [Giovanni; #682244] | ||||
| * Update arrow in the screen shield to match latest mockups [Giovanni; #682285] | ||||
| * Allow lifting the screen shield with the mouse wheel [Giovanni; #683164] | ||||
| * Make sure to show the app menu after unlocking the screen [Jasper; #683154] | ||||
| * Misc bug fixes and cleanups [Debarshi, Florian, Giovanni, Jasper, Rui; | ||||
|   #582650, #667439, #682238, #682268, #682429, #682455, #682544, #682546, | ||||
|   #682683, #682710, #682998, #683073, #683137, #683156] | ||||
|  | ||||
| Contributors: | ||||
|   Alban Browaeys, Giovanni Campagna, Cosimo Cecchi, Stefano Facchini, | ||||
|   Adel Gadllah, Tim Lunn, Rui Matos, William Jon McCann, Florian Müllner, | ||||
|   Alejandro Piñeiro, Debarshi Ray, Jasper St. Pierre, Owen Taylor, Daiki Ueno | ||||
|  | ||||
| Translations: | ||||
|   Piotr Drąg [pl], Takayuki KUSANO [ja], Kjartan Maraas [nb], | ||||
|   Aurimas Černius [lt], Daniel Mustieles [es], Yuri Myasoedov [ru], | ||||
|   Khaled Hosny [ar], Yaron Shahrabani [he], Tom Tryfonidis [el], | ||||
|   Nilamdyuti Goswami [as], Fran Diéguez [gl], Nguyễn Thái Ngọc Duy [vi], | ||||
|   A S Alam [pa], Dr.T.Vasudevan [ta], Luca Ferretti [it] | ||||
|  | ||||
| 3.5.90 | ||||
| ====== | ||||
| * Use symbolic icons for workspace switch OSD [Jon; #680738] | ||||
| * Lock screen improvements: | ||||
|   - Hide user menu and a11y menu in the screen lock [Giovanni; #681143] | ||||
|   - Bump the lock screen slightly when pressing a key [Giovanni; #681143] | ||||
|   - Constrain vertical movement of the screen shield [Giovanni; #681143] | ||||
|   - Return to lock screen on idle [Giovanni; #682041] | ||||
|   - Unlock screen automatically after fast-user switching [Giovanni; #682096] | ||||
|   - Fix "other user" label [Ray; #681750] | ||||
| * Constrain content of system modals to primary monitor [#681743] | ||||
| * Respect automatic lock setting on suspend/user-switch [Giovanni; #680231] | ||||
| * Improve styling of keyring prompt [Jasper; #681821] | ||||
| * Do not hard-code <super> as overlay-key [Florian; #665547] | ||||
| * Update style of attached modal dialogs [Florian; #681601] | ||||
| * a11y: allow navigation on non reactive items [Alejandro; #667439, #667439] | ||||
| * Implement mode-less overview design [Joost, Florian; #682109] | ||||
| * Implement message-tray redesign: | ||||
|   - Restyle the message tray [Ana, Allan, Florian; #677213, #682342] | ||||
|   - Move the desktop upwards when showing the tray [Debarshi; #681392] | ||||
|   - Add a close button to notifications [Ana, Jasper; #682253] | ||||
|   - Add a keybinding to toggle the tray [Debarshi; #681392] | ||||
|   - Make the tray keyboard navigable [Debarshi; #681519] | ||||
|   - Add dwelling at the bottom of the screen to open the tray [Owen; #682310] | ||||
|   - Don't time out banners when the user is inactive [Marina, Jasper] | ||||
|   - Misc fixes and cleanups [Jasper, Marina] | ||||
| * Fix showing "Next Week" on Sundays [Sebastian; #682198] | ||||
| * Delay restoring IM presence until the network comes up [Florian; #677982] | ||||
| * Display enterprise login hint [Ray; #681975] | ||||
| * Ignore unrecognized/irrelevant network devices/connections [Dan; #682364] | ||||
| * Misc bug fixes and cleanups: [Dan, Florian, Jasper, Jiro, Piotr, Rico; | ||||
|   #643687, #682045, #682189] | ||||
|  | ||||
| Contributors: | ||||
|   Giovanni Campagna, Allan Day, Piotr Drąg, William Jon McCann, | ||||
|   Sebastian Keller, Jiro Matsuzawa, Florian Müllner, Alejandro Piñeiro, | ||||
|   Debarshi Ray, Ana Risteska, Jasper St. Pierre, Ray Strode, Owen Taylor, | ||||
|   Rico Tzschichholz, Joost Verdoorn, Dan Winship, Marina Zhurakhinskaya | ||||
|  | ||||
| Translations: | ||||
|   Nilamdyuti Goswami [as], Daniel Mustieles [es], Yaron Shahrabani [he], | ||||
|   Chao-Hsiung Liao [zh_HK, zh_TW], Tobias Endrigkeit [de], A S Alam [pa], | ||||
|   Sandeep Sheshrao Shedmake [mr], Fran Diéguez [gl], | ||||
|   Мирослав Николић [sr, sr@latin] | ||||
|  | ||||
| 3.5.5 | ||||
| ===== | ||||
| * Update style to match mockups [Allan] | ||||
|   - improve calendar layout and legibility | ||||
|   - update notifications and menus | ||||
|   - use a common style for entries | ||||
|   - update scrollbars to match GTK+ | ||||
|   - improve clock/unlock button in lock screen | ||||
|   - update polkit dialogs [Jasper] | ||||
| * Fix login dialog growing when selecting different users [Florian; #675076] | ||||
| * Implement screen lock in the shell [Giovanni] | ||||
|   - restructure login code to be shared with session unlock [#619955] | ||||
|   - add initial screen shield / unlock dialog implementation [#619955] | ||||
|   - implement (optional) notification list on lock shield [#619955] | ||||
|   - update login dialog style to match lock screen [#619955] | ||||
|   - filter notifications to only show new ones on the screen lock [#681143] | ||||
|   - make notifications scrollable if necessary [#681143] | ||||
|   - use correct application names in notifications [#681143] | ||||
|   - allow to return to the shield by pressing Escape [#681143] | ||||
| * Minor login dialog improvements [Florian] | ||||
|   - update style to match the overall visuals [#660913] | ||||
|   - indicate whether users are logged in [#658185] | ||||
| * Add support for background-repeat CSS property [Jasper; #680801] | ||||
| * Add :active pseudo class on scroll handles [Florian] | ||||
| * Remove markup from translated strings [Matthias; #681270] | ||||
| * Misc bug fixes and cleanups: [Alban, Florian, Giovanni, Jasper, Jeremy, | ||||
|   Matthias, Piotr; #677893, #679944, #680064, #680170, #680216, #680426, | ||||
|   #681101, #681382] | ||||
|  | ||||
| Contributors: | ||||
|   Jeremy Bicha, Alban Browaeys, Giovanni Campagna, Matthias Clasen, Allan Day, | ||||
|   Piotr Drąg , Florian Müllner, Jasper St. Pierre | ||||
|  | ||||
| Translations: | ||||
|   Matej Urbančič [sl], Tom Tryfonidis [el], Yaron Shahrabani [he], | ||||
|   Kjartan Maraas [nb], Baurzhan Muftakhidinov [kk], Praveen Illa [te], | ||||
|   Khaled Hosny [ar], Daniel Mustieles [es], Gabor Kelemen [hu], | ||||
|   Fran Diéguez [gl], Sweta Kothari [gu], Aleksej Kabanov [ru], | ||||
|   Nilamdyuti Goswami [as], Arash Mousavi [fa], Мирослав Николић [sr, sr@latin] | ||||
|  | ||||
| 3.5.4 | ||||
| ===== | ||||
| * Fix wrong result handling of remote calls [Florian; #678852] | ||||
|   | ||||
| @@ -153,6 +153,8 @@ NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin) | ||||
|   /* global initialization routine, called once when plugin | ||||
|      is loaded */ | ||||
|  | ||||
|   g_type_init (); | ||||
|  | ||||
|   g_debug ("plugin loaded"); | ||||
|  | ||||
|   memcpy (&funcs, pfuncs, sizeof (funcs)); | ||||
|   | ||||
							
								
								
									
										36
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								configure.ac
									
									
									
									
									
								
							| @@ -1,5 +1,5 @@ | ||||
| AC_PREREQ(2.63) | ||||
| AC_INIT([gnome-shell],[3.7.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) | ||||
| AC_INIT([gnome-shell],[3.5.4],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) | ||||
|  | ||||
| AC_CONFIG_HEADERS([config.h]) | ||||
| AC_CONFIG_SRCDIR([src/shell-global.c]) | ||||
| @@ -36,8 +36,6 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", | ||||
|  | ||||
| PKG_PROG_PKG_CONFIG([0.22]) | ||||
|  | ||||
| AC_PATH_PROG([XSLTPROC], [xsltproc]) | ||||
|  | ||||
| GLIB_GSETTINGS | ||||
|  | ||||
| # Get a value to substitute into gnome-shell.in | ||||
| @@ -54,7 +52,7 @@ AC_MSG_CHECKING([for GStreamer (needed for recording functionality)]) | ||||
| if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then | ||||
|    AC_MSG_RESULT(yes) | ||||
|    build_recorder=true | ||||
|    recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11 gtk+-3.0" | ||||
|    recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11" | ||||
|    PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl) | ||||
| else | ||||
|    AC_MSG_RESULT(no) | ||||
| @@ -62,12 +60,12 @@ fi | ||||
|  | ||||
| AM_CONDITIONAL(BUILD_RECORDER, $build_recorder) | ||||
|  | ||||
| CLUTTER_MIN_VERSION=1.11.11 | ||||
| CLUTTER_MIN_VERSION=1.9.16 | ||||
| GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1 | ||||
| GJS_MIN_VERSION=1.33.2 | ||||
| MUTTER_MIN_VERSION=3.7.1 | ||||
| MUTTER_MIN_VERSION=3.5.4 | ||||
| GTK_MIN_VERSION=3.3.9 | ||||
| GIO_MIN_VERSION=2.35.0 | ||||
| GIO_MIN_VERSION=2.31.6 | ||||
| LIBECAL_MIN_VERSION=3.5.3 | ||||
| LIBEDATASERVER_MIN_VERSION=3.5.3 | ||||
| LIBEDATASERVERUI_MIN_VERSION=3.5.3 | ||||
| @@ -76,7 +74,7 @@ TELEPATHY_LOGGER_MIN_VERSION=0.2.4 | ||||
| POLKIT_MIN_VERSION=0.100 | ||||
| STARTUP_NOTIFICATION_MIN_VERSION=0.11 | ||||
| GCR_MIN_VERSION=3.3.90 | ||||
| GNOME_DESKTOP_REQUIRED_VERSION=3.7.1 | ||||
| GNOME_DESKTOP_REQUIRED_VERSION=3.5.1 | ||||
| GNOME_MENUS_REQUIRED_VERSION=3.5.3 | ||||
|  | ||||
| # Collect more than 20 libraries for a prize! | ||||
| @@ -99,7 +97,8 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION | ||||
|                                telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION | ||||
|                                polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes | ||||
|                                libnm-glib libnm-util gnome-keyring-1 | ||||
|                                gcr-3 >= $GCR_MIN_VERSION) | ||||
|                                gcr-3 >= $GCR_MIN_VERSION | ||||
|                                gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION) | ||||
|  | ||||
| PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0) | ||||
|  | ||||
| @@ -107,9 +106,6 @@ PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0) | ||||
|  | ||||
| PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2) | ||||
|  | ||||
| GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings` | ||||
| AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR]) | ||||
|  | ||||
| GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION]) | ||||
|  | ||||
| saved_CFLAGS=$CFLAGS | ||||
| @@ -121,7 +117,7 @@ CFLAGS=$saved_CFLAGS | ||||
| LIBS=$saved_LIBS | ||||
|  | ||||
| PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION) | ||||
| PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11) | ||||
| PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.2 x11) | ||||
| PKG_CHECK_MODULES(TRAY, gtk+-3.0) | ||||
| PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0) | ||||
| PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.5.4) | ||||
| @@ -140,7 +136,7 @@ PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0], | ||||
| 	 AC_SUBST([HAVE_BLUETOOTH],[0]) | ||||
| 	 AC_MSG_RESULT([no])]) | ||||
|  | ||||
| PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION gio-2.0) | ||||
| PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION libedataserverui-3.0 >= $LIBEDATASERVERUI_MIN_VERSION gio-2.0) | ||||
| AC_SUBST(CALENDAR_SERVER_CFLAGS) | ||||
| AC_SUBST(CALENDAR_SERVER_LIBS) | ||||
|  | ||||
| @@ -209,18 +205,6 @@ AC_SUBST(TYPELIBDIR) | ||||
|  | ||||
| GTK_DOC_CHECK([1.15], [--flavour no-tmpl]) | ||||
|  | ||||
| AC_ARG_ENABLE(man, | ||||
|               [AS_HELP_STRING([--enable-man], | ||||
|                               [generate man pages [default=yes]])],, | ||||
|               enable_man=yes) | ||||
| if test "$enable_man" != no; then | ||||
|   AC_PATH_PROG([XSLTPROC], [xsltproc]) | ||||
|   if test -z "$XSLTPROC"; then | ||||
|     AC_MSG_ERROR([xsltproc is required for --enable-man]) | ||||
|   fi | ||||
| fi | ||||
| AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no) | ||||
|  | ||||
| # Stay command-line compatible with the gnome-common configure option. Here | ||||
| # minimum/yes/maximum are the same, however. | ||||
| AC_ARG_ENABLE(compile_warnings, | ||||
|   | ||||
| @@ -1,12 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" ?> | ||||
| <KeyListEntries schema="org.gnome.shell.keybindings" | ||||
|                 group="system" | ||||
|                 _name="Screenshots" | ||||
|                 wm_name="GNOME Shell" | ||||
|                 package="gnome-shell"> | ||||
|  | ||||
| 	<KeyListEntry name="toggle-recording" | ||||
|                       _description="Record a screencast"/> | ||||
|  | ||||
| </KeyListEntries> | ||||
|  | ||||
| @@ -1,21 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" ?> | ||||
| <KeyListEntries schema="org.gnome.shell.keybindings" | ||||
|                 group="system" | ||||
|                 _name="System" | ||||
|                 wm_name="GNOME Shell" | ||||
|                 package="gnome-shell"> | ||||
|  | ||||
| 	<KeyListEntry name="toggle-message-tray" | ||||
|                       _description="Show the message tray"/> | ||||
|  | ||||
| 	<KeyListEntry name="focus-active-notification" | ||||
|                       _description="Focus the active notification"/> | ||||
|  | ||||
| 	<KeyListEntry name="toggle-application-view" | ||||
|                       _description="Show all applications"/> | ||||
|  | ||||
| 	<KeyListEntry name="open-application-menu" | ||||
|                       _description="Open the application menu"/> | ||||
|  | ||||
| </KeyListEntries> | ||||
|  | ||||
| @@ -10,6 +10,11 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop | ||||
|  | ||||
| @INTLTOOL_DESKTOP_RULE@ | ||||
|  | ||||
| searchprovidersdir = $(pkgdatadir)/open-search-providers | ||||
| dist_searchproviders_DATA =				\ | ||||
| 	open-search-providers/google.xml		\ | ||||
| 	open-search-providers/wikipedia.xml | ||||
|  | ||||
| introspectiondir = $(datadir)/dbus-1/interfaces | ||||
| introspection_DATA = org.gnome.ShellSearchProvider.xml | ||||
|  | ||||
| @@ -31,7 +36,6 @@ dist_theme_DATA =				\ | ||||
| 	theme/filter-selected-rtl.svg		\ | ||||
| 	theme/gnome-shell.css			\ | ||||
| 	theme/logged-in-indicator.svg		\ | ||||
| 	theme/message-tray-background.png	\ | ||||
| 	theme/noise-texture.png			\ | ||||
| 	theme/panel-button-border.svg		\ | ||||
| 	theme/panel-button-highlight-narrow.svg	\ | ||||
| @@ -39,20 +43,12 @@ dist_theme_DATA =				\ | ||||
| 	theme/process-working.svg		\ | ||||
| 	theme/running-indicator.svg		\ | ||||
| 	theme/source-button-border.svg		\ | ||||
| 	theme/summary-counter.svg		\ | ||||
| 	theme/toggle-off-us.svg			\ | ||||
| 	theme/toggle-off-intl.svg		\ | ||||
| 	theme/toggle-on-us.svg			\ | ||||
| 	theme/toggle-on-intl.svg		\ | ||||
| 	theme/ws-switch-arrow-up.png		\ | ||||
| 	theme/ws-switch-arrow-down.png | ||||
|  | ||||
| keysdir = @GNOME_KEYBINDINGS_KEYSDIR@ | ||||
| keys_in_files =					\ | ||||
| 	50-gnome-shell-screenshot.xml.in	\ | ||||
| 	50-gnome-shell-system.xml.in		\ | ||||
| 	$(NULL) | ||||
| keys_DATA = $(keys_in_files:.xml.in=.xml) | ||||
| 	theme/ws-switch-arrow-up.svg		\ | ||||
| 	theme/ws-switch-arrow-down.svg | ||||
|  | ||||
| gsettings_SCHEMAS = org.gnome.shell.gschema.xml | ||||
|  | ||||
| @@ -80,14 +76,12 @@ EXTRA_DIST =						\ | ||||
| 	$(introspection_DATA)				\ | ||||
| 	$(menu_DATA)					\ | ||||
| 	$(convert_DATA)					\ | ||||
| 	$(keys_in_files)				\ | ||||
| 	org.gnome.shell.gschema.xml.in.in | ||||
|  | ||||
| CLEANFILES =						\ | ||||
| 	gnome-shell.desktop.in				\ | ||||
| 	gnome-shell-extension-prefs.in			\ | ||||
| 	$(desktop_DATA)					\ | ||||
| 	$(keys_DATA)					\ | ||||
| 	$(gsettings_SCHEMAS)				\ | ||||
| 	gschemas.compiled				\ | ||||
| 	org.gnome.shell.gschema.valid			\ | ||||
|   | ||||
							
								
								
									
										7
									
								
								data/open-search-providers/google.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								data/open-search-providers/google.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> | ||||
| <ShortName>Google</ShortName> | ||||
| <Description>Google Search</Description> | ||||
| <InputEncoding>UTF-8</InputEncoding> | ||||
| <Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs9Pt8xetPtu9FsfFNtu%2BTzvb2%2B%2Fne4dFJeBw0egA%2FfAJAfAA8ewBBegAAAAD%2B%2FPtft98Mp%2BwWsfAVsvEbs%2FQeqvF8xO7%2F%2F%2F63yqkxdgM7gwE%2FggM%2BfQA%2BegBDeQDe7PIbotgQufcMufEPtfIPsvAbs%2FQvq%2Bfz%2Bf%2F%2B%2B%2FZKhR05hgBBhQI8hgBAgAI9ewD0%2B%2Fg3pswAtO8Cxf4Kw%2FsJvvYAqupKsNv%2B%2Fv7%2F%2FP5VkSU0iQA7jQA9hgBDgQU%2BfQH%2F%2Ff%2FQ6fM4sM4KsN8AteMCruIqqdbZ7PH8%2Fv%2Fg6Nc%2Fhg05kAA8jAM9iQI%2BhQA%2BgQDQu6b97uv%2F%2F%2F7V8Pqw3eiWz97q8%2Ff%2F%2F%2F%2F7%2FPptpkkqjQE4kwA7kAA5iwI8iAA8hQCOSSKdXjiyflbAkG7u2s%2F%2B%2F%2F39%2F%2F7r8utrqEYtjQE8lgA7kwA7kwA9jwA9igA9hACiWSekVRyeSgiYSBHx6N%2F%2B%2Fv7k7OFRmiYtlAA5lwI7lwI4lAA7kgI9jwE9iwI4iQCoVhWcTxCmb0K%2BooT8%2Fv%2F7%2F%2F%2FJ2r8fdwI1mwA3mQA3mgA8lAE8lAE4jwA9iwE%2BhwGfXifWvqz%2B%2Ff%2F58u%2Fev6Dt4tr%2B%2F%2F2ZuIUsggA7mgM6mAM3lgA5lgA6kQE%2FkwBChwHt4dv%2F%2F%2F728ei1bCi7VAC5XQ7kz7n%2F%2F%2F6bsZkgcB03lQA9lgM7kwA2iQktZToPK4r9%2F%2F%2F9%2F%2F%2FSqYK5UwDKZAS9WALIkFn%2B%2F%2F3%2F%2BP8oKccGGcIRJrERILYFEMwAAuEAAdX%2F%2Ff7%2F%2FP%2B%2BfDvGXQLIZgLEWgLOjlf7%2F%2F%2F%2F%2F%2F9QU90EAPQAAf8DAP0AAfMAAOUDAtr%2F%2F%2F%2F7%2B%2Fu2bCTIYwDPZgDBWQDSr4P%2F%2Fv%2F%2F%2FP5GRuABAPkAA%2FwBAfkDAPAAAesAAN%2F%2F%2B%2Fz%2F%2F%2F64g1C5VwDMYwK8Yg7y5tz8%2Fv%2FV1PYKDOcAAP0DAf4AAf0AAfYEAOwAAuAAAAD%2F%2FPvi28ymXyChTATRrIb8%2F%2F3v8fk6P8MAAdUCAvoAAP0CAP0AAfYAAO4AAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAA</Image> | ||||
| <Url type="text/html" method="GET" template="http://www.google.com/search?q={searchTerms}"/> | ||||
| </OpenSearchDescription> | ||||
							
								
								
									
										44
									
								
								data/open-search-providers/wikipedia.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								data/open-search-providers/wikipedia.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> | ||||
| <ShortName>Wikipedia</ShortName> | ||||
| <Description>Wikipedia, the free encyclopedia</Description> | ||||
| <InputEncoding>UTF-8</InputEncoding> | ||||
| <Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAEAgQAhIOEAMjHyABIR0gA6ejpAGlqaQCpqKkAKCgoAPz9%2FAAZGBkAmJiYANjZ2ABXWFcAent6ALm6uQA8OjwAiIiIiIiIiIiIiI4oiL6IiIiIgzuIV4iIiIhndo53KIiIiB%2FWvXoYiIiIfEZfWBSIiIEGi%2FfoqoiIgzuL84i9iIjpGIoMiEHoiMkos3FojmiLlUipYliEWIF%2BiDe0GoRa7D6GPbjcu1yIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</Image> | ||||
| <Url type="text/html" method="GET" template="http://{language}.wikipedia.org/wiki/Special:Search?search={searchTerms}"/> | ||||
| <!-- The criterion for being below is being listed with more than 100,000 | ||||
| articles on http://meta.wikimedia.org/wiki/List_of_Wikipedias --> | ||||
| <Language>ar</Language> | ||||
| <Language>bg</Language> | ||||
| <Language>ca</Language> | ||||
| <Language>cs</Language> | ||||
| <Language>da</Language> | ||||
| <Language>de</Language> | ||||
| <Language>en</Language> | ||||
| <Language>eo</Language> | ||||
| <Language>es</Language> | ||||
| <Language>fa</Language> | ||||
| <Language>fi</Language> | ||||
| <Language>fr</Language> | ||||
| <Language>he</Language> | ||||
| <Language>hu</Language> | ||||
| <Language>id</Language> | ||||
| <Language>it</Language> | ||||
| <Language>ja</Language> | ||||
| <Language>ko</Language> | ||||
| <Language>lt</Language> | ||||
| <Language>nl</Language> | ||||
| <Language>no</Language> | ||||
| <Language>pl</Language> | ||||
| <Language>pt</Language> | ||||
| <Language>ro</Language> | ||||
| <Language>ru</Language> | ||||
| <Language>sk</Language> | ||||
| <Language>sl</Language> | ||||
| <Language>sr</Language> | ||||
| <Language>sv</Language> | ||||
| <Language>tr</Language> | ||||
| <Language>uk</Language> | ||||
| <Language>vi</Language> | ||||
| <Language>vo</Language> | ||||
| <Language>war</Language> | ||||
| <Language>zh</Language> | ||||
| </OpenSearchDescription> | ||||
| @@ -108,7 +108,7 @@ | ||||
|           </doc:summary> | ||||
|         </doc:doc> | ||||
|       </arg> | ||||
|       <arg type="aa{sv}" direction="out"> | ||||
|       <arg type="a{sv}" direction="out"> | ||||
|         <doc:doc> | ||||
|           <doc:summary> | ||||
|             <doc:para> | ||||
|   | ||||
| @@ -39,6 +39,10 @@ | ||||
|         will be displayed in the favorites area. | ||||
|       </_description> | ||||
|     </key> | ||||
|     <key name="disabled-open-search-providers" type="as"> | ||||
|       <default>[]</default> | ||||
|       <_summary>disabled OpenSearch providers</_summary> | ||||
|     </key> | ||||
|     <key name="command-history" type="as"> | ||||
|       <default>[]</default> | ||||
|       <_summary>History for command (Alt-F2) dialog</_summary> | ||||
| @@ -57,14 +61,6 @@ value here is from the TpConnectionPresenceType enumeration.</_summary> | ||||
|       <_summary>Internally used to store the last session presence status for the user. The | ||||
| value here is from the GsmPresenceStatus enumeration.</_summary> | ||||
|     </key> | ||||
|     <key name="always-show-log-out" type="b"> | ||||
|       <default>false</default> | ||||
|       <_summary>Always show the 'Log out' menuitem in the user menu.</_summary> | ||||
|       <_description> | ||||
|         This key overrides the automatic hiding of the 'Log out' | ||||
|         menuitem in single-user, single-session situations. | ||||
|       </_description> | ||||
|     </key> | ||||
|     <child name="calendar" schema="org.gnome.shell.calendar"/> | ||||
|     <child name="recorder" schema="org.gnome.shell.recorder"/> | ||||
|     <child name="keybindings" schema="org.gnome.shell.keybindings"/> | ||||
| @@ -91,28 +87,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary> | ||||
|         Keybinding to open the application menu. | ||||
|       </_description> | ||||
|     </key> | ||||
|     <key name="toggle-application-view" type="as"> | ||||
|       <default>["<Super>a"]</default> | ||||
|       <_summary>Keybinding to open the "Show Applications" view</_summary> | ||||
|       <_description> | ||||
|         Keybinding to open the "Show Applications" view of the Activities | ||||
|         Overview. | ||||
|       </_description> | ||||
|     </key> | ||||
|     <key name="toggle-message-tray" type="as"> | ||||
|       <default>["<Super>m"]</default> | ||||
|       <_summary>Keybinding to toggle the visibility of the message tray</_summary> | ||||
|       <_description> | ||||
|         Keybinding to toggle the visibility of the message tray. | ||||
|       </_description> | ||||
|     </key> | ||||
|     <key name="focus-active-notification" type="as"> | ||||
|       <default>["<Super>n"]</default> | ||||
|       <_summary>Keybinding to focus the active notification</_summary> | ||||
|       <_description> | ||||
|         Keybinding to focus the active notification. | ||||
|       </_description> | ||||
|     </key> | ||||
|     <key name="toggle-recording" type="as"> | ||||
|       <default><![CDATA[['<Control><Shift><Alt>r']]]></default> | ||||
|       <_summary>Keybinding to toggle the screen recorder</_summary> | ||||
| @@ -155,7 +129,7 @@ value here is from the GsmPresenceStatus enumeration.</_summary> | ||||
|         take care of its own output - this might be used to send the output | ||||
|         to an icecast server via shout2send or similar. When unset or set | ||||
|         to an empty value, the default pipeline will be used. This is currently | ||||
|         'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux' | ||||
|         'vp8enc quality=8 speed=6 threads=%T ! queue ! webmmux' | ||||
|         and records to WEBM using the VP8 codec. %T is used as a placeholder | ||||
|         for a guess at the optimal thread count on the system. | ||||
|       </_description> | ||||
| @@ -171,53 +145,44 @@ value here is from the GsmPresenceStatus enumeration.</_summary> | ||||
|     </key> | ||||
|   </schema> | ||||
|  | ||||
|   <schema id="org.gnome.shell.overrides" path="/org/gnome/shell/overrides/" | ||||
| 	  gettext-domain="@GETTEXT_PACKAGE@"> | ||||
|   <schema id="org.gnome.shell.overrides" path="/org/gnome/shell/overrides/"> | ||||
|     <key name="attach-modal-dialogs" type="b"> | ||||
|       <default>true</default> | ||||
|       <_summary>Attach modal dialog to the parent window</_summary> | ||||
|       <_description> | ||||
|       <summary>Attach modal dialog to the parent window</summary> | ||||
|       <description> | ||||
|         This key overrides the key in org.gnome.mutter when running | ||||
|         GNOME Shell. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|  | ||||
|     <key name="button-layout" type="s"> | ||||
|       <default>":close"</default> | ||||
|       <_summary>Arrangement of buttons on the titlebar</_summary> | ||||
|       <_description> | ||||
|       <summary>Arrangement of buttons on the titlebar</summary> | ||||
|       <description> | ||||
|         This key overrides the key in org.gnome.desktop.wm.preferences when | ||||
|         running GNOME Shell. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|  | ||||
|     <key name="edge-tiling" type="b"> | ||||
|       <default>true</default> | ||||
|       <_summary>Enable edge tiling when dropping windows on screen edges</_summary> | ||||
|       <_description> | ||||
|       <summary>Enable edge tiling when dropping windows on screen edges</summary> | ||||
|       <description> | ||||
|         This key overrides the key in org.gnome.mutter when running GNOME Shell. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|  | ||||
|     <key name="dynamic-workspaces" type="b"> | ||||
|       <default>true</default> | ||||
|       <_summary>Workspaces are managed dynamically</_summary> | ||||
|       <_description> | ||||
|       <summary>Workspaces are managed dynamically</summary> | ||||
|       <description> | ||||
|         This key overrides the key in org.gnome.mutter when running GNOME Shell. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|  | ||||
|     <key name="workspaces-only-on-primary" type="b"> | ||||
|       <default>true</default> | ||||
|       <_summary>Workspaces only on primary monitor</_summary> | ||||
|       <_description> | ||||
|         This key overrides the key in org.gnome.mutter when running GNOME Shell. | ||||
|       </_description> | ||||
|     </key> | ||||
|  | ||||
|     <key name="focus-change-on-pointer-rest" type="b"> | ||||
|       <default>true</default> | ||||
|       <summary>Delay focus changes in mouse mode until the pointer stops moving</summary> | ||||
|       <summary>Workspaces only on primary monitor</summary> | ||||
|       <description> | ||||
|         This key overrides the key in org.gnome.mutter when running GNOME Shell. | ||||
|       </description> | ||||
|   | ||||
| @@ -39,6 +39,7 @@ stage { | ||||
| /* small */ | ||||
| .app-well-menu, | ||||
| .contact-details-status, | ||||
| .run-dialog-label, | ||||
| .run-dialog-error-label { | ||||
|     font-size: 9pt; | ||||
| } | ||||
| @@ -47,8 +48,9 @@ stage { | ||||
| .dash-label, | ||||
| .window-caption, | ||||
| .switcher-list,  | ||||
| .source-title, | ||||
| .app-well-app > .overview-icon, | ||||
| .show-apps > .overview-icon, | ||||
| .remove-favorite > .overview-icon, | ||||
| .search-result-content > .overview-icon { | ||||
|     font-size: 9pt; | ||||
|     font-weight: bold; | ||||
| @@ -79,25 +81,25 @@ StScrollBar StBin#trough { | ||||
| } | ||||
|  | ||||
| StScrollBar StButton#vhandle { | ||||
|     background-color: #5d6464; | ||||
|     background-color: #959797; | ||||
|     border: 2px solid #242424; | ||||
|     border-radius: 8px; | ||||
| } | ||||
|  | ||||
| StScrollBar StButton#hhandle { | ||||
|     background-color: #5d6464; | ||||
|     background-color: #959797; | ||||
|     border: 2px solid #242424; | ||||
|     border-radius: 8px; | ||||
| } | ||||
|  | ||||
| StScrollBar StButton#hhandle:hover, | ||||
| StScrollBar StButton#vhandle:hover { | ||||
|     background-color: #777d7c; | ||||
|     background-color: #c2c3c3; | ||||
| } | ||||
|  | ||||
| StScrollBar StButton#hhandle:active, | ||||
| StScrollBar StButton#vhandle:active { | ||||
|     background-color: #3465a4; | ||||
|     background-color: #729fcf; | ||||
| } | ||||
|  | ||||
| /* Check Boxes */ | ||||
| @@ -150,11 +152,11 @@ StScrollBar StButton#vhandle:active { | ||||
|     box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9); | ||||
| } | ||||
|  | ||||
| .popup-sub-menu:scrolled .popup-menu-item:ltr { | ||||
| .popup-sub-menu .popup-menu-item:ltr { | ||||
|     padding-right: 0em; | ||||
| } | ||||
|  | ||||
| .popup-sub-menu:scrolled .popup-menu-item:rtl { | ||||
| .popup-sub-menu .popup-menu-item:rtl { | ||||
|     padding-left: 0em; | ||||
| } | ||||
|  | ||||
| @@ -194,7 +196,7 @@ StScrollBar StButton#vhandle:active { | ||||
|     background-color: #4c4c4c; | ||||
| } | ||||
|  | ||||
| .popup-menu-item:insensitive { | ||||
| StButton.popup-menu-item:insensitive { | ||||
|     color: #9f9f9f; | ||||
| } | ||||
|  | ||||
| @@ -233,18 +235,13 @@ StScrollBar StButton#vhandle:active { | ||||
|     spacing: .5em; | ||||
| } | ||||
|  | ||||
| .popup-status-menu-item { | ||||
| .popup-inactive-menu-item { | ||||
|     font-weight: normal; | ||||
|     color: #999; | ||||
| } | ||||
|  | ||||
| .popup-inactive-menu-item, .popup-inactive-menu-item:insensitive { | ||||
|     color: white; | ||||
| } | ||||
|  | ||||
| .popup-subtitle-menu-item, .popup-subtitle-menu-item:insensitive { | ||||
| .popup-subtitle-menu-item { | ||||
|     font-weight: bold; | ||||
|     color: white; | ||||
| } | ||||
|  | ||||
| .popup-menu-icon { | ||||
| @@ -310,7 +307,7 @@ StScrollBar StButton#vhandle:active { | ||||
| .notification-icon-button:focus, | ||||
| .hotplug-notification-item:focus, | ||||
| .modal-dialog-button:focus { | ||||
|     border-width: 2px; | ||||
|     border: 2px solid #8b8b8b; | ||||
| } | ||||
|  | ||||
| .dash-search-button:active, | ||||
| @@ -325,30 +322,26 @@ StScrollBar StButton#vhandle:active { | ||||
|     background-gradient-end: rgba(255, 255, 255, 0.2); | ||||
| } | ||||
|  | ||||
| .notification-button:insensitive, | ||||
| .notification-icon-button:insensitive, | ||||
| .modal-dialog-button:insensitive { | ||||
|     border-color: #666666; | ||||
| .notification-button:insensitive { | ||||
|     color: #9f9f9f; | ||||
|     background-gradient-direction: none; | ||||
|     background-color: rgba(102, 102, 102, 0.15); | ||||
| } | ||||
|  | ||||
| /* Entries */ | ||||
|  | ||||
| #searchEntry, | ||||
| .notification StEntry, | ||||
| .modal-dialog StEntry { | ||||
| .login-dialog-prompt-entry, | ||||
| .prompt-dialog-password-entry { | ||||
|     color: rgb(64, 64, 64); | ||||
|     caret-color: rgb(64, 64, 64); | ||||
|     font-size: 12pt; | ||||
|     caret-size: 1px; | ||||
|     selected-color: white; | ||||
|     selected-color: black; | ||||
|     padding: 4px 12px; | ||||
| } | ||||
|  | ||||
| #searchEntry, | ||||
| .run-dialog-entry, | ||||
| .notification StEntry { | ||||
|     border: 2px solid rgba(245,245,245,0.2); | ||||
|     background-gradient-start: rgba(5,5,6,0.1); | ||||
| @@ -361,7 +354,8 @@ StScrollBar StButton#vhandle:active { | ||||
| #searchEntry:focus, | ||||
| #searchEntry:hover, | ||||
| .notification StEntry:focus, | ||||
| .modal-dialog StEntry { | ||||
| .login-dialog-prompt-entry, | ||||
| .prompt-dialog-password-entry { | ||||
|     border: 2px solid rgb(136,138,133); | ||||
|     background-gradient-start: rgb(200,200,200); | ||||
|     background-gradient-end: white; | ||||
| @@ -370,17 +364,12 @@ StScrollBar StButton#vhandle:active { | ||||
| } | ||||
|  | ||||
| .notification StEntry:focus, | ||||
| .modal-dialog StEntry:focus { | ||||
| .prompt-dialog-password-entry:focus, | ||||
| .login-dialog-prompt-entry:focus { | ||||
|     border: 2px solid #3465a4; | ||||
| } | ||||
|  | ||||
| #searchEntry { | ||||
|     border-color: rgba(245,245,245,0.3); | ||||
|     color: rgb(192, 192, 192); | ||||
|     caret-color: rgb(192, 192, 192); | ||||
| } | ||||
|  | ||||
| #searchEntry:hover { | ||||
|     color: rgb(128, 128, 128); | ||||
|     caret-color: rgb(128, 128, 128); | ||||
| } | ||||
| @@ -393,7 +382,8 @@ StScrollBar StButton#vhandle:active { | ||||
| } | ||||
|  | ||||
| .notification StEntry, | ||||
| .modal-dialog StEntry { | ||||
| .prompt-dialog-password-entry, | ||||
| .login-dialog-prompt-entry { | ||||
|     border-radius: 5px; | ||||
|     padding: 4px 4px; | ||||
| } | ||||
| @@ -408,8 +398,6 @@ StScrollBar StButton#vhandle:active { | ||||
| .login-dialog-prompt-entry:insensitive { | ||||
|     color: rgba(0,0,0,0.7); | ||||
|     border: 2px solid #565656; | ||||
|     background-gradient-start: rgb(200,200,200); | ||||
|     background-gradient-end: rgb(210,210,210); | ||||
| } | ||||
|  | ||||
| /* Panel */ | ||||
| @@ -515,7 +503,7 @@ StScrollBar StButton#vhandle:active { | ||||
|     -boxpointer-gap: 4px | ||||
| } | ||||
|  | ||||
| .panel-status-button-box { | ||||
| #networkMenu { | ||||
|     spacing: 4px; | ||||
| } | ||||
|  | ||||
| @@ -557,6 +545,7 @@ StScrollBar StButton#vhandle:active { | ||||
| } | ||||
|  | ||||
| .status-chooser-combo.popup-combo-menu { | ||||
|     background-color: rgba(0,0,0,0.7); | ||||
|     padding: .4em 0em; | ||||
|     border-radius: 4px; | ||||
|     border: 1px solid #5f5f5f; | ||||
| @@ -574,7 +563,7 @@ StScrollBar StButton#vhandle:active { | ||||
| /* Overview */ | ||||
|  | ||||
| #overview { | ||||
|     spacing: 40px; | ||||
|     spacing: 12px; | ||||
| } | ||||
|  | ||||
| .window-caption { | ||||
| @@ -590,24 +579,22 @@ StScrollBar StButton#vhandle:active { | ||||
|     border-right: 0px; | ||||
|     border-radius: 9px 0px 0px 9px; | ||||
|     background-color: rgba(0, 0, 0, 0.5); | ||||
|     padding: 11px 7px 11px 11px; | ||||
|     padding: 8px; | ||||
| } | ||||
|  | ||||
| .workspace-thumbnails-background:rtl { | ||||
|     border-right: 1px; | ||||
|     border-left: 0px; | ||||
|     border-radius: 0px 9px 9px 0px; | ||||
|     padding: 11px 11px 11px 7px; | ||||
| } | ||||
|  | ||||
| .workspace-thumbnails { | ||||
|     spacing: 11px; | ||||
|     spacing: 7px; | ||||
| } | ||||
|  | ||||
| .workspace-thumbnail-indicator { | ||||
|     border: 4px solid rgba(255,255,255,0.7); | ||||
|     border-radius: 4px; | ||||
|     padding: 1px; | ||||
|     outline: 2px solid white; | ||||
|     border: 1px solid #888; | ||||
| } | ||||
|  | ||||
| .window-caption { | ||||
| @@ -617,46 +604,18 @@ StScrollBar StButton#vhandle:active { | ||||
|     -shell-caption-spacing: 12px; | ||||
| } | ||||
|  | ||||
| .window-close, .notification-close { | ||||
|     background-image: url("close-window.svg"); | ||||
|     background-size: 32px; | ||||
|     height: 32px; | ||||
|     width: 32px; | ||||
| } | ||||
|  | ||||
| .window-close { | ||||
|     background-image: url("close-window.svg"); | ||||
|     background-size: 34px; | ||||
|     height: 34px; | ||||
|     width: 34px; | ||||
|     -shell-close-overlap: 20px; | ||||
| } | ||||
|  | ||||
| .notification-close { | ||||
|     /* we start out in the top right of the | ||||
|      * notification, inset. | ||||
|      * | ||||
|      * center is 32px/2 = 16px | ||||
|      * | ||||
|      * adjust left 2px | ||||
|      * adjust down 8px */ | ||||
|  | ||||
|     -shell-close-overlap-x: 14px; | ||||
|     -shell-close-overlap-y: -12px; | ||||
| } | ||||
|  | ||||
| .notification-close:rtl { | ||||
|     /* as above, but starting out in the top left of the | ||||
|      * notification. */ | ||||
|  | ||||
|     -shell-close-overlap-x: -14px; | ||||
| } | ||||
|  | ||||
| .window-close:rtl { | ||||
|     -st-background-image-shadow: 2px 2px 6px rgba(0,0,0,0.5); | ||||
| } | ||||
|  | ||||
| .window-picker { | ||||
|     -horizontal-spacing: 40px; | ||||
|     -vertical-spacing: 40px; | ||||
| } | ||||
|  | ||||
| /* Dash */ | ||||
|  | ||||
| #dash { | ||||
| @@ -675,6 +634,11 @@ StScrollBar StButton#vhandle:active { | ||||
|     border-radius: 9px 0px 0px 9px; | ||||
| } | ||||
|  | ||||
| #dash:empty { | ||||
|     height: 100px; | ||||
|     width: 60px; | ||||
| } | ||||
|  | ||||
| .placeholder { | ||||
|     background-image: url("dash-placeholder.svg"); | ||||
|     background-size: contain; | ||||
| @@ -685,21 +649,44 @@ StScrollBar StButton#vhandle:active { | ||||
|     spacing: 1em; | ||||
| } | ||||
|  | ||||
| #viewSelectorTabBar { | ||||
|     padding: 1em; | ||||
| } | ||||
|  | ||||
| /* Search Box */ | ||||
|  | ||||
| #searchArea { | ||||
|     padding: 0px 24px; | ||||
| } | ||||
|  | ||||
| #searchEntry { | ||||
|     border-radius: 17px; | ||||
|     width: 320px; | ||||
|     width: 250px; | ||||
| } | ||||
|  | ||||
| .search-entry-icon { | ||||
|     icon-size: 1em; | ||||
|     color: #c0c0c0; | ||||
|     color: #8d8f8a; | ||||
| } | ||||
|  | ||||
| #searchEntry:hover .search-entry-icon, | ||||
| #searchEntry:focus .search-entry-icon { | ||||
|     color: #8d8f8a; | ||||
| /* View Tabs */ | ||||
|  | ||||
| .view-tab-title { | ||||
|     color: #888a85; | ||||
|     font-size: 12pt; | ||||
|     font-weight: bold; | ||||
|     padding: 0px 0.75em; | ||||
|     height: 1.5em; | ||||
| } | ||||
|  | ||||
| .view-tab-title:hover { | ||||
|     color: #bbb; | ||||
| } | ||||
|  | ||||
| .view-tab-title:selected { | ||||
|     color: #000000; | ||||
|     background-color: #c2c7cd; | ||||
|     border-radius: 0.25em; | ||||
| } | ||||
|  | ||||
| /* Search Results */ | ||||
| @@ -719,6 +706,7 @@ StScrollBar StButton#vhandle:active { | ||||
|     padding-left: 20px; | ||||
| } | ||||
|  | ||||
| .search-statustext, | ||||
| .search-section-header { | ||||
|     padding: 4px 12px; | ||||
|     spacing: 4px; | ||||
| @@ -726,12 +714,6 @@ StScrollBar StButton#vhandle:active { | ||||
|     font-size: .8em; | ||||
| } | ||||
|  | ||||
| .search-statustext { | ||||
|     color: #efefef; | ||||
|     font-size: 2em; | ||||
|     font-weight: bold; | ||||
| } | ||||
|  | ||||
| .search-section-results { | ||||
|     padding: 6px; | ||||
| } | ||||
| @@ -744,6 +726,10 @@ StScrollBar StButton#vhandle:active { | ||||
|     spacing: 4px; | ||||
| } | ||||
|  | ||||
| .search-providers-box { | ||||
|     spacing: 12px; | ||||
| } | ||||
|  | ||||
| /* Text labels are an odd number of pixels tall. The uneven top and bottom | ||||
|  * padding compensates for this and ensures that the label is vertically | ||||
|  * centered */ | ||||
| @@ -818,12 +804,21 @@ StScrollBar StButton#vhandle:active { | ||||
|     outline: 1px solid #aaa; | ||||
| } | ||||
|  | ||||
| .dash-item-container > StButton { | ||||
| .dash-item-container > .app-well-app { | ||||
|     padding: 4px 8px; | ||||
| } | ||||
|  | ||||
| .remove-favorite-icon { | ||||
|     color: #a0a0a0; | ||||
| } | ||||
|  | ||||
| .remove-favorite-icon:hover { | ||||
|     color: white; | ||||
|     icon-shadow: black 0px 2px 2px; | ||||
| } | ||||
|  | ||||
| .app-well-app > .overview-icon, | ||||
| .show-apps > .overview-icon, | ||||
| .remove-favorite > .overview-icon, | ||||
| .search-result-content > .overview-icon { | ||||
|     border-radius: 4px; | ||||
|     padding: 3px; | ||||
| @@ -839,44 +834,15 @@ StScrollBar StButton#vhandle:active { | ||||
| } | ||||
|  | ||||
| .app-well-app:hover > .overview-icon, | ||||
| .show-apps:hover > .overview-icon, | ||||
| .remove-favorite:hover > .overview-icon, | ||||
| .search-result-content:hover > .overview-icon { | ||||
|     background-color: rgba(255,255,255,0.1); | ||||
|     text-shadow: black 0px 2px 2px; | ||||
|     transition-duration: 100; | ||||
|     color:white; | ||||
| } | ||||
|  | ||||
| .show-apps { | ||||
|     padding: 4px 0; | ||||
| } | ||||
|  | ||||
| .show-apps-icon { | ||||
|     color: #a0a0a0; | ||||
| } | ||||
|  | ||||
| .show-apps:hover .show-apps-icon { | ||||
|     color: white; | ||||
| } | ||||
|  | ||||
| .show-apps:checked > .overview-icon { | ||||
|     background-gradient-start: rgba(255, 255, 255, .05); | ||||
|     background-gradient-end: rgba(255, 255, 255, .15); | ||||
|     background-gradient-direction: vertical; | ||||
|     border-radius: 4px; | ||||
|     box-shadow: inset 0px 1px 2px 0px rgba(0, 0, 0, 1); | ||||
|     transition-duration: 100; | ||||
| } | ||||
|  | ||||
| .show-apps:checked .show-apps-icon, | ||||
| .show-apps:focus .show-apps-icon { | ||||
|     color: white; | ||||
|     transition-duration: 100; | ||||
| } | ||||
|  | ||||
| .app-well-app:focus > .overview-icon, | ||||
| .search-result-content:focus > .overview-icon, | ||||
| .show-apps:focus > .overview-icon, | ||||
| .app-well-app:selected > .overview-icon, | ||||
| .search-result-content:selected > .overview-icon { | ||||
|     background-color: rgba(255,255,255,0.33); | ||||
| @@ -1207,31 +1173,19 @@ StScrollBar StButton#vhandle:active { | ||||
|  | ||||
| /* Message Tray */ | ||||
| #message-tray { | ||||
|     background: #2e3436 url(message-tray-background.png); | ||||
|     background-repeat: repeat; | ||||
|     transition-duration: 250; | ||||
| } | ||||
|  | ||||
| #message-tray:keyboard { | ||||
|     /* Same as the OSK */ | ||||
|     background: rgba(0, 0, 0, 0.8); | ||||
| } | ||||
|  | ||||
| #message-tray:overview { | ||||
|     background: rgba(0, 0, 0, 0.1); | ||||
|     outline: 1px solid rgba(128, 128, 128, 0.3); | ||||
|     background-gradient-direction: vertical; | ||||
|     background-gradient-start: rgba(0,0,0,0.01); | ||||
|     background-gradient-end: rgba(0,0,0,0.82); | ||||
|     height: 36px; | ||||
| } | ||||
|  | ||||
| .notification { | ||||
|     font-size: 11pt; | ||||
|     border-radius: 10px 10px 0px 0px; | ||||
|     background: rgba(0,0,0,0.8); | ||||
|     padding: 8px 8px 4px 8px; | ||||
|     spacing-rows: 10px; | ||||
|     spacing-columns: 10px; | ||||
| } | ||||
|  | ||||
| .notification, #notification-container { | ||||
|     font-size: 11pt; | ||||
|     width: 34em; | ||||
| } | ||||
|  | ||||
| @@ -1239,13 +1193,6 @@ StScrollBar StButton#vhandle:active { | ||||
|     padding-bottom: 8px; | ||||
| } | ||||
|  | ||||
| .notification-unexpanded { | ||||
|     /* We want to force the actor at a specific size, irrespective | ||||
|        of its minimum and preferred size, so we override both */ | ||||
|     min-height: 36px; | ||||
|     height: 36px; | ||||
| } | ||||
|  | ||||
| /* We use row-span = 2 for the image cell, which prevents its height preferences to be | ||||
|    taken into account during allocation, so its height ends up being limited by the height | ||||
|    of the content in the other rows. To avoid showing a stretched image, we set the minimum | ||||
| @@ -1260,7 +1207,6 @@ StScrollBar StButton#vhandle:active { | ||||
|     -arrow-base: 36px; | ||||
|     -arrow-rise: 18px; | ||||
|     color: white; | ||||
|     -boxpointer-gap: 4px; | ||||
| } | ||||
|  | ||||
| .summary-boxpointer .notification { | ||||
| @@ -1432,10 +1378,6 @@ StScrollBar StButton#vhandle:active { | ||||
|     padding-right: 4px; | ||||
| } | ||||
|  | ||||
| .chat-notification-scrollview{ | ||||
| 	max-height: 22em; | ||||
| } | ||||
|  | ||||
| .subscription-message { | ||||
|     font-style: italic; | ||||
| } | ||||
| @@ -1444,46 +1386,87 @@ StScrollBar StButton#vhandle:active { | ||||
|     border-radius: 4px; | ||||
| } | ||||
|  | ||||
| /* The spacing and padding on the summary is tricky; we want to keep | ||||
|  * the icons from touching each other or the edges of the screen, but | ||||
|  * we also want them to be "Fitts"-y with respect to the edges, so the | ||||
|  * summary area's bottom and right padding must actually be part of | ||||
|  * the icons. However, we can't put *all* of the padding into the | ||||
|  * icons, because then the summary would be 0x0 when there were no | ||||
|  * icons in it, and so you wouldn't be able to hover over it to | ||||
|  * activate it. | ||||
|  * | ||||
|  * Also, the spacing between a summary-source's icon and title is | ||||
|  * actually specified as padding-left in source-title, because we | ||||
|  * want the spacing to collapse along with the title. | ||||
|  */ | ||||
| #summary-mode { | ||||
|     height: 72px; | ||||
|     padding: 2px 0px 0px 4px; | ||||
|     height: 36px; | ||||
| } | ||||
|  | ||||
| #summary-mode:rtl { | ||||
|     padding: 2px 4px 0px 0px; | ||||
| } | ||||
|  | ||||
| .summary-source-button { | ||||
|     padding: 6px 3px 6px 3px; | ||||
|     text-shadow: black 0px 2px 2px; | ||||
| } | ||||
|  | ||||
| .summary-source-button:ltr { | ||||
|     padding-right: 12px; | ||||
| } | ||||
|  | ||||
| .summary-source-button:selected .summary-source { | ||||
|     background-image: url("panel-button-highlight-narrow.svg"); | ||||
|     background-size: contain; | ||||
|     border-image: url("source-button-border.svg") 10 10 0 1; | ||||
| } | ||||
|  | ||||
| .summary-source-button:expanded:selected .summary-source { | ||||
|     background-image: none; | ||||
|     border-image: none; | ||||
| } | ||||
|  | ||||
| .summary-source-button:expanded:selected { | ||||
|     background-image: url("panel-button-highlight-wide.svg"); | ||||
|     background-size: contain; | ||||
|     border-image: url("source-button-border.svg") 10 10 0 1; | ||||
| } | ||||
|  | ||||
| .summary-source-button:rtl { | ||||
|     padding-left: 12px; | ||||
| } | ||||
|  | ||||
| .summary-source-button:last-child:ltr { | ||||
|     padding-right: 6px; | ||||
|     padding-right: 12px; | ||||
| } | ||||
|  | ||||
| .summary-source-button:last-child:rtl { | ||||
|     padding-left: 6px; | ||||
| } | ||||
|  | ||||
| .summary-source-button:hover .summary-source { | ||||
|     background-color: rgba(255,255,255,0.1); | ||||
| } | ||||
|  | ||||
| .summary-source-button:focus .summary-source, | ||||
| .summary-source-button:selected .summary-source { | ||||
|     background-color: rgba(255,255,255,0.33); | ||||
|     padding-left: 12px; | ||||
| } | ||||
|  | ||||
| .summary-source { | ||||
|     border-radius: 4px; | ||||
|     padding: 0 6px 0 6px; | ||||
|     transition-duration: 100; | ||||
|     padding-right: 4px; | ||||
|     padding-left: 4px; | ||||
| } | ||||
|  | ||||
| .summary-source-counter { | ||||
|     background-image: url("summary-counter.svg"); | ||||
|     background-size: 2.5em; | ||||
|     font-size: 10pt; | ||||
|     font-weight: bold; | ||||
|     height: 2.5em; | ||||
|     width: 2.5em; | ||||
|     -shell-counter-overlap-x: 4px; | ||||
|     -shell-counter-overlap-y: 4px; | ||||
|     color: white; | ||||
|     background-color: #3465A4; | ||||
|     text-shadow: black 1px 1px 0; | ||||
|     font-size: 9pt; | ||||
|     border-radius: 1em; | ||||
|     min-height: 1em; | ||||
|     min-width: 1em; | ||||
| } | ||||
|  | ||||
| .source-title { | ||||
|     padding-left: 4px; | ||||
| } | ||||
|  | ||||
| .source-title:rtl { | ||||
|     padding-left: 0px; | ||||
|     padding-right: 4px; | ||||
| } | ||||
|  | ||||
| /* App Switcher */ | ||||
| @@ -1596,7 +1579,7 @@ StScrollBar StButton#vhandle:active { | ||||
|     height: 100px; | ||||
|     border: 0px; | ||||
|     background: rgba(255,255,255,0.5); | ||||
|     background-image: url("ws-switch-arrow-up.png"); | ||||
|     background-image: url("ws-switch-arrow-up.svg"); | ||||
|     border-radius: 8px; | ||||
| } | ||||
|  | ||||
| @@ -1604,7 +1587,7 @@ StScrollBar StButton#vhandle:active { | ||||
|     height: 100px; | ||||
|     border: 0px; | ||||
|     background: rgba(255,255,255,0.5); | ||||
|     background-image: url("ws-switch-arrow-down.png"); | ||||
|     background-image: url("ws-switch-arrow-down.svg"); | ||||
|     border-radius: 8px; | ||||
| } | ||||
|  | ||||
| @@ -1650,26 +1633,35 @@ StScrollBar StButton#vhandle:active { | ||||
|     padding: 4px 32px 5px; | ||||
| } | ||||
|  | ||||
| .modal-dialog-button:insensitive { | ||||
|     color: rgb(60, 60, 60); | ||||
| } | ||||
|  | ||||
| .modal-dialog-button:focus { | ||||
|     padding: 3px 31px 4px; | ||||
| } | ||||
|  | ||||
| /* Run Dialog */ | ||||
|  | ||||
| .run-dialog-label { | ||||
|     font-size: 12pt; | ||||
|     font-weight: bold; | ||||
|     color: #999999; | ||||
|     padding-bottom: .4em; | ||||
| } | ||||
|  | ||||
| .run-dialog-error-box { | ||||
|     padding-top: 15px; | ||||
|     spacing: 5px; | ||||
| } | ||||
|  | ||||
| .modal-dialog .run-dialog-entry { | ||||
|     width: 20em; | ||||
| .run-dialog-entry { | ||||
|     font-weight: bold; | ||||
|     width: 23em; | ||||
|     selection-background-color: white; | ||||
|     selected-color: black; | ||||
| } | ||||
|  | ||||
| .run-dialog { | ||||
|     border-radius: 16px; | ||||
|  | ||||
|     padding-right: 21px; | ||||
|     padding-left: 21px; | ||||
|     padding-bottom: 15px; | ||||
|     padding-top: 15px; | ||||
| } | ||||
|  | ||||
| .lightbox { | ||||
| @@ -1709,7 +1701,6 @@ StScrollBar StButton#vhandle:active { | ||||
|     border-radius: 5px; | ||||
|     width: 32px; | ||||
|     height: 32px; | ||||
|     background-size: contain; | ||||
| } | ||||
|  | ||||
| .end-session-dialog-shutdown-icon { | ||||
| @@ -1870,7 +1861,6 @@ StScrollBar StButton#vhandle:active { | ||||
|  | ||||
| .prompt-dialog-password-box { | ||||
|     spacing: 1em; | ||||
|     padding-bottom: 1em; | ||||
| } | ||||
|  | ||||
| .prompt-dialog-error-label { | ||||
| @@ -1909,24 +1899,14 @@ StScrollBar StButton#vhandle:active { | ||||
|     color: #ff0000; | ||||
| } | ||||
|  | ||||
| .polkit-dialog-user-icon { | ||||
|     border: 2px solid #8b8b8b; | ||||
|     border-radius: 5px; | ||||
|     background-size: contain; | ||||
|     width: 48px; | ||||
|     height: 48px; | ||||
| } | ||||
|  | ||||
| /* Network Agent Dialog */ | ||||
|  | ||||
| .network-dialog-secret-table { | ||||
|     spacing-rows: 15px; | ||||
|     spacing-columns: 1em; | ||||
| } | ||||
|  | ||||
| .keyring-dialog-control-table { | ||||
|     spacing-rows: 15px; | ||||
|     spacing-columns: 1em; | ||||
| } | ||||
|  | ||||
| /* Magnifier */ | ||||
| @@ -2036,7 +2016,7 @@ StScrollBar StButton#vhandle:active { | ||||
|     min-width: 350px; | ||||
| } | ||||
|  | ||||
| .login-dialog-prompt-login-hint-message { | ||||
| .login-dialog-prompt-fingerprint-message { | ||||
|     font-size: 10.5pt; | ||||
| } | ||||
|  | ||||
| @@ -2050,6 +2030,7 @@ StScrollBar StButton#vhandle:active { | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list-item { | ||||
|     color: #666666; | ||||
|     border-radius: 10px; | ||||
|     padding: .2em; | ||||
| } | ||||
| @@ -2067,11 +2048,6 @@ StScrollBar StButton#vhandle:active { | ||||
|     padding-left: 1em; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list:expanded .login-dialog-user-list-item { | ||||
|     color: #666666; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list-item, | ||||
| .login-dialog-user-list-item:hover .login-dialog-user-list-item-name, | ||||
| .login-dialog-user-list:expanded .login-dialog-user-list-item:focus .login-dialog-user-list-item-name, | ||||
| .login-dialog-user-list:expanded .login-dialog-user-list-item:logged-in { | ||||
| @@ -2116,10 +2092,8 @@ StScrollBar StButton#vhandle:active { | ||||
|     font-size: 10.5pt; | ||||
|     font-weight: bold; | ||||
|     color: #666666; | ||||
|     padding-top: 1em; | ||||
| } | ||||
|  | ||||
| .login-dialog-not-listed-button:focus .login-dialog-not-listed-label, | ||||
| .login-dialog-not-listed-button:hover .login-dialog-not-listed-label { | ||||
|     color: #E8E8E8; | ||||
| } | ||||
| @@ -2195,78 +2169,46 @@ StScrollBar StButton#vhandle:active { | ||||
|     height: .75em; | ||||
| } | ||||
|  | ||||
|  | ||||
| .login-dialog .modal-dialog-button { | ||||
|     border: 1px solid white; | ||||
|     border-radius: 5px; | ||||
|     padding: 3px 18px; | ||||
| } | ||||
|  | ||||
| .login-dialog .modal-dialog-button:focus { | ||||
|     padding: 2px 17px; | ||||
| } | ||||
|  | ||||
| .login-dialog .modal-dialog-button:default { | ||||
|     background-gradient-start: #6793c4; | ||||
|     background-gradient-end: #335d8f; | ||||
|     background-gradient-direction: vertical; | ||||
|     border-color: #16335d; | ||||
|     border: 2px solid #16335d; | ||||
| } | ||||
|  | ||||
| .login-dialog .modal-dialog-button:default:focus { | ||||
|     border: 2px solid #377fe7; | ||||
| } | ||||
|  | ||||
| .login-dialog .modal-dialog-button:default:hover { | ||||
| .login-dialog .modal-dialog-button:hover { | ||||
|     background-gradient-start: #74a0d0; | ||||
|     background-gradient-end: #436d9f; | ||||
| } | ||||
|  | ||||
| .login-dialog .modal-dialog-button:default:active, | ||||
| .login-dialog .modal-dialog-button:default:pressed { | ||||
| .login-dialog .modal-dialog-button:active, | ||||
| .login-dialog .modal-dialog-button:pressed { | ||||
|     background-gradient-start: #436d9f; | ||||
|     background-gradient-end: #74a0d0; | ||||
| } | ||||
|  | ||||
| .login-dialog .modal-dialog-button:default:insensitive { | ||||
|     border-color: #666666; | ||||
|     color: #9f9f9f; | ||||
|     background-gradient-direction: none; | ||||
|     background-color: rgba(102, 102, 102, 0.15); | ||||
| } | ||||
|  | ||||
| .login-dialog-message-warning { | ||||
|     color: orange; | ||||
| } | ||||
|  | ||||
| .unlock-dialog-user-name-container { | ||||
|     spacing: .4em; | ||||
| } | ||||
|  | ||||
| /* Screen shield */ | ||||
|  | ||||
| .screen-shield-background { | ||||
|     background: black; | ||||
|     box-shadow: 0px 4px 8px rgba(0,0,0,0.9); | ||||
| } | ||||
|  | ||||
| #lockDialogGroup { | ||||
| #screenShieldGroup { | ||||
|     background: #2e3436 url(noise-texture.png); | ||||
|     background-repeat: repeat; | ||||
| } | ||||
|  | ||||
| .screen-shield-arrows { | ||||
|     padding-bottom: 3em; | ||||
| } | ||||
|  | ||||
| .screen-shield-arrows Gjs_Arrow { | ||||
|     color: white; | ||||
|     width: 80px; | ||||
|     height: 48px; | ||||
|     -arrow-thickness: 12px; | ||||
|     -arrow-shadow: 0 1px 1px rgba(0,0,0,0.4); | ||||
| } | ||||
|  | ||||
| .screen-shield-contents-box { | ||||
|     spacing: 48px; | ||||
| #screenShieldGroup .arrow { | ||||
|     color: #333333; | ||||
|     width: 100px; | ||||
|     height: 50px; | ||||
| } | ||||
|  | ||||
| .screen-shield-clock { | ||||
| @@ -2278,12 +2220,12 @@ StScrollBar StButton#vhandle:active { | ||||
| } | ||||
|  | ||||
| .screen-shield-clock-time { | ||||
|     font-size: 72pt; | ||||
|     font-size: 86px; | ||||
|     text-shadow: 0px 2px 2px rgba(0,0,0,0.4); | ||||
| } | ||||
|  | ||||
| .screen-shield-clock-date { | ||||
|     font-size: 28pt; | ||||
|     font-size: 48px; | ||||
| } | ||||
|  | ||||
| #screenShieldNotifications { | ||||
| @@ -2291,12 +2233,11 @@ StScrollBar StButton#vhandle:active { | ||||
|     background-color: rgba(0.0, 0.0, 0.0, 0.9); | ||||
|     border: 2px solid #868686; | ||||
|     max-height: 500px; | ||||
|     padding: 18px 0; | ||||
|     box-shadow: .5em .5em 20px rgba(0, 0, 0, 0.5); | ||||
|     padding: 12px 0; | ||||
| } | ||||
|  | ||||
| .screen-shield-notifications-box { | ||||
|     spacing: 18px; | ||||
|     spacing: 12px; | ||||
| } | ||||
|  | ||||
| .screen-shield-notification-source { | ||||
| @@ -2307,7 +2248,6 @@ StScrollBar StButton#vhandle:active { | ||||
| .screen-shield-notification-label { | ||||
|     font-size: 1.2em; | ||||
|     font-weight: bold; | ||||
|     color: #babdb6; | ||||
| } | ||||
|  | ||||
| /* Remove background from notifications, otherwise | ||||
| @@ -2325,17 +2265,3 @@ StScrollBar StButton#vhandle:active { | ||||
|     padding-bottom: 0px; | ||||
| } | ||||
|  | ||||
| .osd-window { | ||||
|     color: #ededed; | ||||
|     background-color: rgba(33, 37, 38, 0.80); | ||||
|     border-radius: 15px; | ||||
|     text-shadow: 0 1px rgba(0, 0, 0, 0.75); | ||||
|  | ||||
|     padding: 40px; | ||||
|     spacing: 5px; | ||||
| } | ||||
|  | ||||
| .osd-progress-bar { | ||||
|     height: 0.8em; | ||||
|     border: 1px solid; | ||||
| } | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 25 KiB | 
| @@ -1,120 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948)  --> | ||||
|  | ||||
| <svg | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:xlink="http://www.w3.org/1999/xlink" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    version="1.0" | ||||
|    id="Foreground" | ||||
|    x="0px" | ||||
|    y="0px" | ||||
|    width="32" | ||||
|    height="32" | ||||
|    viewBox="0 0 23.272727 23.272727" | ||||
|    enable-background="new 0 0 16 16" | ||||
|    xml:space="preserve" | ||||
|    sodipodi:version="0.32" | ||||
|    inkscape:version="0.48.2 r9819" | ||||
|    sodipodi:docname="summary-counter.svg" | ||||
|    inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata | ||||
|      id="metadata2399"><rdf:RDF><cc:Work | ||||
|          rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs | ||||
|      id="defs2397"><linearGradient | ||||
|        id="linearGradient3173"><stop | ||||
|          style="stop-color:#c4c4c4;stop-opacity:1;" | ||||
|          offset="0" | ||||
|          id="stop3175" /><stop | ||||
|          style="stop-color:#ffffff;stop-opacity:1;" | ||||
|          offset="1" | ||||
|          id="stop3177" /></linearGradient><inkscape:perspective | ||||
|        sodipodi:type="inkscape:persp3d" | ||||
|        inkscape:vp_x="0 : 8 : 1" | ||||
|        inkscape:vp_y="0 : 1000 : 0" | ||||
|        inkscape:vp_z="16 : 8 : 1" | ||||
|        inkscape:persp3d-origin="8 : 5.3333333 : 1" | ||||
|        id="perspective2401" /><filter | ||||
|        color-interpolation-filters="sRGB" | ||||
|        inkscape:collect="always" | ||||
|        id="filter16494-4" | ||||
|        x="-0.20989846" | ||||
|        width="1.4197969" | ||||
|        y="-0.20903821" | ||||
|        height="1.4180764"><feGaussianBlur | ||||
|          inkscape:collect="always" | ||||
|          stdDeviation="1.3282637" | ||||
|          id="feGaussianBlur16496-8" /></filter><radialGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient16498-6" | ||||
|        id="radialGradient16504-1" | ||||
|        cx="7.6582627" | ||||
|        cy="5.8191104" | ||||
|        fx="7.6582627" | ||||
|        fy="5.8191104" | ||||
|        r="8.6928644" | ||||
|        gradientTransform="matrix(1.0474339,0,0,1.0517402,-0.3632615,-0.42032492)" | ||||
|        gradientUnits="userSpaceOnUse" /><linearGradient | ||||
|        inkscape:collect="always" | ||||
|        id="linearGradient16498-6"><stop | ||||
|          style="stop-color:#9FD0FF;stop-opacity:1" | ||||
|          offset="0" | ||||
|          id="stop16500-8" /><stop | ||||
|          style="stop-color:#3465A4;stop-opacity:1" | ||||
|          offset="1" | ||||
|          id="stop16502-0" /></linearGradient></defs><sodipodi:namedview | ||||
|      inkscape:window-height="709" | ||||
|      inkscape:window-width="1366" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:pageopacity="0" | ||||
|      guidetolerance="10.0" | ||||
|      gridtolerance="10.0" | ||||
|      objecttolerance="10.0" | ||||
|      borderopacity="1.0" | ||||
|      bordercolor="#666666" | ||||
|      pagecolor="#000000" | ||||
|      id="base" | ||||
|      showgrid="false" | ||||
|      inkscape:zoom="11.313708" | ||||
|      inkscape:cx="15.386407" | ||||
|      inkscape:cy="13.739577" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="1179" | ||||
|      inkscape:current-layer="g16402-8" | ||||
|      showguides="true" | ||||
|      inkscape:guide-bbox="true" | ||||
|      borderlayer="true" | ||||
|      inkscape:showpageshadow="false" | ||||
|      inkscape:window-maximized="1"><inkscape:grid | ||||
|        type="xygrid" | ||||
|        id="grid11246" | ||||
|        empspacing="5" | ||||
|        visible="true" | ||||
|        enabled="true" | ||||
|        snapvisiblegridlinesonly="true" /></sodipodi:namedview><g | ||||
|      style="display:inline" | ||||
|      id="g16402-8" | ||||
|      transform="translate(4.7533483,2.8238929)"><g | ||||
|        id="g3175-4" | ||||
|        transform="translate(-0.89995416,0.94028614)"><path | ||||
|          sodipodi:type="inkscape:offset" | ||||
|          inkscape:radius="0" | ||||
|          inkscape:original="M 7.65625 0.125 C 3.2589349 0.125 -0.3125 3.7070002 -0.3125 8.125 C -0.3125 12.543001 3.2589349 16.125 7.65625 16.125 C 12.053566 16.125 15.625 12.543001 15.625 8.125 C 15.625 3.7070002 12.053566 0.125 7.65625 0.125 z " | ||||
|          xlink:href="#path2394-32" | ||||
|          style="opacity:0.52994014;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.18181825;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter16494-4);enable-background:accumulate" | ||||
|          id="path16480-5" | ||||
|          inkscape:href="#path2394-32" | ||||
|          d="m 7.65625,0.125 c -4.3973151,0 -7.96875,3.5820002 -7.96875,8 0,4.418001 3.5714349,8 7.96875,8 4.397316,0 7.96875,-3.581999 7.96875,-8 0,-4.4179998 -3.571434,-8 -7.96875,-8 z" | ||||
|          transform="translate(0,1.028519)" /><path | ||||
|          clip-rule="evenodd" | ||||
|          d="m -0.30428257,8.1237596 c 0,-4.4179998 3.56522987,-7.9999996 7.96254497,-7.9999996 4.3973156,0 7.9625456,3.5819998 7.9625456,7.9999996 0,4.4180014 -3.56523,8.0000004 -7.9625456,8.0000004 -4.3973151,0 -7.96254497,-3.581999 -7.96254497,-8.0000004 z" | ||||
|          id="path2394-32" | ||||
|          style="color:#000000;fill:url(#radialGradient16504-1);fill-opacity:1;fill-rule:nonzero;stroke:#eeeeec;stroke-width:1.4545455;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" | ||||
|          sodipodi:nodetypes="csssc" | ||||
|          inkscape:connector-curvature="0" /><g | ||||
|          id="g3172-6" /></g></g></svg> | ||||
| Before Width: | Height: | Size: 5.4 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 850 B | 
							
								
								
									
										376
									
								
								data/theme/ws-switch-arrow-down.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										376
									
								
								data/theme/ws-switch-arrow-down.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,376 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
|  | ||||
| <svg | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:xlink="http://www.w3.org/1999/xlink" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    version="1.1" | ||||
|    width="96" | ||||
|    height="96" | ||||
|    id="svg25070" | ||||
|    inkscape:version="0.48.0 r9654" | ||||
|    sodipodi:docname="ws-switch-arrow-down.svg"> | ||||
|   <metadata | ||||
|      id="metadata3353"> | ||||
|     <rdf:RDF> | ||||
|       <cc:Work | ||||
|          rdf:about=""> | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   <sodipodi:namedview | ||||
|      pagecolor="#ffffff" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1" | ||||
|      objecttolerance="10" | ||||
|      gridtolerance="10" | ||||
|      guidetolerance="10" | ||||
|      inkscape:pageopacity="0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:window-width="718" | ||||
|      inkscape:window-height="480" | ||||
|      id="namedview3351" | ||||
|      showgrid="false" | ||||
|      inkscape:zoom="2.6979167" | ||||
|      inkscape:cx="48" | ||||
|      inkscape:cy="48" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="26" | ||||
|      inkscape:window-maximized="0" | ||||
|      inkscape:current-layer="svg25070" /> | ||||
|   <defs | ||||
|      id="defs25072"> | ||||
|     <linearGradient | ||||
|        x1="-86.552246" | ||||
|        y1="185.439" | ||||
|        x2="-83.37072" | ||||
|        y2="197.31261" | ||||
|        id="linearGradient24957" | ||||
|        xlink:href="#linearGradient4034-0-4" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="translate(6,0)" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient4034-0-4"> | ||||
|       <stop | ||||
|          id="stop4036-5-7" | ||||
|          style="stop-color:#eeeeec;stop-opacity:1" | ||||
|          offset="0" /> | ||||
|       <stop | ||||
|          id="stop4038-9-6" | ||||
|          style="stop-color:#babdb6;stop-opacity:1" | ||||
|          offset="1" /> | ||||
|     </linearGradient> | ||||
|     <filter | ||||
|        x="0" | ||||
|        y="0" | ||||
|        width="1" | ||||
|        height="1" | ||||
|        color-interpolation-filters="sRGB" | ||||
|        id="filter24765"> | ||||
|       <feColorMatrix | ||||
|          result="fbSourceGraphic" | ||||
|          values="1" | ||||
|          type="saturate" | ||||
|          id="feColorMatrix24767" /> | ||||
|       <feColorMatrix | ||||
|          values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" | ||||
|          in="fbSourceGraphic" | ||||
|          id="feColorMatrix24769" /> | ||||
|     </filter> | ||||
|     <linearGradient | ||||
|        x1="-74.520325" | ||||
|        y1="169.06032" | ||||
|        x2="-74.520325" | ||||
|        y2="205.94189" | ||||
|        id="linearGradient24955" | ||||
|        xlink:href="#linearGradient4632-1-3-9-3-2" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="translate(-5,0)" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient4632-1-3-9-3-2"> | ||||
|       <stop | ||||
|          id="stop4634-1-8-3-9-0" | ||||
|          style="stop-color:#eeeeec;stop-opacity:1" | ||||
|          offset="0" /> | ||||
|       <stop | ||||
|          id="stop4636-1-9-9-8-8" | ||||
|          style="stop-color:#ffffff;stop-opacity:1" | ||||
|          offset="0.0274937" /> | ||||
|       <stop | ||||
|          id="stop4638-8-3-9-6-6" | ||||
|          style="stop-color:#f2f2f2;stop-opacity:1" | ||||
|          offset="0.274937" /> | ||||
|       <stop | ||||
|          id="stop4640-8-5-7-8-9" | ||||
|          style="stop-color:#eeeeec;stop-opacity:1" | ||||
|          offset="0.38707438" /> | ||||
|       <stop | ||||
|          id="stop4642-5-41-9-6-9" | ||||
|          style="stop-color:#d9dad8;stop-opacity:1" | ||||
|          offset="0.66528589" /> | ||||
|       <stop | ||||
|          id="stop4644-5-2-7-9-2" | ||||
|          style="stop-color:#dfe0dd;stop-opacity:1" | ||||
|          offset="0.76745707" /> | ||||
|       <stop | ||||
|          id="stop4646-3-2-3-7-3" | ||||
|          style="stop-color:#f0f0f0;stop-opacity:1" | ||||
|          offset="1" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="-33.412369" | ||||
|        cy="185.74171" | ||||
|        r="2.3554697" | ||||
|        fx="-33.412369" | ||||
|        fy="185.74171" | ||||
|        id="radialGradient24959" | ||||
|        xlink:href="#linearGradient4869-4-1" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="matrix(1.0075,0,0,1.0075,-5.4544,-1.25141)" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient4869-4-1"> | ||||
|       <stop | ||||
|          id="stop4871-6-2" | ||||
|          style="stop-color:#ffffff;stop-opacity:1" | ||||
|          offset="0" /> | ||||
|       <stop | ||||
|          id="stop4879-7-4" | ||||
|          style="stop-color:#eeeeec;stop-opacity:1" | ||||
|          offset="0.31807542" /> | ||||
|       <stop | ||||
|          id="stop4877-6-1" | ||||
|          style="stop-color:#c8c9c6;stop-opacity:1" | ||||
|          offset="0.74691135" /> | ||||
|       <stop | ||||
|          id="stop4873-1-0" | ||||
|          style="stop-color:#d3d7cf;stop-opacity:1" | ||||
|          offset="1" /> | ||||
|     </linearGradient> | ||||
|     <filter | ||||
|        x="0" | ||||
|        y="0" | ||||
|        width="1" | ||||
|        height="1" | ||||
|        color-interpolation-filters="sRGB" | ||||
|        id="filter25011"> | ||||
|       <feColorMatrix | ||||
|          result="fbSourceGraphic" | ||||
|          values="1" | ||||
|          type="saturate" | ||||
|          id="feColorMatrix25013" /> | ||||
|       <feColorMatrix | ||||
|          values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" | ||||
|          in="fbSourceGraphic" | ||||
|          id="feColorMatrix25015" /> | ||||
|     </filter> | ||||
|     <radialGradient | ||||
|        cx="-33.412369" | ||||
|        cy="185.74171" | ||||
|        r="2.3554697" | ||||
|        fx="-33.412369" | ||||
|        fy="185.74171" | ||||
|        id="radialGradient24961" | ||||
|        xlink:href="#linearGradient4869-4-0" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="matrix(1.0075,0,0,1.0075,-5.4544,-1.25141)" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient4869-4-0"> | ||||
|       <stop | ||||
|          id="stop4871-6-8" | ||||
|          style="stop-color:#ffffff;stop-opacity:1" | ||||
|          offset="0" /> | ||||
|       <stop | ||||
|          id="stop4879-7-5" | ||||
|          style="stop-color:#eeeeec;stop-opacity:1" | ||||
|          offset="0.31807542" /> | ||||
|       <stop | ||||
|          id="stop4877-6-5" | ||||
|          style="stop-color:#c8c9c6;stop-opacity:1" | ||||
|          offset="0.74691135" /> | ||||
|       <stop | ||||
|          id="stop4873-1-4" | ||||
|          style="stop-color:#d3d7cf;stop-opacity:1" | ||||
|          offset="1" /> | ||||
|     </linearGradient> | ||||
|     <filter | ||||
|        x="0" | ||||
|        y="0" | ||||
|        width="1" | ||||
|        height="1" | ||||
|        color-interpolation-filters="sRGB" | ||||
|        id="filter25023"> | ||||
|       <feColorMatrix | ||||
|          result="fbSourceGraphic" | ||||
|          values="1" | ||||
|          type="saturate" | ||||
|          id="feColorMatrix25025" /> | ||||
|       <feColorMatrix | ||||
|          values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" | ||||
|          in="fbSourceGraphic" | ||||
|          id="feColorMatrix25027" /> | ||||
|     </filter> | ||||
|     <linearGradient | ||||
|        x1="-39.858727" | ||||
|        y1="184.61784" | ||||
|        x2="-38.244785" | ||||
|        y2="188.84898" | ||||
|        id="linearGradient24963" | ||||
|        xlink:href="#linearGradient4941" | ||||
|        gradientUnits="userSpaceOnUse" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient4941"> | ||||
|       <stop | ||||
|          id="stop4943" | ||||
|          style="stop-color:#ffffff;stop-opacity:1" | ||||
|          offset="0" /> | ||||
|       <stop | ||||
|          id="stop4945" | ||||
|          style="stop-color:#ffffff;stop-opacity:0" | ||||
|          offset="1" /> | ||||
|     </linearGradient> | ||||
|     <filter | ||||
|        x="0" | ||||
|        y="0" | ||||
|        width="1" | ||||
|        height="1" | ||||
|        color-interpolation-filters="sRGB" | ||||
|        id="filter25033"> | ||||
|       <feColorMatrix | ||||
|          result="fbSourceGraphic" | ||||
|          values="1" | ||||
|          type="saturate" | ||||
|          id="feColorMatrix25035" /> | ||||
|       <feColorMatrix | ||||
|          values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" | ||||
|          in="fbSourceGraphic" | ||||
|          id="feColorMatrix25037" /> | ||||
|     </filter> | ||||
|     <linearGradient | ||||
|        x1="-39.858727" | ||||
|        y1="184.61784" | ||||
|        x2="-38.244785" | ||||
|        y2="188.84898" | ||||
|        id="linearGradient24965" | ||||
|        xlink:href="#linearGradient4941-7" | ||||
|        gradientUnits="userSpaceOnUse" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient4941-7"> | ||||
|       <stop | ||||
|          id="stop4943-2" | ||||
|          style="stop-color:#ffffff;stop-opacity:1" | ||||
|          offset="0" /> | ||||
|       <stop | ||||
|          id="stop4945-5" | ||||
|          style="stop-color:#ffffff;stop-opacity:0" | ||||
|          offset="1" /> | ||||
|     </linearGradient> | ||||
|     <filter | ||||
|        x="0" | ||||
|        y="0" | ||||
|        width="1" | ||||
|        height="1" | ||||
|        color-interpolation-filters="sRGB" | ||||
|        id="filter25043"> | ||||
|       <feColorMatrix | ||||
|          result="fbSourceGraphic" | ||||
|          values="1" | ||||
|          type="saturate" | ||||
|          id="feColorMatrix25045" /> | ||||
|       <feColorMatrix | ||||
|          values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" | ||||
|          in="fbSourceGraphic" | ||||
|          id="feColorMatrix25047" /> | ||||
|     </filter> | ||||
|     <filter | ||||
|        x="0" | ||||
|        y="0" | ||||
|        width="1" | ||||
|        height="1" | ||||
|        color-interpolation-filters="sRGB" | ||||
|        id="filter25049"> | ||||
|       <feColorMatrix | ||||
|          result="fbSourceGraphic" | ||||
|          values="1" | ||||
|          type="saturate" | ||||
|          id="feColorMatrix25051" /> | ||||
|       <feColorMatrix | ||||
|          values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" | ||||
|          in="fbSourceGraphic" | ||||
|          id="feColorMatrix25053" /> | ||||
|     </filter> | ||||
|     <filter | ||||
|        x="0" | ||||
|        y="0" | ||||
|        width="1" | ||||
|        height="1" | ||||
|        color-interpolation-filters="sRGB" | ||||
|        id="filter25055"> | ||||
|       <feColorMatrix | ||||
|          result="fbSourceGraphic" | ||||
|          values="1" | ||||
|          type="saturate" | ||||
|          id="feColorMatrix25057" /> | ||||
|       <feColorMatrix | ||||
|          values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" | ||||
|          in="fbSourceGraphic" | ||||
|          id="feColorMatrix25059" /> | ||||
|     </filter> | ||||
|   </defs> | ||||
|   <g | ||||
|      transform="matrix(0,1,-1,0,48.0003,4.1307112e-7)" | ||||
|      id="layer1"> | ||||
|     <g | ||||
|        transform="matrix(-2,0,0,2,-97.2497,-374.967)" | ||||
|        id="g4030-1-8" | ||||
|        style="stroke:#000000;stroke-opacity:1;display:inline"> | ||||
|       <path | ||||
|          d="m -72.5,173.5 -14,14 14,14" | ||||
|          id="path3165-7-3" | ||||
|          style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" | ||||
|          inkscape:connector-curvature="0" /> | ||||
|     </g> | ||||
|     <path | ||||
|        d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" | ||||
|        transform="matrix(-3.34328,0,0,3.34328,-89.2797,-623.176)" | ||||
|        id="path4050-2-7-9-4" | ||||
|        style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible" | ||||
|        inkscape:connector-curvature="0" /> | ||||
|     <path | ||||
|        d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" | ||||
|        transform="matrix(-3.34328,0,0,3.34328,-111.2797,-623.176)" | ||||
|        id="path4050-2-7-9-4-8" | ||||
|        style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible" | ||||
|        inkscape:connector-curvature="0" /> | ||||
|     <path | ||||
|        d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" | ||||
|        transform="matrix(-2.86565,0,0,2.86565,-70.8457,-534.143)" | ||||
|        id="path4050-2-7-9-4-0" | ||||
|        style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" | ||||
|        inkscape:connector-curvature="0" /> | ||||
|     <path | ||||
|        d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" | ||||
|        transform="matrix(-2.86565,0,0,2.86565,-92.8457,-534.143)" | ||||
|        id="path4050-2-7-9-4-0-9" | ||||
|        style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" | ||||
|        inkscape:connector-curvature="0" /> | ||||
|     <path | ||||
|        d="m 47.87528,-34.0295 c 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25 -32.25,32.25 c -2.2253,2.2253 -6.2747,2.2253 -8.5,0 -2.2253,-2.22528 -2.2253,-6.2747 0,-8.5 l 23.75,-23.75 -23.75,-23.75 c -1.73168,-1.6731 -2.295,-4.44228 -1.3546,-6.65894 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 z" | ||||
|        id="path3165-7-3-1" | ||||
|        style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans" | ||||
|        inkscape:connector-curvature="0" /> | ||||
|     <path | ||||
|        d="m 41.8316,28.09418 c -0.014,-1.58898 0.54158,-3.18406 1.66868,-4.31118 l 23.75,-23.75 m -25.1046,-30.40894 c 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25" | ||||
|        id="path3165-7-3-1-9" | ||||
|        style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans" | ||||
|        inkscape:connector-curvature="0" /> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 13 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 841 B | 
							
								
								
									
										447
									
								
								data/theme/ws-switch-arrow-up.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										447
									
								
								data/theme/ws-switch-arrow-up.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,447 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
|  | ||||
| <svg | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:xlink="http://www.w3.org/1999/xlink" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    width="96" | ||||
|    height="96" | ||||
|    id="svg25070" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.48.0 r9654" | ||||
|    sodipodi:docname="ws-switch-arrow-up.svg"> | ||||
|   <defs | ||||
|      id="defs25072"> | ||||
|     <inkscape:perspective | ||||
|        sodipodi:type="inkscape:persp3d" | ||||
|        inkscape:vp_x="0 : 24 : 1" | ||||
|        inkscape:vp_y="0 : 1000 : 0" | ||||
|        inkscape:vp_z="48 : 24 : 1" | ||||
|        inkscape:persp3d-origin="24 : 16 : 1" | ||||
|        id="perspective25078" /> | ||||
|     <inkscape:perspective | ||||
|        id="perspective24985" | ||||
|        inkscape:persp3d-origin="0.5 : 0.33333333 : 1" | ||||
|        inkscape:vp_z="1 : 0.5 : 1" | ||||
|        inkscape:vp_y="0 : 1000 : 0" | ||||
|        inkscape:vp_x="0 : 0.5 : 1" | ||||
|        sodipodi:type="inkscape:persp3d" /> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient4034-0-4" | ||||
|        id="linearGradient24957" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="translate(6)" | ||||
|        x1="-86.552246" | ||||
|        y1="185.439" | ||||
|        x2="-83.37072" | ||||
|        y2="197.31261" /> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        id="linearGradient4034-0-4"> | ||||
|       <stop | ||||
|          style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" | ||||
|          offset="0" | ||||
|          id="stop4036-5-7" /> | ||||
|       <stop | ||||
|          style="stop-color: rgb(186, 189, 182); stop-opacity: 1;" | ||||
|          offset="1" | ||||
|          id="stop4038-9-6" /> | ||||
|     </linearGradient> | ||||
|     <filter | ||||
|        id="filter24765" | ||||
|        inkscape:label="Invert" | ||||
|        x="0" | ||||
|        y="0" | ||||
|        width="1" | ||||
|        height="1" | ||||
|        inkscape:menu="Color" | ||||
|        inkscape:menu-tooltip="Invert colors" | ||||
|        color-interpolation-filters="sRGB"> | ||||
|       <feColorMatrix | ||||
|          id="feColorMatrix24767" | ||||
|          type="saturate" | ||||
|          values="1" | ||||
|          result="fbSourceGraphic" /> | ||||
|       <feColorMatrix | ||||
|          id="feColorMatrix24769" | ||||
|          in="fbSourceGraphic" | ||||
|          values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" /> | ||||
|     </filter> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient4632-1-3-9-3-2" | ||||
|        id="linearGradient24955" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="translate(-5)" | ||||
|        x1="-74.520325" | ||||
|        y1="169.06032" | ||||
|        x2="-74.520325" | ||||
|        y2="205.94189" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient4632-1-3-9-3-2"> | ||||
|       <stop | ||||
|          style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" | ||||
|          offset="0" | ||||
|          id="stop4634-1-8-3-9-0" /> | ||||
|       <stop | ||||
|          id="stop4636-1-9-9-8-8" | ||||
|          offset="0.0274937" | ||||
|          style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" /> | ||||
|       <stop | ||||
|          id="stop4638-8-3-9-6-6" | ||||
|          offset="0.274937" | ||||
|          style="stop-color: rgb(242, 242, 242); stop-opacity: 1;" /> | ||||
|       <stop | ||||
|          id="stop4640-8-5-7-8-9" | ||||
|          offset="0.38707438" | ||||
|          style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" /> | ||||
|       <stop | ||||
|          id="stop4642-5-41-9-6-9" | ||||
|          offset="0.66528589" | ||||
|          style="stop-color: rgb(217, 218, 216); stop-opacity: 1;" /> | ||||
|       <stop | ||||
|          id="stop4644-5-2-7-9-2" | ||||
|          offset="0.76745707" | ||||
|          style="stop-color: rgb(223, 224, 221); stop-opacity: 1;" /> | ||||
|       <stop | ||||
|          style="stop-color: rgb(240, 240, 240); stop-opacity: 1;" | ||||
|          offset="1" | ||||
|          id="stop4646-3-2-3-7-3" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient4869-4-1" | ||||
|        id="radialGradient24959" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)" | ||||
|        cx="-33.412369" | ||||
|        cy="185.74171" | ||||
|        fx="-33.412369" | ||||
|        fy="185.74171" | ||||
|        r="2.3554697" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient4869-4-1"> | ||||
|       <stop | ||||
|          style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" | ||||
|          offset="0" | ||||
|          id="stop4871-6-2" /> | ||||
|       <stop | ||||
|          id="stop4879-7-4" | ||||
|          offset="0.31807542" | ||||
|          style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" /> | ||||
|       <stop | ||||
|          id="stop4877-6-1" | ||||
|          offset="0.74691135" | ||||
|          style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" /> | ||||
|       <stop | ||||
|          style="stop-color: rgb(211, 215, 207); stop-opacity: 1;" | ||||
|          offset="1" | ||||
|          id="stop4873-1-0" /> | ||||
|     </linearGradient> | ||||
|     <filter | ||||
|        id="filter25011" | ||||
|        inkscape:label="Invert" | ||||
|        x="0" | ||||
|        y="0" | ||||
|        width="1" | ||||
|        height="1" | ||||
|        inkscape:menu="Color" | ||||
|        inkscape:menu-tooltip="Invert colors" | ||||
|        color-interpolation-filters="sRGB"> | ||||
|       <feColorMatrix | ||||
|          id="feColorMatrix25013" | ||||
|          type="saturate" | ||||
|          values="1" | ||||
|          result="fbSourceGraphic" /> | ||||
|       <feColorMatrix | ||||
|          id="feColorMatrix25015" | ||||
|          in="fbSourceGraphic" | ||||
|          values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" /> | ||||
|     </filter> | ||||
|     <radialGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient4869-4-0" | ||||
|        id="radialGradient24961" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)" | ||||
|        cx="-33.412369" | ||||
|        cy="185.74171" | ||||
|        fx="-33.412369" | ||||
|        fy="185.74171" | ||||
|        r="2.3554697" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient4869-4-0"> | ||||
|       <stop | ||||
|          style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" | ||||
|          offset="0" | ||||
|          id="stop4871-6-8" /> | ||||
|       <stop | ||||
|          id="stop4879-7-5" | ||||
|          offset="0.31807542" | ||||
|          style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" /> | ||||
|       <stop | ||||
|          id="stop4877-6-5" | ||||
|          offset="0.74691135" | ||||
|          style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" /> | ||||
|       <stop | ||||
|          style="stop-color: rgb(211, 215, 207); stop-opacity: 1;" | ||||
|          offset="1" | ||||
|          id="stop4873-1-4" /> | ||||
|     </linearGradient> | ||||
|     <filter | ||||
|        id="filter25023" | ||||
|        inkscape:label="Invert" | ||||
|        x="0" | ||||
|        y="0" | ||||
|        width="1" | ||||
|        height="1" | ||||
|        inkscape:menu="Color" | ||||
|        inkscape:menu-tooltip="Invert colors" | ||||
|        color-interpolation-filters="sRGB"> | ||||
|       <feColorMatrix | ||||
|          id="feColorMatrix25025" | ||||
|          type="saturate" | ||||
|          values="1" | ||||
|          result="fbSourceGraphic" /> | ||||
|       <feColorMatrix | ||||
|          id="feColorMatrix25027" | ||||
|          in="fbSourceGraphic" | ||||
|          values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" /> | ||||
|     </filter> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient4941" | ||||
|        id="linearGradient24963" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        x1="-39.858727" | ||||
|        y1="184.61784" | ||||
|        x2="-38.244785" | ||||
|        y2="188.84898" /> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        id="linearGradient4941"> | ||||
|       <stop | ||||
|          style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" | ||||
|          offset="0" | ||||
|          id="stop4943" /> | ||||
|       <stop | ||||
|          style="stop-color: rgb(255, 255, 255); stop-opacity: 0;" | ||||
|          offset="1" | ||||
|          id="stop4945" /> | ||||
|     </linearGradient> | ||||
|     <filter | ||||
|        id="filter25033" | ||||
|        inkscape:label="Invert" | ||||
|        x="0" | ||||
|        y="0" | ||||
|        width="1" | ||||
|        height="1" | ||||
|        inkscape:menu="Color" | ||||
|        inkscape:menu-tooltip="Invert colors" | ||||
|        color-interpolation-filters="sRGB"> | ||||
|       <feColorMatrix | ||||
|          id="feColorMatrix25035" | ||||
|          type="saturate" | ||||
|          values="1" | ||||
|          result="fbSourceGraphic" /> | ||||
|       <feColorMatrix | ||||
|          id="feColorMatrix25037" | ||||
|          in="fbSourceGraphic" | ||||
|          values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" /> | ||||
|     </filter> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient4941-7" | ||||
|        id="linearGradient24965" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        x1="-39.858727" | ||||
|        y1="184.61784" | ||||
|        x2="-38.244785" | ||||
|        y2="188.84898" /> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        id="linearGradient4941-7"> | ||||
|       <stop | ||||
|          style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" | ||||
|          offset="0" | ||||
|          id="stop4943-2" /> | ||||
|       <stop | ||||
|          style="stop-color: rgb(255, 255, 255); stop-opacity: 0;" | ||||
|          offset="1" | ||||
|          id="stop4945-5" /> | ||||
|     </linearGradient> | ||||
|     <filter | ||||
|        id="filter25043" | ||||
|        inkscape:label="Invert" | ||||
|        x="0" | ||||
|        y="0" | ||||
|        width="1" | ||||
|        height="1" | ||||
|        inkscape:menu="Color" | ||||
|        inkscape:menu-tooltip="Invert colors" | ||||
|        color-interpolation-filters="sRGB"> | ||||
|       <feColorMatrix | ||||
|          id="feColorMatrix25045" | ||||
|          type="saturate" | ||||
|          values="1" | ||||
|          result="fbSourceGraphic" /> | ||||
|       <feColorMatrix | ||||
|          id="feColorMatrix25047" | ||||
|          in="fbSourceGraphic" | ||||
|          values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" /> | ||||
|     </filter> | ||||
|     <filter | ||||
|        id="filter25049" | ||||
|        inkscape:label="Invert" | ||||
|        x="0" | ||||
|        y="0" | ||||
|        width="1" | ||||
|        height="1" | ||||
|        inkscape:menu="Color" | ||||
|        inkscape:menu-tooltip="Invert colors" | ||||
|        color-interpolation-filters="sRGB"> | ||||
|       <feColorMatrix | ||||
|          id="feColorMatrix25051" | ||||
|          type="saturate" | ||||
|          values="1" | ||||
|          result="fbSourceGraphic" /> | ||||
|       <feColorMatrix | ||||
|          id="feColorMatrix25053" | ||||
|          in="fbSourceGraphic" | ||||
|          values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" /> | ||||
|     </filter> | ||||
|     <filter | ||||
|        id="filter25055" | ||||
|        inkscape:label="Invert" | ||||
|        x="0" | ||||
|        y="0" | ||||
|        width="1" | ||||
|        height="1" | ||||
|        inkscape:menu="Color" | ||||
|        inkscape:menu-tooltip="Invert colors" | ||||
|        color-interpolation-filters="sRGB"> | ||||
|       <feColorMatrix | ||||
|          id="feColorMatrix25057" | ||||
|          type="saturate" | ||||
|          values="1" | ||||
|          result="fbSourceGraphic" /> | ||||
|       <feColorMatrix | ||||
|          id="feColorMatrix25059" | ||||
|          in="fbSourceGraphic" | ||||
|          values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" /> | ||||
|     </filter> | ||||
|   </defs> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
|      pagecolor="#ffffff" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="0.0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="2.8284271" | ||||
|      inkscape:cx="-12.356322" | ||||
|      inkscape:cy="57.536221" | ||||
|      inkscape:current-layer="layer1" | ||||
|      showgrid="true" | ||||
|      inkscape:grid-bbox="true" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:window-width="1200" | ||||
|      inkscape:window-height="840" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="26" | ||||
|      inkscape:window-maximized="0" /> | ||||
|   <metadata | ||||
|      id="metadata25075"> | ||||
|     <rdf:RDF> | ||||
|       <cc:Work | ||||
|          rdf:about=""> | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|         <dc:title /> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   <g | ||||
|      id="layer1" | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      transform="translate(0, 48)"> | ||||
|     <g | ||||
|        id="g3181" | ||||
|        transform="matrix(0,1,-1,0,48.0003,-48)"> | ||||
|       <g | ||||
|          style="stroke:#000000;stroke-opacity:1;display:inline" | ||||
|          transform="matrix(2,0,0,2,193.25,-374.967)" | ||||
|          id="g4030-1-8"> | ||||
|         <path | ||||
|            style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" | ||||
|            d="m -72.5,173.5 -14,14 14,14" | ||||
|            id="path3165-7-3" | ||||
|            sodipodi:nodetypes="ccc" | ||||
|            inkscape:connector-curvature="0" /> | ||||
|       </g> | ||||
|       <path | ||||
|          transform="matrix(3.34328,0,0,3.34328,185.28,-623.176)" | ||||
|          d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z" | ||||
|          sodipodi:ry="2.09375" | ||||
|          sodipodi:rx="2.09375" | ||||
|          sodipodi:cy="186.40625" | ||||
|          sodipodi:cx="-38.59375" | ||||
|          id="path4050-2-7-9-4" | ||||
|          style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible" | ||||
|          sodipodi:type="arc" /> | ||||
|       <path | ||||
|          transform="matrix(3.34328,0,0,3.34328,207.28,-623.176)" | ||||
|          d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z" | ||||
|          sodipodi:ry="2.09375" | ||||
|          sodipodi:rx="2.09375" | ||||
|          sodipodi:cy="186.40625" | ||||
|          sodipodi:cx="-38.59375" | ||||
|          id="path4050-2-7-9-4-8" | ||||
|          style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible" | ||||
|          sodipodi:type="arc" /> | ||||
|       <path | ||||
|          transform="matrix(2.86565,0,0,2.86565,166.846,-534.143)" | ||||
|          d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z" | ||||
|          sodipodi:ry="2.09375" | ||||
|          sodipodi:rx="2.09375" | ||||
|          sodipodi:cy="186.40625" | ||||
|          sodipodi:cx="-38.59375" | ||||
|          id="path4050-2-7-9-4-0" | ||||
|          style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" | ||||
|          sodipodi:type="arc" /> | ||||
|       <path | ||||
|          transform="matrix(2.86565,0,0,2.86565,188.846,-534.143)" | ||||
|          d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z" | ||||
|          sodipodi:ry="2.09375" | ||||
|          sodipodi:rx="2.09375" | ||||
|          sodipodi:cy="186.40625" | ||||
|          sodipodi:cx="-38.59375" | ||||
|          id="path4050-2-7-9-4-0-9" | ||||
|          style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" | ||||
|          sodipodi:type="arc" /> | ||||
|       <path | ||||
|          transform="matrix(2,0,0,2,-586,-765.967)" | ||||
|          sodipodi:nodetypes="ccccscccsc" | ||||
|          id="path3165-7-3-1" | ||||
|          d="m 317.06251,365.96875 c -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 l -16.125,16.125 16.125,16.125 c 1.11265,1.11265 3.13735,1.11265 4.25,0 1.11265,-1.11264 1.11265,-3.13735 0,-4.25 l -11.875,-11.875 11.875,-11.875 c 0.86584,-0.83655 1.1475,-2.22114 0.6773,-3.32947 -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 z" | ||||
|          style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans" | ||||
|          inkscape:connector-curvature="0" /> | ||||
|       <path | ||||
|          transform="matrix(2,0,0,2,-586,-765.967)" | ||||
|          sodipodi:nodetypes="ccccccc" | ||||
|          id="path3165-7-3-1-9" | ||||
|          d="m 320.08435,397.03059 c 0.007,-0.79449 -0.27079,-1.59203 -0.83434,-2.15559 L 307.37501,383 m 12.5523,-15.20447 c -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 L 298.87501,383" | ||||
|          style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans" | ||||
|          inkscape:connector-curvature="0" /> | ||||
|     </g> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 16 KiB | 
| @@ -29,6 +29,8 @@ | ||||
|   <chapter> | ||||
|     <title>Search</title> | ||||
|     <xi:include href="xml/shell-app-system.xml"/> | ||||
|     <xi:include href="xml/shell-contact-system.xml"/> | ||||
|     <xi:include href="xml/shell-doc-system.xml"/> | ||||
|   </chapter> | ||||
|   <chapter> | ||||
|     <title>Tray Icons</title> | ||||
| @@ -40,6 +42,7 @@ | ||||
|   <chapter> | ||||
|     <title>Recorder</title> | ||||
|     <xi:include href="xml/shell-recorder.xml"/> | ||||
|     <xi:include href="xml/shell-recorder-src.xml"/> | ||||
|   </chapter> | ||||
|   <chapter> | ||||
|     <title>Integration helpers and utilities</title> | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| NULL = | ||||
|  | ||||
| EXTRA_DIST = misc/config.js.in | ||||
| CLEANFILES = misc/config.js | ||||
| @@ -18,10 +17,11 @@ jsdir = $(pkgdatadir)/js | ||||
|  | ||||
| nobase_dist_js_DATA = 	\ | ||||
| 	gdm/batch.js		\ | ||||
| 	gdm/consoleKit.js	\ | ||||
| 	gdm/fingerprint.js	\ | ||||
| 	gdm/loginDialog.js	\ | ||||
| 	gdm/powerMenu.js	\ | ||||
| 	gdm/realmd.js		\ | ||||
| 	gdm/systemd.js	 	\ | ||||
| 	gdm/util.js		\ | ||||
| 	extensionPrefs/main.js	\ | ||||
| 	misc/config.js		\ | ||||
| @@ -30,7 +30,6 @@ nobase_dist_js_DATA = 	\ | ||||
| 	misc/gnomeSession.js	\ | ||||
| 	misc/history.js		\ | ||||
| 	misc/jsParse.js		\ | ||||
| 	misc/loginManager.js	\ | ||||
| 	misc/modemManager.js	\ | ||||
| 	misc/params.js		\ | ||||
| 	misc/util.js		\ | ||||
| @@ -38,6 +37,8 @@ nobase_dist_js_DATA = 	\ | ||||
| 	ui/altTab.js		\ | ||||
| 	ui/appDisplay.js	\ | ||||
| 	ui/appFavorites.js	\ | ||||
| 	ui/automountManager.js  \ | ||||
| 	ui/autorunManager.js    \ | ||||
| 	ui/boxpointer.js	\ | ||||
| 	ui/calendar.js		\ | ||||
| 	ui/checkBox.js		\ | ||||
| @@ -46,14 +47,14 @@ nobase_dist_js_DATA = 	\ | ||||
| 	ui/dateMenu.js		\ | ||||
| 	ui/dnd.js		\ | ||||
| 	ui/endSessionDialog.js	\ | ||||
| 	ui/environment.js	\ | ||||
| 	ui/extensionSystem.js	\ | ||||
| 	ui/extensionDownloader.js \ | ||||
| 	ui/environment.js	\ | ||||
| 	ui/flashspot.js		\ | ||||
| 	ui/ibusCandidatePopup.js\ | ||||
| 	ui/grabHelper.js	\ | ||||
| 	ui/iconGrid.js		\ | ||||
| 	ui/keyboard.js		\ | ||||
| 	ui/keyringPrompt.js	\ | ||||
| 	ui/layout.js		\ | ||||
| 	ui/lightbox.js		\ | ||||
| 	ui/lookingGlass.js	\ | ||||
| @@ -62,6 +63,7 @@ nobase_dist_js_DATA = 	\ | ||||
| 	ui/main.js		\ | ||||
| 	ui/messageTray.js	\ | ||||
| 	ui/modalDialog.js	\ | ||||
| 	ui/networkAgent.js	\ | ||||
| 	ui/sessionMode.js	\ | ||||
| 	ui/shellEntry.js	\ | ||||
| 	ui/shellMountOperation.js \ | ||||
| @@ -69,7 +71,8 @@ nobase_dist_js_DATA = 	\ | ||||
| 	ui/overview.js		\ | ||||
| 	ui/panel.js		\ | ||||
| 	ui/panelMenu.js		\ | ||||
| 	ui/pointerWatcher.js    \ | ||||
| 	ui/placeDisplay.js	\ | ||||
| 	ui/polkitAuthenticationAgent.js \ | ||||
| 	ui/popupMenu.js		\ | ||||
| 	ui/remoteSearch.js	\ | ||||
| 	ui/runDialog.js		\ | ||||
| @@ -80,11 +83,11 @@ nobase_dist_js_DATA = 	\ | ||||
| 	ui/shellDBus.js		\ | ||||
| 	ui/status/accessibility.js	\ | ||||
| 	ui/status/keyboard.js	\ | ||||
| 	ui/status/lockScreenMenu.js	\ | ||||
| 	ui/status/network.js	\ | ||||
| 	ui/status/power.js	\ | ||||
| 	ui/status/volume.js	\ | ||||
| 	ui/status/bluetooth.js	\ | ||||
| 	ui/telepathyClient.js	\ | ||||
| 	ui/tweener.js		\ | ||||
| 	ui/unlockDialog.js	\ | ||||
| 	ui/userMenu.js		\ | ||||
| @@ -96,14 +99,4 @@ nobase_dist_js_DATA = 	\ | ||||
| 	ui/workspaceThumbnail.js	\ | ||||
| 	ui/workspacesView.js	\ | ||||
| 	ui/workspaceSwitcherPopup.js    \ | ||||
| 	ui/xdndHandler.js	\ | ||||
| 	ui/components/__init__.js		\ | ||||
| 	ui/components/autorunManager.js		\ | ||||
| 	ui/components/automountManager.js	\ | ||||
| 	ui/components/mediaKeysManager.js	\ | ||||
| 	ui/components/networkAgent.js		\ | ||||
| 	ui/components/polkitAgent.js		\ | ||||
| 	ui/components/recorder.js		\ | ||||
| 	ui/components/telepathyClient.js	\ | ||||
| 	ui/components/keyring.js		\ | ||||
| 	$(NULL) | ||||
| 	ui/xdndHandler.js | ||||
|   | ||||
| @@ -174,7 +174,7 @@ const Application = new Lang.Class({ | ||||
|         let renderer = new Gtk.CellRendererText(); | ||||
|         this._extensionSelector.pack_start(renderer, true); | ||||
|         this._extensionSelector.add_attribute(renderer, 'text', 1); | ||||
|         this._extensionSelector.set_cell_data_func(renderer, Lang.bind(this, this._setExtensionInsensitive)); | ||||
|         this._extensionSelector.set_cell_data_func(renderer, Lang.bind(this, this._setExtensionInsensitive), null); | ||||
|         this._extensionSelector.connect('changed', Lang.bind(this, this._extensionSelected)); | ||||
|  | ||||
|         toolitem = new Gtk.ToolItem({ child: this._extensionSelector }); | ||||
|   | ||||
							
								
								
									
										22
									
								
								js/gdm/consoleKit.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								js/gdm/consoleKit.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Gio = imports.gi.Gio; | ||||
|  | ||||
| const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'> | ||||
| <method name='CanRestart'> | ||||
|     <arg type='b' direction='out'/> | ||||
| </method> | ||||
| <method name='CanStop'> | ||||
|     <arg type='b' direction='out'/> | ||||
| </method> | ||||
| <method name='Restart' /> | ||||
| <method name='Stop' /> | ||||
| </interface>; | ||||
|  | ||||
| const ConsoleKitProxy = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface); | ||||
|  | ||||
| function ConsoleKitManager() { | ||||
|     return new ConsoleKitProxy(Gio.DBus.system, | ||||
|                                'org.freedesktop.ConsoleKit', | ||||
|                                '/org/freedesktop/ConsoleKit/Manager'); | ||||
| }; | ||||
| @@ -19,7 +19,8 @@ function FprintManager() { | ||||
|                                    g_interface_info: FprintManagerInfo, | ||||
|                                    g_name: 'net.reactivated.Fprint', | ||||
|                                    g_object_path: '/net/reactivated/Fprint/Manager', | ||||
|                                    g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) }); | ||||
|                                    g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START | | ||||
|                                              Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) }); | ||||
|  | ||||
|     self.init(null); | ||||
|     return self; | ||||
|   | ||||
| @@ -25,7 +25,6 @@ const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Lang = imports.lang; | ||||
| const Pango = imports.gi.Pango; | ||||
| const Signals = imports.signals; | ||||
| @@ -39,14 +38,12 @@ const GdmUtil = imports.gdm.util; | ||||
| const Lightbox = imports.ui.lightbox; | ||||
| const Main = imports.ui.main; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
| const PanelMenu = imports.ui.panelMenu; | ||||
| const Tweener = imports.ui.tweener; | ||||
| const UserMenu = imports.ui.userMenu; | ||||
|  | ||||
| const _RESIZE_ANIMATION_TIME = 0.25; | ||||
| const _SCROLL_ANIMATION_TIME = 0.5; | ||||
| const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0; | ||||
| const _LOGO_ICON_HEIGHT = 16; | ||||
| const _LOGO_ICON_NAME_SIZE = 48; | ||||
|  | ||||
| let _loginDialog = null; | ||||
|  | ||||
| @@ -83,36 +80,6 @@ function _smoothlyResizeActor(actor, width, height) { | ||||
|     return hold; | ||||
| } | ||||
|  | ||||
| const LogoMenuButton = new Lang.Class({ | ||||
|     Name: 'LogoMenuButton', | ||||
|     Extends: PanelMenu.Button, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent(0.0, null, true); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA }); | ||||
|         this._settings.connect('changed::' + GdmUtil.LOGO_KEY, | ||||
|                                Lang.bind(this, this._updateLogo)); | ||||
|  | ||||
|         this._iconBin = new St.Bin(); | ||||
|         this.actor.add_actor(this._iconBin); | ||||
|  | ||||
|         this._updateLogo(); | ||||
|     }, | ||||
|  | ||||
|     _updateLogo: function() { | ||||
|         let path = this._settings.get_string(GdmUtil.LOGO_KEY); | ||||
|         let icon = null; | ||||
|  | ||||
|         if (path) { | ||||
|             let file = Gio.file_new_for_path(path); | ||||
|             let cache = St.TextureCache.get_default(); | ||||
|             icon = cache.load_uri_async(file.get_uri(), -1, _LOGO_ICON_HEIGHT); | ||||
|         } | ||||
|         this._iconBin.set_child(icon); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const UserListItem = new Lang.Class({ | ||||
|     Name: 'UserListItem', | ||||
|  | ||||
| @@ -129,15 +96,14 @@ const UserListItem = new Lang.Class({ | ||||
|                                      x_align: St.Align.START, | ||||
|                                      x_fill: true }); | ||||
|  | ||||
|         this._userAvatar = new UserMenu.UserAvatarWidget(this.user, | ||||
|                                                          { styleClass: 'login-dialog-user-list-item-icon' }); | ||||
|         layout.add(this._userAvatar.actor); | ||||
|         this._iconBin = new St.Bin(); | ||||
|         layout.add(this._iconBin); | ||||
|         let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box', | ||||
|                                             vertical:    true }); | ||||
|         layout.add(textLayout, { expand: true }); | ||||
|  | ||||
|         this._nameLabel = new St.Label({ style_class: 'login-dialog-user-list-item-name' }); | ||||
|         this.actor.label_actor = this._nameLabel; | ||||
|         this._nameLabel = new St.Label({ text:        this.user.get_real_name(), | ||||
|                                          style_class: 'login-dialog-user-list-item-name' }); | ||||
|         textLayout.add(this._nameLabel, | ||||
|                        { y_fill: false, | ||||
|                          y_align: St.Align.MIDDLE, | ||||
| @@ -151,16 +117,63 @@ const UserListItem = new Lang.Class({ | ||||
|                          y_fill: false, | ||||
|                          y_align: St.Align.END }); | ||||
|  | ||||
|         this._updateIcon(); | ||||
|         this._updateLoggedIn(); | ||||
|  | ||||
|         this.actor.connect('clicked', Lang.bind(this, this._onClicked)); | ||||
|         this._onUserChanged(); | ||||
|     }, | ||||
|  | ||||
|     _onUserChanged: function() { | ||||
|         this._nameLabel.set_text(this.user.get_real_name()); | ||||
|         this._userAvatar.update(); | ||||
|         this._updateIcon(); | ||||
|         this._updateLoggedIn(); | ||||
|     }, | ||||
|  | ||||
|     _setIconFromFile: function(iconFile, styleClass) { | ||||
|         if (styleClass) | ||||
|             this._iconBin.set_style_class_name(styleClass); | ||||
|         this._iconBin.set_style(null); | ||||
|  | ||||
|         this._iconBin.child = null; | ||||
|         if (iconFile) { | ||||
|             this._iconBin.show(); | ||||
|             // We use background-image instead of, say, St.TextureCache | ||||
|             // so the theme writers can add a rounded frame around the image | ||||
|             // and so theme writers can pick the icon size. | ||||
|             this._iconBin.set_style('background-image: url("' + iconFile + '");' + | ||||
|                                     'background-size: contain;'); | ||||
|         } else { | ||||
|             this._iconBin.hide(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _setIconFromName: function(iconName, styleClass) { | ||||
|         if (styleClass) | ||||
|             this._iconBin.set_style_class_name(styleClass); | ||||
|         this._iconBin.set_style(null); | ||||
|  | ||||
|         if (iconName != null) { | ||||
|             let icon = new St.Icon(); | ||||
|             icon.set_icon_name(iconName) | ||||
|  | ||||
|             this._iconBin.child = icon; | ||||
|             this._iconBin.show(); | ||||
|         } else { | ||||
|             this._iconBin.child = null; | ||||
|             this._iconBin.hide(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _updateIcon: function() { | ||||
|         let iconFileName = this.user.get_icon_file(); | ||||
|         let gicon = null; | ||||
|  | ||||
|         if (GLib.file_test(iconFileName, GLib.FileTest.EXISTS)) | ||||
|             this._setIconFromFile(iconFileName, 'login-dialog-user-list-item-icon'); | ||||
|         else | ||||
|             this._setIconFromName('avatar-default', 'login-dialog-user-list-item-icon'); | ||||
|     }, | ||||
|  | ||||
|     syncStyleClasses: function() { | ||||
|         this._updateLoggedIn(); | ||||
|  | ||||
| @@ -181,6 +194,14 @@ const UserListItem = new Lang.Class({ | ||||
|         this.emit('activate'); | ||||
|     }, | ||||
|  | ||||
|     fadeOutName: function() { | ||||
|         return GdmUtil.fadeOutActor(this._nameLabel); | ||||
|     }, | ||||
|  | ||||
|     fadeInName: function() { | ||||
|         return GdmUtil.fadeInActor(this._nameLabel); | ||||
|     }, | ||||
|  | ||||
|     showTimedLoginIndicator: function(time) { | ||||
|         let hold = new Batch.Hold(); | ||||
|  | ||||
| @@ -231,18 +252,16 @@ const UserList = new Lang.Class({ | ||||
|         if (global.stage.get_key_focus() != this.actor) | ||||
|             return; | ||||
|  | ||||
|         let focusSet = this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); | ||||
|         if (!focusSet) { | ||||
|             Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { | ||||
|                 this._moveFocusToItems(); | ||||
|                 return false; | ||||
|             })); | ||||
|         } | ||||
|         this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); | ||||
|     }, | ||||
|  | ||||
|     _showItem: function(item) { | ||||
|         let tasks = [function() { | ||||
|                          return GdmUtil.fadeInActor(item.actor); | ||||
|                      }, | ||||
|  | ||||
|                      function() { | ||||
|                          return item.fadeInName(); | ||||
|                      }]; | ||||
|  | ||||
|         let batch = new Batch.ConsecutiveBatch(this, tasks); | ||||
| @@ -306,16 +325,13 @@ const UserList = new Lang.Class({ | ||||
|                 }); | ||||
|         } | ||||
|  | ||||
|         this._box.remove_style_pseudo_class('expanded'); | ||||
|         let batch = new Batch.ConsecutiveBatch(this, | ||||
|                                                [function() { | ||||
|                                                     return GdmUtil.fadeOutActor(this.actor.vscroll); | ||||
|                                                 }, | ||||
|  | ||||
|                                                 new Batch.ConcurrentBatch(this, tasks), | ||||
|  | ||||
|                                                 function() { | ||||
|                                                     this._box.remove_style_pseudo_class('expanded'); | ||||
|                                                 } | ||||
|                                                 new Batch.ConcurrentBatch(this, tasks) | ||||
|                                                ]); | ||||
|  | ||||
|         return batch.run(); | ||||
| @@ -365,6 +381,7 @@ const UserList = new Lang.Class({ | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         this._box.add_style_pseudo_class('expanded'); | ||||
|         let batch = new Batch.ConsecutiveBatch(this, | ||||
|                                                [function() { | ||||
|                                                     this.takeOverWhitespace(); | ||||
| @@ -375,10 +392,6 @@ const UserList = new Lang.Class({ | ||||
|                                                     return _smoothlyResizeActor(this._box, -1, fullHeight); | ||||
|                                                 }, | ||||
|  | ||||
|                                                 function() { | ||||
|                                                     this._box.add_style_pseudo_class('expanded'); | ||||
|                                                 }, | ||||
|  | ||||
|                                                 new Batch.ConcurrentBatch(this, tasks), | ||||
|  | ||||
|                                                 function() { | ||||
| @@ -504,7 +517,6 @@ const SessionListItem = new Lang.Class({ | ||||
|  | ||||
|         let label = new St.Label({ style_class: 'login-dialog-session-list-item-label', | ||||
|                                    text: name }); | ||||
|         this.actor.label_actor = label; | ||||
|  | ||||
|         this._box.add_actor(label); | ||||
|     }, | ||||
| @@ -659,8 +671,8 @@ const LoginDialog = new Lang.Class({ | ||||
|     _init: function(parentActor) { | ||||
|         this.parent({ shellReactive: true, | ||||
|                       styleClass: 'login-dialog', | ||||
|                       parentActor: parentActor, | ||||
|                       shouldFadeIn: false }); | ||||
|                       parentActor: parentActor | ||||
|                     }); | ||||
|         this.connect('destroy', | ||||
|                      Lang.bind(this, this._onDestroy)); | ||||
|         this.connect('opened', | ||||
| @@ -669,34 +681,36 @@ const LoginDialog = new Lang.Class({ | ||||
|         this._userManager = AccountsService.UserManager.get_default() | ||||
|         this._greeterClient = new Gdm.Client(); | ||||
|  | ||||
|         if (GLib.getenv('GDM_GREETER_TEST') != '1') { | ||||
|             this._greeter = this._greeterClient.get_greeter_sync(null); | ||||
|         this._greeter = this._greeterClient.get_greeter_sync(null); | ||||
|  | ||||
|             this._greeter.connect('default-session-name-changed', | ||||
|                                   Lang.bind(this, this._onDefaultSessionChanged)); | ||||
|         this._greeter.connect('default-session-name-changed', | ||||
|                               Lang.bind(this, this._onDefaultSessionChanged)); | ||||
|  | ||||
|             this._greeter.connect('session-opened', | ||||
|                                   Lang.bind(this, this._onSessionOpened)); | ||||
|             this._greeter.connect('timed-login-requested', | ||||
|                                   Lang.bind(this, this._onTimedLoginRequested)); | ||||
|         } | ||||
|         this._greeter.connect('session-opened', | ||||
|                               Lang.bind(this, this._onSessionOpened)); | ||||
|         this._greeter.connect('timed-login-requested', | ||||
|                               Lang.bind(this, this._onTimedLoginRequested)); | ||||
|  | ||||
|         this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient); | ||||
|         this._userVerifier.connect('ask-question', Lang.bind(this, this._askQuestion)); | ||||
|         this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage)); | ||||
|         this._userVerifier.connect('reset', Lang.bind(this, this._reset)); | ||||
|         this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint)); | ||||
|         this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint)); | ||||
|         this._verifyingUser = false; | ||||
|         this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed)); | ||||
|         this._userVerifier.connect('reset', Lang.bind(this, this._onReset)); | ||||
|  | ||||
|         this._userVerifier.connect('show-fingerprint-prompt', Lang.bind(this, this._showFingerprintPrompt)); | ||||
|         this._userVerifier.connect('hide-fingerprint-prompt', Lang.bind(this, this._hideFingerprintPrompt)); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA }); | ||||
|  | ||||
|         this._settings.connect('changed::' + GdmUtil.LOGO_KEY, | ||||
|                                Lang.bind(this, this._updateLogo)); | ||||
|         this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY, | ||||
|                                Lang.bind(this, this._updateBanner)); | ||||
|         this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_TEXT_KEY, | ||||
|                                Lang.bind(this, this._updateBanner)); | ||||
|         this._settings.connect('changed::' + GdmUtil.DISABLE_USER_LIST_KEY, | ||||
|                                Lang.bind(this, this._updateDisableUserList)); | ||||
|  | ||||
|         this._logoBox = new St.Bin({ style_class: 'login-dialog-logo-box' }); | ||||
|         this.contentLayout.add(this._logoBox); | ||||
|         this._updateLogo(); | ||||
|  | ||||
|         this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner', | ||||
|                                            text: '' }); | ||||
| @@ -704,30 +718,37 @@ const LoginDialog = new Lang.Class({ | ||||
|         this._updateBanner(); | ||||
|  | ||||
|         this._titleLabel = new St.Label({ style_class: 'login-dialog-title', | ||||
|                                           text: C_("title", "Sign In"), | ||||
|                                           visible: false }); | ||||
|                                           text: C_("title", "Sign In") }); | ||||
|  | ||||
|         this.contentLayout.add(this._titleLabel, | ||||
|                               { y_fill: false, | ||||
|                                 y_align: St.Align.START }); | ||||
|  | ||||
|         this._userList = new UserList(); | ||||
|         this.contentLayout.add(this._userList.actor, | ||||
|         let mainContentBox = new St.BoxLayout({ vertical: false }); | ||||
|         this.contentLayout.add(mainContentBox, | ||||
|                                { expand: true, | ||||
|                                  x_fill: true, | ||||
|                                  y_fill: true }); | ||||
|                                  y_fill: false }); | ||||
|  | ||||
|         this._userList = new UserList(); | ||||
|         mainContentBox.add(this._userList.actor, | ||||
|                            { expand: true, | ||||
|                              x_fill: true, | ||||
|                              y_fill: true }); | ||||
|  | ||||
|         this.setInitialKeyFocus(this._userList.actor); | ||||
|  | ||||
|         this._promptBox = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout', | ||||
|                                              vertical: true }); | ||||
|         this.contentLayout.add(this._promptBox, | ||||
|                                { expand: true, | ||||
|                                  x_fill: true, | ||||
|                                  y_fill: true, | ||||
|                                  x_align: St.Align.START }); | ||||
|         mainContentBox.add(this._promptBox, | ||||
|                            { expand: true, | ||||
|                              x_fill: true, | ||||
|                              y_fill: true, | ||||
|                              x_align: St.Align.START }); | ||||
|         this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' }); | ||||
|  | ||||
|         this._mainContentBox = mainContentBox; | ||||
|  | ||||
|         this._promptBox.add(this._promptLabel, | ||||
|                             { expand: true, | ||||
|                               x_fill: true, | ||||
| @@ -735,19 +756,17 @@ const LoginDialog = new Lang.Class({ | ||||
|                               x_align: St.Align.START }); | ||||
|         this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry', | ||||
|                                            can_focus: true }); | ||||
|         this._promptEntryTextChangedId = 0; | ||||
|         this._promptBox.add(this._promptEntry, | ||||
|                             { expand: true, | ||||
|                               x_fill: true, | ||||
|                               y_fill: false, | ||||
|                               x_align: St.Align.START }); | ||||
|  | ||||
|         this._promptMessage = new St.Label({ visible: false }); | ||||
|         this._promptBox.add(this._promptMessage, { x_fill: true }); | ||||
|  | ||||
|         this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint-message' }); | ||||
|         this._promptLoginHint.hide(); | ||||
|         this._promptBox.add(this._promptLoginHint); | ||||
|         // Translators: this message is shown below the password entry field | ||||
|         // to indicate the user can swipe their finger instead | ||||
|         this._promptFingerprintMessage = new St.Label({ text: _("(or swipe finger)"), | ||||
|                                                         style_class: 'login-dialog-prompt-fingerprint-message' }); | ||||
|         this._promptFingerprintMessage.hide(); | ||||
|         this._promptBox.add(this._promptFingerprintMessage); | ||||
|  | ||||
|         this._sessionList = new SessionList(); | ||||
|         this._sessionList.connect('session-activated', | ||||
| @@ -774,7 +793,7 @@ const LoginDialog = new Lang.Class({ | ||||
|                                                 x_align: St.Align.START, | ||||
|                                                 x_fill: true }); | ||||
|  | ||||
|         this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAndLogIn)); | ||||
|         this._notListedButton.connect('clicked', Lang.bind(this, this._onNotListedClicked)); | ||||
|  | ||||
|         this.contentLayout.add(this._notListedButton, | ||||
|                                { expand: false, | ||||
| @@ -800,19 +819,18 @@ const LoginDialog = new Lang.Class({ | ||||
|  | ||||
|    }, | ||||
|  | ||||
|     _updateDisableUserList: function() { | ||||
|         let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY); | ||||
|     _updateLogo: function() { | ||||
|         this._logoBox.child = null; | ||||
|         let path = this._settings.get_string(GdmUtil.LOGO_KEY); | ||||
|  | ||||
|         // If this is the first time around, set initial focus | ||||
|         if (this._disableUserList == undefined && disableUserList) | ||||
|             this.setInitialKeyFocus(this._promptEntry); | ||||
|         if (path) { | ||||
|             let file = Gio.file_new_for_path(path); | ||||
|             let uri = file.get_uri(); | ||||
|  | ||||
|         if (disableUserList != this._disableUserList) { | ||||
|             this._disableUserList = disableUserList; | ||||
|  | ||||
|             if (!this._verifyingUser) | ||||
|                 this._reset(); | ||||
|             let textureCache = St.TextureCache.get_default(); | ||||
|             this._logoBox.child = textureCache.load_uri_async(uri, -1, _LOGO_ICON_NAME_SIZE); | ||||
|         } | ||||
|  | ||||
|     }, | ||||
|  | ||||
|     _updateBanner: function() { | ||||
| @@ -827,39 +845,42 @@ const LoginDialog = new Lang.Class({ | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _reset: function() { | ||||
|         this._promptMessage.hide(); | ||||
|         this._user = null; | ||||
|         this._verifyingUser = false; | ||||
|     _onReset: function(client, serviceName) { | ||||
|         let tasks = [this._hidePrompt, | ||||
|  | ||||
|         if (this._disableUserList) | ||||
|             this._hideUserListAndLogIn(); | ||||
|         else | ||||
|             this._showUserList(); | ||||
|                      new Batch.ConcurrentBatch(this, [this._fadeInTitleLabel, | ||||
|                                                       this._fadeInNotListedButton, | ||||
|                                                       this._fadeInLogo]), | ||||
|  | ||||
|                      function() { | ||||
|                          this._sessionList.close(); | ||||
|                          this._promptFingerprintMessage.hide(); | ||||
|                          this._userList.actor.show(); | ||||
|                          this._userList.actor.opacity = 255; | ||||
|                          return this._userList.showItems(); | ||||
|                      }, | ||||
|  | ||||
|                      function() { | ||||
|                          this._userList.actor.reactive = true; | ||||
|                          this._userList.actor.grab_key_focus(); | ||||
|                      }]; | ||||
|  | ||||
|         this._user = null; | ||||
|  | ||||
|         let batch = new Batch.ConsecutiveBatch(this, tasks); | ||||
|         batch.run(); | ||||
|     }, | ||||
|  | ||||
|     _onDefaultSessionChanged: function(client, sessionId) { | ||||
|         this._sessionList.setActiveSession(sessionId); | ||||
|     }, | ||||
|  | ||||
|     _showMessage: function(userVerifier, message, styleClass) { | ||||
|         if (message) { | ||||
|             this._promptMessage.text = message; | ||||
|             this._promptMessage.styleClass = styleClass; | ||||
|             GdmUtil.fadeInActor(this._promptMessage); | ||||
|         } else { | ||||
|             GdmUtil.fadeOutActor(this._promptMessage); | ||||
|         } | ||||
|     _showFingerprintPrompt: function() { | ||||
|         GdmUtil.fadeInActor(this._promptFingerprintMessage); | ||||
|     }, | ||||
|  | ||||
|     _showLoginHint: function(verifier, message) { | ||||
|         this._promptLoginHint.set_text(message) | ||||
|         GdmUtil.fadeInActor(this._promptLoginHint); | ||||
|     }, | ||||
|  | ||||
|     _hideLoginHint: function() { | ||||
|         GdmUtil.fadeOutActor(this._promptLoginHint); | ||||
|         this._promptLoginHint.set_text(''); | ||||
|     _hideFingerprintPrompt: function() { | ||||
|         GdmUtil.fadeOutActor(this._promptFingerprintMessage); | ||||
|     }, | ||||
|  | ||||
|     cancel: function() { | ||||
| @@ -878,8 +899,8 @@ const LoginDialog = new Lang.Class({ | ||||
|                      function() { | ||||
|                          // Show it with 0 opacity so we preallocate space for it | ||||
|                          // in the event we need to fade in the message | ||||
|                          this._promptLoginHint.opacity = 0; | ||||
|                          this._promptLoginHint.show(); | ||||
|                          this._promptFingerprintMessage.opacity = 0; | ||||
|                          this._promptFingerprintMessage.show(); | ||||
|                      }, | ||||
|  | ||||
|                      function() { | ||||
| @@ -890,9 +911,6 @@ const LoginDialog = new Lang.Class({ | ||||
|                          if (this._user && this._user.is_logged_in()) | ||||
|                              return null; | ||||
|  | ||||
|                          if (!this._verifyingUser) | ||||
|                              return null; | ||||
|  | ||||
|                          return GdmUtil.fadeInActor(this._sessionList.actor); | ||||
|                      }, | ||||
|  | ||||
| @@ -905,21 +923,26 @@ const LoginDialog = new Lang.Class({ | ||||
|         return batch.run(); | ||||
|     }, | ||||
|  | ||||
|     _showPrompt: function(forSecret) { | ||||
|     _showPrompt: function() { | ||||
|         let hold = new Batch.Hold(); | ||||
|  | ||||
|         let cancelButtonInfo = { action: Lang.bind(this, this.cancel), | ||||
|                                  label: _("Cancel"), | ||||
|                                  key: Clutter.Escape }; | ||||
|         let okButtonInfo = { action: Lang.bind(this, function() { | ||||
|                                          hold.release(); | ||||
|                                      }), | ||||
|                              label: forSecret ? C_("button", "Sign In") : _("Next"), | ||||
|                              default: true }; | ||||
|         let buttons = []; | ||||
|         if (!this._disableUserList || this._verifyingUser) | ||||
|             buttons.push(cancelButtonInfo); | ||||
|         buttons.push(okButtonInfo); | ||||
|         let buttons = [{ action: Lang.bind(this, this.cancel), | ||||
|                          label: _("Cancel"), | ||||
|                          key: Clutter.Escape }, | ||||
|                        { action: Lang.bind(this, function() { | ||||
|                                      hold.release(); | ||||
|                                  }), | ||||
|                          label: C_("button", "Sign In"), | ||||
|                          default: true }]; | ||||
|  | ||||
|         this._promptEntryActivateCallbackId = this._promptEntry.clutter_text.connect('activate', | ||||
|                                                                                      Lang.bind(this, function() { | ||||
|                                                                                          hold.release(); | ||||
|                                                                                      })); | ||||
|         hold.connect('release', Lang.bind(this, function() { | ||||
|                          this._promptEntry.clutter_text.disconnect(this._promptEntryActivateCallbackId); | ||||
|                          this._promptEntryActivateCallbackId = null; | ||||
|                      })); | ||||
|  | ||||
|         let tasks = [function() { | ||||
|                          return this._fadeInPrompt(); | ||||
| @@ -927,16 +950,6 @@ const LoginDialog = new Lang.Class({ | ||||
|  | ||||
|                      function() { | ||||
|                          this.setButtons(buttons); | ||||
|  | ||||
|                          let updateOkButtonEnabled = Lang.bind(this, function() { | ||||
|                              let sensitive = this._promptEntry.text.length > 0; | ||||
|                              okButtonInfo.button.reactive = sensitive; | ||||
|                              okButtonInfo.button.can_focus = sensitive; | ||||
|                          }); | ||||
|  | ||||
|                          updateOkButtonEnabled(); | ||||
|  | ||||
|                          this._promptEntryTextChangedId = this._promptEntry.clutter_text.connect('text-changed', updateOkButtonEnabled); | ||||
|                      }, | ||||
|  | ||||
|                      hold]; | ||||
| @@ -947,20 +960,21 @@ const LoginDialog = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _hidePrompt: function() { | ||||
|         this.setButtons([]); | ||||
|  | ||||
|         if (this._promptEntryTextChangedId > 0) { | ||||
|             this._promptEntry.clutter_text.disconnect(this._promptEntryTextChangedId); | ||||
|             this._promptEntryTextChangedId = 0; | ||||
|         if (this._promptEntryActivateCallbackId) { | ||||
|             this._promptEntry.clutter_text.disconnect(this._promptEntryActivateCallbackId); | ||||
|             this._promptEntryActivateCallbackId = null; | ||||
|         } | ||||
|  | ||||
|         this.setButtons([]); | ||||
|  | ||||
|         let tasks = [function() { | ||||
|                          return GdmUtil.fadeOutActor(this._promptBox); | ||||
|                      }, | ||||
|  | ||||
|                      function() { | ||||
|                          this._promptLoginHint.hide(); | ||||
|                          this._promptFingerprintMessage.hide(); | ||||
|                          this._promptEntry.reactive = true; | ||||
|                          this._promptEntry.remove_style_pseudo_class('insensitive'); | ||||
|                          this._promptEntry.set_text(''); | ||||
|                      }]; | ||||
|  | ||||
| @@ -975,13 +989,12 @@ const LoginDialog = new Lang.Class({ | ||||
|         this._promptEntry.set_text(''); | ||||
|         this._promptEntry.clutter_text.set_password_char(passwordChar); | ||||
|  | ||||
|         let tasks = [function() { | ||||
|                          return this._showPrompt(!!passwordChar); | ||||
|                      }, | ||||
|         let tasks = [this._showPrompt, | ||||
|  | ||||
|                      function() { | ||||
|                          let _text = this._promptEntry.get_text(); | ||||
|                          this._promptEntry.reactive = false; | ||||
|                          this._promptEntry.add_style_pseudo_class('insensitive'); | ||||
|                          this._userVerifier.answerQuery(serviceName, _text); | ||||
|                      }]; | ||||
|  | ||||
| @@ -989,23 +1002,6 @@ const LoginDialog = new Lang.Class({ | ||||
|         return batch.run(); | ||||
|     }, | ||||
|  | ||||
|     _askForUsernameAndLogIn: function() { | ||||
|         this._promptLabel.set_text(_("Username: ")); | ||||
|         this._promptEntry.set_text(''); | ||||
|         this._promptEntry.clutter_text.set_password_char(''); | ||||
|  | ||||
|         let tasks = [this._showPrompt, | ||||
|  | ||||
|                      function() { | ||||
|                          let userName = this._promptEntry.get_text(); | ||||
|                          this._promptEntry.reactive = false; | ||||
|                          return this._beginVerificationForUser(userName); | ||||
|                      }]; | ||||
|  | ||||
|         let batch = new Batch.ConsecutiveBatch(this, tasks); | ||||
|         return batch.run(); | ||||
|     }, | ||||
|  | ||||
|     _onSessionOpened: function(client, serviceName) { | ||||
|         this._greeter.call_start_session_when_ready_sync(serviceName, true, null); | ||||
|     }, | ||||
| @@ -1134,7 +1130,11 @@ const LoginDialog = new Lang.Class({ | ||||
|                              })); | ||||
|     }, | ||||
|  | ||||
|     _hideUserListAndLogIn: function() { | ||||
|     _onVerificationFailed: function() { | ||||
|         this._userVerifier.cancel(); | ||||
|     }, | ||||
|  | ||||
|     _onNotListedClicked: function(user) { | ||||
|         let tasks = [function() { | ||||
|                          return this._userList.hideItems(); | ||||
|                      }, | ||||
| @@ -1148,37 +1148,26 @@ const LoginDialog = new Lang.Class({ | ||||
|                      }, | ||||
|  | ||||
|                      new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel, | ||||
|                                                       this._fadeOutNotListedButton]), | ||||
|                                                       this._fadeOutNotListedButton, | ||||
|                                                       this._fadeOutLogo]), | ||||
|  | ||||
|                      function() { | ||||
|                          return this._askForUsernameAndLogIn(); | ||||
|                          let hold = new Batch.Hold(); | ||||
|  | ||||
|                          this._userVerifier.begin(null, hold); | ||||
|                          return hold; | ||||
|                      }]; | ||||
|  | ||||
|         let batch = new Batch.ConsecutiveBatch(this, tasks); | ||||
|         batch.run(); | ||||
|     }, | ||||
|  | ||||
|     _showUserList: function() { | ||||
|         let tasks = [this._hidePrompt, | ||||
|     _fadeInLogo: function() { | ||||
|         return GdmUtil.fadeInActor(this._logoBox); | ||||
|     }, | ||||
|  | ||||
|                      new Batch.ConcurrentBatch(this, [this._fadeInTitleLabel, | ||||
|                                                       this._fadeInNotListedButton]), | ||||
|  | ||||
|                      function() { | ||||
|                          this._sessionList.close(); | ||||
|                          this._promptLoginHint.hide(); | ||||
|                          this._userList.actor.show(); | ||||
|                          this._userList.actor.opacity = 255; | ||||
|                          return this._userList.showItems(); | ||||
|                      }, | ||||
|  | ||||
|                      function() { | ||||
|                          this._userList.actor.reactive = true; | ||||
|                          this._userList.actor.grab_key_focus(); | ||||
|                      }]; | ||||
|  | ||||
|         let batch = new Batch.ConsecutiveBatch(this, tasks); | ||||
|         batch.run(); | ||||
|     _fadeOutLogo: function() { | ||||
|         return GdmUtil.fadeOutActor(this._logoBox); | ||||
|     }, | ||||
|  | ||||
|     _fadeInBanner: function() { | ||||
| @@ -1209,7 +1198,6 @@ const LoginDialog = new Lang.Class({ | ||||
|         let hold = new Batch.Hold(); | ||||
|  | ||||
|         this._userVerifier.begin(userName, hold); | ||||
|         this._verifyingUser = true; | ||||
|         return hold; | ||||
|     }, | ||||
|  | ||||
| @@ -1229,8 +1217,13 @@ const LoginDialog = new Lang.Class({ | ||||
|                          return this._userList.giveUpWhitespace(); | ||||
|                      }, | ||||
|  | ||||
|                      function() { | ||||
|                          return activatedItem.fadeOutName(); | ||||
|                      }, | ||||
|  | ||||
|                      new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel, | ||||
|                                                       this._fadeOutNotListedButton]), | ||||
|                                                       this._fadeOutNotListedButton, | ||||
|                                                       this._fadeOutLogo]), | ||||
|  | ||||
|                      function() { | ||||
|                          return this._userList.shrinkToNaturalHeight(); | ||||
| @@ -1262,8 +1255,6 @@ const LoginDialog = new Lang.Class({ | ||||
|             this._userList.addUser(users[i]); | ||||
|         } | ||||
|  | ||||
|         this._updateDisableUserList(); | ||||
|  | ||||
|         this._userManager.connect('user-added', | ||||
|                                   Lang.bind(this, function(userManager, user) { | ||||
|                                       this._userList.addUser(user); | ||||
| @@ -1285,7 +1276,7 @@ const LoginDialog = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onOpened: function() { | ||||
|         Main.ctrlAltTabManager.addGroup(this.dialogLayout, | ||||
|         Main.ctrlAltTabManager.addGroup(this._mainContentBox, | ||||
|                                         _("Login Window"), | ||||
|                                         'dialog-password', | ||||
|                                         { sortGroup: CtrlAltTab.SortGroup.MIDDLE }); | ||||
|   | ||||
| @@ -18,12 +18,12 @@ | ||||
|  * 02111-1307, USA. | ||||
|  */ | ||||
|  | ||||
| const Gio = imports.gi.Gio; | ||||
| const Lang = imports.lang; | ||||
| const UPowerGlib = imports.gi.UPowerGlib; | ||||
|  | ||||
| const LoginManager = imports.misc.loginManager; | ||||
| const ConsoleKit = imports.gdm.consoleKit; | ||||
| const Systemd = imports.gdm.systemd; | ||||
|  | ||||
| const GdmUtil = imports.gdm.util; | ||||
| const PanelMenu = imports.ui.panelMenu; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
|  | ||||
| @@ -32,17 +32,20 @@ const PowerMenuButton = new Lang.Class({ | ||||
|     Extends: PanelMenu.SystemStatusButton, | ||||
|  | ||||
|     _init: function() { | ||||
|         /* Translators: accessible name of the power menu in the login screen */ | ||||
|         this.parent('system-shutdown-symbolic', _("Power")); | ||||
|         this.parent('system-shutdown', null); | ||||
|         this._upClient = new UPowerGlib.Client(); | ||||
|  | ||||
|         this._loginManager = LoginManager.getLoginManager(); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA }); | ||||
|         this._settings.connect('changed::disable-restart-buttons', | ||||
|                                Lang.bind(this, this._updateVisibility)); | ||||
|         if (Systemd.haveSystemd()) | ||||
|             this._systemdLoginManager = new Systemd.SystemdLoginManager(); | ||||
|         else | ||||
|             this._consoleKitManager = new ConsoleKit.ConsoleKitManager(); | ||||
|  | ||||
|         this._createSubMenu(); | ||||
|  | ||||
|         this._upClient.connect('notify::can-suspend', | ||||
|                                Lang.bind(this, this._updateHaveSuspend)); | ||||
|         this._updateHaveSuspend(); | ||||
|  | ||||
|         // ConsoleKit doesn't send notifications when shutdown/reboot | ||||
|         // are disabled, so we update the menu item each time the menu opens | ||||
|         this.menu.connect('open-state-changed', Lang.bind(this, | ||||
| @@ -50,41 +53,75 @@ const PowerMenuButton = new Lang.Class({ | ||||
|                 if (open) { | ||||
|                     this._updateHaveShutdown(); | ||||
|                     this._updateHaveRestart(); | ||||
|                     this._updateHaveSuspend(); | ||||
|                 } | ||||
|             })); | ||||
|         this._updateHaveShutdown(); | ||||
|         this._updateHaveRestart(); | ||||
|         this._updateHaveSuspend(); | ||||
|     }, | ||||
|  | ||||
|     _updateVisibility: function() { | ||||
|         let shouldBeVisible = (this._haveSuspend || this._haveShutdown || this._haveRestart); | ||||
|         this.actor.visible = shouldBeVisible && !this._settings.get_boolean('disable-restart-buttons'); | ||||
|         this.actor.visible = shouldBeVisible; | ||||
|     }, | ||||
|  | ||||
|     _updateHaveShutdown: function() { | ||||
|         this._loginManager.canPowerOff(Lang.bind(this, function(result) { | ||||
|             this._haveShutdown = result; | ||||
|             this._powerOffItem.actor.visible = this._haveShutdown; | ||||
|             this._updateVisibility(); | ||||
|         })); | ||||
|  | ||||
|         if (Systemd.haveSystemd()) { | ||||
|             this._systemdLoginManager.CanPowerOffRemote(Lang.bind(this, | ||||
|                 function(result, error) { | ||||
|                     if (!error) | ||||
|                         this._haveShutdown = result[0] != 'no'; | ||||
|                     else | ||||
|                         this._haveShutdown = false; | ||||
|  | ||||
|                     this._powerOffItem.actor.visible = this._haveShutdown; | ||||
|                     this._updateVisibility(); | ||||
|                 })); | ||||
|         } else { | ||||
|             this._consoleKitManager.CanStopRemote(Lang.bind(this, | ||||
|                 function(result, error) { | ||||
|                     if (!error) | ||||
|                         this._haveShutdown = result[0]; | ||||
|                     else | ||||
|                         this._haveShutdown = false; | ||||
|  | ||||
|                     this._powerOffItem.actor.visible = this._haveShutdown; | ||||
|                     this._updateVisibility(); | ||||
|                 })); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _updateHaveRestart: function() { | ||||
|         this._loginManager.canReboot(Lang.bind(this, function(result) { | ||||
|             this._haveRestart = result; | ||||
|             this._restartItem.actor.visible = this._haveRestart; | ||||
|             this._updateVisibility(); | ||||
|         })); | ||||
|  | ||||
|         if (Systemd.haveSystemd()) { | ||||
|             this._systemdLoginManager.CanRebootRemote(Lang.bind(this, | ||||
|                 function(result, error) { | ||||
|                     if (!error) | ||||
|                         this._haveRestart = result[0] != 'no'; | ||||
|                     else | ||||
|                         this._haveRestart = false; | ||||
|  | ||||
|                     this._restartItem.actor.visible = this._haveRestart; | ||||
|                     this._updateVisibility(); | ||||
|                 })); | ||||
|         } else { | ||||
|             this._consoleKitManager.CanRestartRemote(Lang.bind(this, | ||||
|                 function(result, error) { | ||||
|                     if (!error) | ||||
|                         this._haveRestart = result[0]; | ||||
|                     else | ||||
|                         this._haveRestart = false; | ||||
|  | ||||
|                     this._restartItem.actor.visible = this._haveRestart; | ||||
|                     this._updateVisibility(); | ||||
|                 })); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _updateHaveSuspend: function() { | ||||
|         this._loginManager.canSuspend(Lang.bind(this, function(result) { | ||||
|             this._haveSuspend = result; | ||||
|             this._suspendItem.actor.visible = this._haveSuspend; | ||||
|             this._updateVisibility(); | ||||
|         })); | ||||
|         this._haveSuspend = this._upClient.get_can_suspend(); | ||||
|         this._suspendItem.actor.visible = this._haveSuspend; | ||||
|         this._updateVisibility(); | ||||
|     }, | ||||
|  | ||||
|     _createSubMenu: function() { | ||||
| @@ -107,23 +144,27 @@ const PowerMenuButton = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onActivateSuspend: function() { | ||||
|         if (!this._haveSuspend) | ||||
|             return; | ||||
|  | ||||
|         this._loginManager.suspend(); | ||||
|         if (this._haveSuspend) | ||||
|             this._upClient.suspend_sync(null); | ||||
|     }, | ||||
|  | ||||
|     _onActivateRestart: function() { | ||||
|         if (!this._haveRestart) | ||||
|             return; | ||||
|  | ||||
|         this._loginManager.reboot(); | ||||
|         if (Systemd.haveSystemd()) | ||||
|             this._systemdLoginManager.RebootRemote(true); | ||||
|         else | ||||
|             this._consoleKitManager.RestartRemote(); | ||||
|     }, | ||||
|  | ||||
|     _onActivatePowerOff: function() { | ||||
|         if (!this._haveShutdown) | ||||
|             return; | ||||
|  | ||||
|         this._loginManager.powerOff(); | ||||
|         if (Systemd.haveSystemd()) | ||||
|             this._systemdLoginManager.PowerOffRemote(true); | ||||
|         else | ||||
|             this._consoleKitManager.StopRemote(); | ||||
|     } | ||||
| }); | ||||
|   | ||||
							
								
								
									
										139
									
								
								js/gdm/realmd.js
									
									
									
									
									
								
							
							
						
						
									
										139
									
								
								js/gdm/realmd.js
									
									
									
									
									
								
							| @@ -1,139 +0,0 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Gio = imports.gi.Gio; | ||||
| const Lang = imports.lang; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const ProviderIface = <interface name='org.freedesktop.realmd.Provider'> | ||||
|     <property name="Name" type="s" access="read"/> | ||||
|     <property name="Version" type="s" access="read"/> | ||||
|     <property name="Realms" type="ao" access="read"/> | ||||
|     <method name="Discover"> | ||||
|         <arg name="string" type="s" direction="in"/> | ||||
|         <arg name="options" type="a{sv}" direction="in"/> | ||||
|         <arg name="relevance" type="i" direction="out"/> | ||||
|         <arg name="realm" type="ao" direction="out"/> | ||||
|     </method> | ||||
| </interface>; | ||||
| const Provider = Gio.DBusProxy.makeProxyWrapper(ProviderIface); | ||||
|  | ||||
| const ServiceIface = <interface name="org.freedesktop.realmd.Service"> | ||||
|     <method name="Cancel"> | ||||
|         <arg name="operation" type="s" direction="in"/> | ||||
|     </method> | ||||
|     <method name="Release" /> | ||||
|     <method name="SetLocale"> | ||||
|         <arg name="locale" type="s" direction="in"/> | ||||
|     </method> | ||||
|     <signal name="Diagnostics"> | ||||
|         <arg name="data" type="s"/> | ||||
|         <arg name="operation" type="s"/> | ||||
|     </signal> | ||||
| </interface>; | ||||
| const Service = Gio.DBusProxy.makeProxyWrapper(ServiceIface); | ||||
|  | ||||
| const RealmIface = <interface name="org.freedesktop.realmd.Realm"> | ||||
|     <property name="Name" type="s" access="read"/> | ||||
|     <property name="Configured" type="s" access="read"/> | ||||
|     <property name="Details" type="a(ss)" access="read"/> | ||||
|     <property name="LoginFormats" type="as" access="read"/> | ||||
|     <property name="LoginPolicy" type="s" access="read"/> | ||||
|     <property name="PermittedLogins" type="as" access="read"/> | ||||
|     <property name="SupportedInterfaces" type="as" access="read"/> | ||||
|     <method name="ChangeLoginPolicy"> | ||||
|         <arg name="login_policy" type="s" direction="in"/> | ||||
|         <arg name="permitted_add" type="as" direction="in"/> | ||||
|         <arg name="permitted_remove" type="as" direction="in"/> | ||||
|         <arg name="options" type="a{sv}" direction="in"/> | ||||
|     </method> | ||||
|     <method name="Deconfigure"> | ||||
|         <arg name="options" type="a{sv}" direction="in"/> | ||||
|     </method> | ||||
| </interface>; | ||||
| const Realm = Gio.DBusProxy.makeProxyWrapper(RealmIface); | ||||
|  | ||||
| const Manager = new Lang.Class({ | ||||
|     Name: 'Manager', | ||||
|  | ||||
|     _init: function(parentActor) { | ||||
|         this._aggregateProvider = Provider(Gio.DBus.system, | ||||
|                                            'org.freedesktop.realmd', | ||||
|                                            '/org/freedesktop/realmd', | ||||
|                                            Lang.bind(this, this._reloadRealms)) | ||||
|         this._realms = {}; | ||||
|  | ||||
|         this._aggregateProvider.connect('g-properties-changed', | ||||
|                                         Lang.bind(this, function(proxy, properties) { | ||||
|                                             if ('Realms' in properties.deep_unpack()) | ||||
|                                                 this._reloadRealms(); | ||||
|                                         })); | ||||
|     }, | ||||
|  | ||||
|     _reloadRealms: function() { | ||||
|         let realmPaths = this._aggregateProvider.Realms; | ||||
|  | ||||
|         if (!realmPaths) | ||||
|             return; | ||||
|  | ||||
|         for (let i = 0; i < realmPaths.length; i++) { | ||||
|             let realm = Realm(Gio.DBus.system, | ||||
|                               'org.freedesktop.realmd', | ||||
|                               realmPaths[i], | ||||
|                               Lang.bind(this, this._onRealmLoaded)); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _reloadRealm: function(realm) { | ||||
|         if (!realm.Configured) { | ||||
|             if (this._realms[realm.get_object_path()]) | ||||
|                 delete this._realms[realm.get_object_path()]; | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._realms[realm.get_object_path()] = realm; | ||||
|  | ||||
|         this._updateLoginFormat(); | ||||
|     }, | ||||
|  | ||||
|     _onRealmLoaded: function(realm, error) { | ||||
|         if (error) | ||||
|             return; | ||||
|  | ||||
|         this._reloadRealm(realm); | ||||
|  | ||||
|         realm.connect('g-properties-changed', | ||||
|                       Lang.bind(this, function(proxy, properties) { | ||||
|                                 if ('Configured' in properties.deep_unpack()) | ||||
|                                     this._reloadRealm(); | ||||
|                                 })); | ||||
|     }, | ||||
|  | ||||
|     _updateLoginFormat: function() { | ||||
|         let newLoginFormat; | ||||
|  | ||||
|         for (let realmPath in this._realms) { | ||||
|             let realm = this._realms[realmPath]; | ||||
|             if (realm.LoginFormats && realm.LoginFormats.length > 0) { | ||||
|                 newLoginFormat = realm.LoginFormats[0]; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (this._loginFormat != newLoginFormat) { | ||||
|             this._loginFormat = newLoginFormat; | ||||
|             this.emit('login-format-changed', newLoginFormat); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     get loginFormat() { | ||||
|         if (this._loginFormat !== undefined) | ||||
|             return this._loginFormat; | ||||
|  | ||||
|         this._updateLoginFormat(); | ||||
|  | ||||
|         return this._loginFormat; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(Manager.prototype) | ||||
							
								
								
									
										31
									
								
								js/gdm/systemd.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								js/gdm/systemd.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gio = imports.gi.Gio; | ||||
|  | ||||
| const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'> | ||||
| <method name='PowerOff'> | ||||
|     <arg type='b' direction='in'/> | ||||
| </method> | ||||
| <method name='Reboot'> | ||||
|     <arg type='b' direction='in'/> | ||||
| </method> | ||||
| <method name='CanPowerOff'> | ||||
|     <arg type='s' direction='out'/> | ||||
| </method> | ||||
| <method name='CanReboot'> | ||||
|     <arg type='s' direction='out'/> | ||||
| </method> | ||||
| </interface>; | ||||
|  | ||||
| const SystemdLoginManagerProxy = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface); | ||||
|  | ||||
| function SystemdLoginManager() { | ||||
|     return new SystemdLoginManagerProxy(Gio.DBus.system, | ||||
|                                         'org.freedesktop.login1', | ||||
|                                         '/org/freedesktop/login1'); | ||||
| }; | ||||
|  | ||||
| function haveSystemd() { | ||||
|     return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0; | ||||
| } | ||||
							
								
								
									
										176
									
								
								js/gdm/util.js
									
									
									
									
									
								
							
							
						
						
									
										176
									
								
								js/gdm/util.js
									
									
									
									
									
								
							| @@ -2,12 +2,10 @@ | ||||
|  | ||||
| const Gio = imports.gi.Gio; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const Batch = imports.gdm.batch; | ||||
| const Fprint = imports.gdm.fingerprint; | ||||
| const Realmd = imports.gdm.realmd; | ||||
| const Main = imports.ui.main; | ||||
| const Params = imports.misc.params; | ||||
| const Tweener = imports.ui.tweener; | ||||
| @@ -20,10 +18,8 @@ const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen'; | ||||
| const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication'; | ||||
| const BANNER_MESSAGE_KEY = 'banner-message-enable'; | ||||
| const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text'; | ||||
| const ALLOWED_FAILURES_KEY = 'allowed-failures'; | ||||
|  | ||||
| const LOGO_KEY = 'logo'; | ||||
| const DISABLE_USER_LIST_KEY = 'disable-user-list'; | ||||
|  | ||||
| function fadeInActor(actor) { | ||||
|     if (actor.opacity == 255 && actor.visible) | ||||
| @@ -82,19 +78,16 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA }); | ||||
|  | ||||
|         this._fprintManager = new Fprint.FprintManager(); | ||||
|         this._realmManager = new Realmd.Manager(); | ||||
|         this._cancellable = new Gio.Cancellable(); | ||||
|  | ||||
|         this._failCounter = 0; | ||||
|         this._fprintManager = new Fprint.FprintManager(); | ||||
|         this._checkForFingerprintReader(); | ||||
|     }, | ||||
|  | ||||
|     begin: function(userName, hold) { | ||||
|         this._cancellable = new Gio.Cancellable(); | ||||
|         this._hold = hold; | ||||
|         this._userName = userName; | ||||
|  | ||||
|         this._checkForFingerprintReader(); | ||||
|  | ||||
|         if (userName) { | ||||
|             // If possible, reauthenticate an already running session, | ||||
|             // so any session specific credentials get updated appropriately | ||||
| @@ -106,18 +99,14 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     cancel: function() { | ||||
|         if (this._cancellable) | ||||
|             this._cancellable.cancel(); | ||||
|         this._cancellable.cancel(); | ||||
|  | ||||
|         if (this._userVerifier) | ||||
|             this._userVerifier.call_cancel_sync(null); | ||||
|     }, | ||||
|  | ||||
|     clear: function() { | ||||
|         if (this._cancellable) { | ||||
|             this._cancellable.cancel(); | ||||
|             this._cancellable = null; | ||||
|         } | ||||
|         this._cancellable.cancel(); | ||||
|  | ||||
|         if (this._userVerifier) { | ||||
|             this._userVerifier.run_dispose(); | ||||
| @@ -126,9 +115,6 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     answerQuery: function(serviceName, answer) { | ||||
|         // Clear any previous message | ||||
|         this.emit('show-message', null, null); | ||||
|  | ||||
|         this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null); | ||||
|     }, | ||||
|  | ||||
| @@ -145,48 +131,34 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
|     _reportInitError: function(where, error) { | ||||
|         logError(error, where); | ||||
|         this._hold.release(); | ||||
|  | ||||
|         this.emit('show-message', _("Authentication error"), 'login-dialog-message-warning'); | ||||
|         this._verificationFailed(false); | ||||
|     }, | ||||
|  | ||||
|     _reauthenticationChannelOpened: function(client, result) { | ||||
|         try { | ||||
|             this._userVerifier = client.open_reauthentication_channel_finish(result); | ||||
|         } catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) { | ||||
|             return; | ||||
|         } catch(e if e.matches(Gio.DBusError, Gio.DBusError.ACCESS_DENIED) && | ||||
|                 !this._reauthOnly) { | ||||
|             // Gdm emits org.freedesktop.DBus.Error.AccessDenied when there is | ||||
|             // no session to reauthenticate. Fall back to performing verification | ||||
|             // from this login session | ||||
|             client.get_user_verifier(this._cancellable, Lang.bind(this, this._userVerifierGot)); | ||||
|             return; | ||||
|         } catch(e) { | ||||
|             this._reportInitError('Failed to open reauthentication channel', e); | ||||
|             return; | ||||
|         } | ||||
|             this._connectSignals(); | ||||
|             this._beginVerification(); | ||||
|  | ||||
|         this._connectSignals(); | ||||
|         this._beginVerification(); | ||||
|         this._hold.release(); | ||||
|             this._hold.release(); | ||||
|         } catch (e) { | ||||
|             if (this._reauthOnly) { | ||||
|                 logError(e, 'Failed to open reauthentication channel'); | ||||
|  | ||||
|                 this._hold.release(); | ||||
|                 this.emit('verification-failed'); | ||||
|  | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             // If there's no session running, or it otherwise fails, then fall back | ||||
|             // to performing verification from this login session | ||||
|             client.get_user_verifier(this._cancellable, Lang.bind(this, this._userVerifierGot)); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _userVerifierGot: function(client, result) { | ||||
|         try { | ||||
|             this._userVerifier = client.get_user_verifier_finish(result); | ||||
|         } catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) { | ||||
|             return; | ||||
|         } catch(e) { | ||||
|             this._reportInitError('Failed to obtain user verifier', e); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._userVerifier = client.get_user_verifier_finish(result); | ||||
|         this._connectSignals(); | ||||
|         this._beginVerification(); | ||||
|  | ||||
|         this._hold.release(); | ||||
|     }, | ||||
|  | ||||
| @@ -208,15 +180,7 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|                                                                 this._userName, | ||||
|                                                                 this._cancellable, | ||||
|                                                                 Lang.bind(this, function(obj, result) { | ||||
|                 try { | ||||
|                     obj.call_begin_verification_for_user_finish(result); | ||||
|                 } catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) { | ||||
|                     return; | ||||
|                 } catch(e) { | ||||
|                     this._reportInitError('Failed to start verification for user', e); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 obj.call_begin_verification_for_user_finish(result); | ||||
|                 this._hold.release(); | ||||
|             })); | ||||
|  | ||||
| @@ -227,15 +191,7 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|                                                                     this._userName, | ||||
|                                                                     this._cancellable, | ||||
|                                                                     Lang.bind(this, function(obj, result) { | ||||
|                     try { | ||||
|                         obj.call_begin_verification_for_user_finish(result); | ||||
|                     } catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) { | ||||
|                         return; | ||||
|                     } catch(e) { | ||||
|                         this._reportInitError('Failed to start fingerprint verification for user', e); | ||||
|                         return; | ||||
|                     } | ||||
|  | ||||
|                     obj.call_begin_verification_for_user_finish(result); | ||||
|                     this._hold.release(); | ||||
|                 })); | ||||
|             } | ||||
| @@ -243,15 +199,7 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|             this._userVerifier.call_begin_verification(PASSWORD_SERVICE_NAME, | ||||
|                                                        this._cancellable, | ||||
|                                                        Lang.bind(this, function(obj, result) { | ||||
|                 try { | ||||
|                     obj.call_begin_verification_finish(result); | ||||
|                 } catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) { | ||||
|                     return; | ||||
|                 } catch(e) { | ||||
|                     this._reportInitError('Failed to start verification', e); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 obj.call_begin_verification_finish(result); | ||||
|                 this._hold.release(); | ||||
|             })); | ||||
|         } | ||||
| @@ -263,12 +211,9 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|         // as a cue to display our own message. | ||||
|         if (serviceName == FINGERPRINT_SERVICE_NAME && | ||||
|             this._haveFingerprintReader) { | ||||
|  | ||||
|             // Translators: this message is shown below the password entry field | ||||
|             // to indicate the user can swipe their finger instead | ||||
|             this.emit('show-login-hint', _("(or swipe finger)")); | ||||
|             this.emit('show-fingerprint-prompt'); | ||||
|         } else if (serviceName == PASSWORD_SERVICE_NAME) { | ||||
|             this.emit('show-message', info, 'login-dialog-message-info'); | ||||
|             Main.notifyError(info); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -277,22 +222,7 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|         // users who haven't enrolled their fingerprint. | ||||
|         if (serviceName != PASSWORD_SERVICE_NAME) | ||||
|             return; | ||||
|         this.emit('show-message', problem, 'login-dialog-message-warning'); | ||||
|     }, | ||||
|  | ||||
|     _showRealmLoginHint: function() { | ||||
|         if (this._realmManager.loginFormat) { | ||||
|             let hint = this._realmManager.loginFormat; | ||||
|  | ||||
|             hint = hint.replace(/%U/g, 'user'); | ||||
|             hint = hint.replace(/%D/g, 'DOMAIN'); | ||||
|             hint = hint.replace(/%[^UD]/g, ''); | ||||
|  | ||||
|             // Translators: this message is shown below the username entry field | ||||
|             // to clue the user in on how to login to the local network realm | ||||
|             this.emit('show-login-hint', | ||||
|                       _("(e.g., user or %s)").format(hint)); | ||||
|         } | ||||
|         Main.notifyError(problem); | ||||
|     }, | ||||
|  | ||||
|     _onInfoQuery: function(client, serviceName, question) { | ||||
| @@ -300,10 +230,6 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|         if (serviceName != PASSWORD_SERVICE_NAME) | ||||
|             return; | ||||
|  | ||||
|         this._showRealmLoginHint(); | ||||
|         this._realmLoginHintSignalId = this._realmManager.connect('login-format-changed', | ||||
|                                                                   Lang.bind(this, this._showRealmLoginHint)); | ||||
|  | ||||
|         this.emit('ask-question', serviceName, question, ''); | ||||
|     }, | ||||
|  | ||||
| @@ -316,10 +242,10 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onReset: function() { | ||||
|         this.clear(); | ||||
|         this._userVerifier.run_dispose(); | ||||
|         this._userVerifier = null; | ||||
|  | ||||
|         // Clear previous attempts to authenticate | ||||
|         this._failCounter = 0; | ||||
|         this._checkForFingerprintReader(); | ||||
|  | ||||
|         this.emit('reset'); | ||||
|     }, | ||||
| @@ -328,44 +254,14 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|         this.emit('verification-complete'); | ||||
|     }, | ||||
|  | ||||
|     _verificationFailed: function(retry) { | ||||
|         // For Not Listed / enterprise logins, immediately reset | ||||
|         // the dialog | ||||
|         // Otherwise, we allow ALLOWED_FAILURES attempts. After that, we | ||||
|         // go back to the welcome screen. | ||||
|  | ||||
|         this._failCounter++; | ||||
|         let canRetry = retry && this._userName && | ||||
|             this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY); | ||||
|  | ||||
|         if (canRetry) { | ||||
|             this.clear(); | ||||
|             this.begin(this._userName, new Batch.Hold()); | ||||
|         } else { | ||||
|             // Allow some time to see the message, then reset everything | ||||
|             Mainloop.timeout_add(3000, Lang.bind(this, function() { | ||||
|                 this.cancel(); | ||||
|  | ||||
|                 this._onReset(); | ||||
|             })); | ||||
|         } | ||||
|  | ||||
|         this.emit('verification-failed'); | ||||
|     }, | ||||
|  | ||||
|     _onConversationStopped: function(client, serviceName) { | ||||
|         // if the password service fails, then cancel everything. | ||||
|         // But if, e.g., fingerprint fails, still give | ||||
|         // password authentication a chance to succeed | ||||
|         if (serviceName == PASSWORD_SERVICE_NAME) { | ||||
|             this._verificationFailed(true); | ||||
|         } | ||||
|  | ||||
|         this.emit('hide-login-hint'); | ||||
|  | ||||
|         if (this._realmLoginHintSignalId) { | ||||
|             this._realmManager.disconnect(this._realmLoginHintSignalId); | ||||
|             this._realmLoginHintSignalId = 0; | ||||
|             this.emit('verification-failed'); | ||||
|         } else if (serviceName == FINGERPRINT_SERVICE_NAME) { | ||||
|             this.emit('hide-fingerprint-prompt'); | ||||
|         } | ||||
|     }, | ||||
| }); | ||||
|   | ||||
| @@ -120,6 +120,11 @@ function createExtensionObject(uuid, dir, type) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Encourage people to add this | ||||
|     if (!meta.url) { | ||||
|         log('Warning: Missing "url" property in %s/metadata.json'.format(uuid)); | ||||
|     } | ||||
|  | ||||
|     if (uuid != meta.uuid) { | ||||
|         throw new Error('uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"'); | ||||
|     } | ||||
| @@ -156,8 +161,7 @@ const ExtensionFinder = new Lang.Class({ | ||||
|         try { | ||||
|             fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null); | ||||
|         } catch(e) { | ||||
|             if (e.domain != Gio.io_error_quark() || e.code != Gio.IOErrorEnum.NOT_FOUND) | ||||
|                 logError(e, 'Could not enumerate extensions directory'); | ||||
|             logError(e, 'Could not enumerate extensions directory'); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -41,26 +41,24 @@ const HistoryManager = new Lang.Class({ | ||||
|         this._historyIndex = this._history.length; | ||||
|     }, | ||||
|  | ||||
|     _setPrevItem: function(text) { | ||||
|     prevItem: function(text) { | ||||
|         if (this._historyIndex <= 0) | ||||
|             return false; | ||||
|             return text; | ||||
|  | ||||
|         if (text) | ||||
|             this._history[this._historyIndex] = text; | ||||
|         this._historyIndex--; | ||||
|         this._indexChanged(); | ||||
|         return true; | ||||
|         return this._indexChanged(); | ||||
|     }, | ||||
|  | ||||
|     _setNextItem: function(text) { | ||||
|     nextItem: function(text) { | ||||
|         if (this._historyIndex >= this._history.length) | ||||
|             return false; | ||||
|             return text; | ||||
|  | ||||
|         if (text) | ||||
|             this._history[this._historyIndex] = text; | ||||
|         this._historyIndex++; | ||||
|         this._indexChanged(); | ||||
|         return true; | ||||
|         return this._indexChanged(); | ||||
|     }, | ||||
|  | ||||
|     lastItem: function() { | ||||
| @@ -85,9 +83,11 @@ const HistoryManager = new Lang.Class({ | ||||
|     _onEntryKeyPress: function(entry, event) { | ||||
|         let symbol = event.get_key_symbol(); | ||||
|         if (symbol == Clutter.KEY_Up) { | ||||
|             return this._setPrevItem(entry.get_text()); | ||||
|             this.prevItem(entry.get_text()); | ||||
|             return true; | ||||
|         } else if (symbol == Clutter.KEY_Down) { | ||||
|             return this._setNextItem(entry.get_text()); | ||||
|             this.nextItem(entry.get_text()); | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     }, | ||||
| @@ -98,6 +98,8 @@ const HistoryManager = new Lang.Class({ | ||||
|  | ||||
|         if (this._entry) | ||||
|             this._entry.set_text(current); | ||||
|  | ||||
|         return current; | ||||
|     }, | ||||
|  | ||||
|     _save: function() { | ||||
|   | ||||
| @@ -1,259 +0,0 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gio = imports.gi.Gio; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Shell = imports.gi.Shell; | ||||
| const UPowerGlib = imports.gi.UPowerGlib; | ||||
|  | ||||
| const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'> | ||||
| <method name='PowerOff'> | ||||
|     <arg type='b' direction='in'/> | ||||
| </method> | ||||
| <method name='Reboot'> | ||||
|     <arg type='b' direction='in'/> | ||||
| </method> | ||||
| <method name='Suspend'> | ||||
|     <arg type='b' direction='in'/> | ||||
| </method> | ||||
| <method name='Hibernate'> | ||||
|     <arg type='b' direction='in'/> | ||||
| </method> | ||||
| <method name='CanPowerOff'> | ||||
|     <arg type='s' direction='out'/> | ||||
| </method> | ||||
| <method name='CanReboot'> | ||||
|     <arg type='s' direction='out'/> | ||||
| </method> | ||||
| <method name='CanSuspend'> | ||||
|     <arg type='s' direction='out'/> | ||||
| </method> | ||||
| <method name='CanHibernate'> | ||||
|     <arg type='s' direction='out'/> | ||||
| </method> | ||||
| </interface>; | ||||
|  | ||||
| const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'> | ||||
| <signal name='Lock' /> | ||||
| <signal name='Unlock' /> | ||||
| </interface>; | ||||
|  | ||||
| const SystemdLoginManager = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface); | ||||
| const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface); | ||||
|  | ||||
| const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'> | ||||
| <method name='CanRestart'> | ||||
|     <arg type='b' direction='out'/> | ||||
| </method> | ||||
| <method name='CanStop'> | ||||
|     <arg type='b' direction='out'/> | ||||
| </method> | ||||
| <method name='Restart' /> | ||||
| <method name='Stop' /> | ||||
| <method name='GetCurrentSession'> | ||||
|     <arg type='o' direction='out' /> | ||||
| </method> | ||||
| </interface>; | ||||
|  | ||||
| const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'> | ||||
| <method name='IsActive'> | ||||
|     <arg type='b' direction='out' /> | ||||
| </method> | ||||
| <signal name='ActiveChanged'> | ||||
|     <arg type='b' direction='out' /> | ||||
| </signal> | ||||
| <signal name='Lock' /> | ||||
| <signal name='Unlock' /> | ||||
| </interface>; | ||||
|  | ||||
| const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface); | ||||
| const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface); | ||||
|  | ||||
| function haveSystemd() { | ||||
|     return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0; | ||||
| } | ||||
|  | ||||
| let _loginManager = null; | ||||
|  | ||||
| /** | ||||
|  * LoginManager: | ||||
|  * An abstraction over systemd/logind and ConsoleKit. | ||||
|  * | ||||
|  */ | ||||
| function getLoginManager() { | ||||
|     if (_loginManager == null) { | ||||
|         if (haveSystemd()) | ||||
|             _loginManager = new LoginManagerSystemd(); | ||||
|         else | ||||
|             _loginManager = new LoginManagerConsoleKit(); | ||||
|     } | ||||
|  | ||||
|     return _loginManager; | ||||
| } | ||||
|  | ||||
| const LoginManagerSystemd = new Lang.Class({ | ||||
|     Name: 'LoginManagerSystemd', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._proxy = new SystemdLoginManager(Gio.DBus.system, | ||||
|                                               'org.freedesktop.login1', | ||||
|                                               '/org/freedesktop/login1'); | ||||
|     }, | ||||
|  | ||||
|     // Having this function is a bit of a hack since the Systemd and ConsoleKit | ||||
|     // session objects have different interfaces - but in both cases there are | ||||
|     // Lock/Unlock signals, and that's all we count upon at the moment. | ||||
|     getCurrentSessionProxy: function() { | ||||
|         if (!this._currentSession) { | ||||
|             this._currentSession = new SystemdLoginSession(Gio.DBus.system, | ||||
|                                                            'org.freedesktop.login1', | ||||
|                                                            '/org/freedesktop/login1/session/' + | ||||
|                                                            GLib.getenv('XDG_SESSION_ID')); | ||||
|         } | ||||
|  | ||||
|         return this._currentSession; | ||||
|     }, | ||||
|  | ||||
|     get sessionActive() { | ||||
|         return Shell.session_is_active_for_systemd(); | ||||
|     }, | ||||
|  | ||||
|     canPowerOff: function(asyncCallback) { | ||||
|         this._proxy.CanPowerOffRemote(function(result, error) { | ||||
|             if (error) | ||||
|                 asyncCallback(false); | ||||
|             else | ||||
|                 asyncCallback(result[0] != 'no'); | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|     canReboot: function(asyncCallback) { | ||||
|         this._proxy.CanRebootRemote(function(result, error) { | ||||
|             if (error) | ||||
|                 asyncCallback(false); | ||||
|             else | ||||
|                 asyncCallback(result[0] != 'no'); | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|     canSuspend: function(asyncCallback) { | ||||
|         this._proxy.CanSuspendRemote(function(result, error) { | ||||
|             if (error) | ||||
|                 asyncCallback(false); | ||||
|             else | ||||
|                 asyncCallback(result[0] != 'no'); | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|     canHibernate: function(asyncCallback) { | ||||
|         this._proxy.CanSuspendRemote(function(result, error) { | ||||
|             if (error) | ||||
|                 asyncCallback(false); | ||||
|             else | ||||
|                 asyncCallback(result[0] != 'no'); | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|     powerOff: function() { | ||||
|         this._proxy.PowerOffRemote(true); | ||||
|     }, | ||||
|  | ||||
|     reboot: function() { | ||||
|         this._proxy.RebootRemote(true); | ||||
|     }, | ||||
|  | ||||
|     suspend: function() { | ||||
|         this._proxy.SuspendRemote(true); | ||||
|     }, | ||||
|  | ||||
|     hibernate: function() { | ||||
|         this._proxy.HibernateRemote(true); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const LoginManagerConsoleKit = new Lang.Class({ | ||||
|     Name: 'LoginManagerConsoleKit', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._proxy = new ConsoleKitManager(Gio.DBus.system, | ||||
|                                             'org.freedesktop.ConsoleKit', | ||||
|                                             '/org/freedesktop/ConsoleKit/Manager'); | ||||
|         this._upClient = new UPowerGlib.Client(); | ||||
|     }, | ||||
|  | ||||
|     // Having this function is a bit of a hack since the Systemd and ConsoleKit | ||||
|     // session objects have different interfaces - but in both cases there are | ||||
|     // Lock/Unlock signals, and that's all we count upon at the moment. | ||||
|     getCurrentSessionProxy: function() { | ||||
|         if (!this._currentSession) { | ||||
|             let [currentSessionId] = this._proxy.GetCurrentSessionSync(); | ||||
|             this._currentSession = new ConsoleKitSession(Gio.DBus.system, | ||||
|                                                          'org.freedesktop.ConsoleKit', | ||||
|                                                          currentSessionId); | ||||
|         } | ||||
|  | ||||
|         return this._currentSession; | ||||
|     }, | ||||
|  | ||||
|     get sessionActive() { | ||||
|         if (this._sessionActive !== undefined) | ||||
|             return this._sessionActive; | ||||
|  | ||||
|         let session = this.getCurrentSessionProxy(); | ||||
|         session.connectSignal('ActiveChanged', Lang.bind(this, function(object, senderName, [isActive]) { | ||||
|             this._sessionActive = isActive; | ||||
|         })); | ||||
|         [this._sessionActive] = session.IsActiveSync(); | ||||
|  | ||||
|         return this._sessionActive; | ||||
|     }, | ||||
|  | ||||
|     canPowerOff: function(asyncCallback) { | ||||
|         this._proxy.CanStopRemote(function(result, error) { | ||||
|             if (error) | ||||
|                 asyncCallback(false); | ||||
|             else | ||||
|                 asyncCallback(result[0]); | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|     canReboot: function(asyncCallback) { | ||||
|         this._proxy.CanRestartRemote(function(result, error) { | ||||
|             if (error) | ||||
|                 asyncCallback(false); | ||||
|             else | ||||
|                 asyncCallback(result[0]); | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|     canSuspend: function(asyncCallback) { | ||||
|         Mainloop.idle_add(Lang.bind(this, function() { | ||||
|             asyncCallback(this._upClient.get_can_suspend()); | ||||
|             return false; | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     canHibernate: function(asyncCallback) { | ||||
|         Mainloop.idle_add(Lang.bind(this, function() { | ||||
|             asyncCallback(this._upClient.get_can_hibernate()); | ||||
|             return false; | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     powerOff: function() { | ||||
|         this._proxy.StopRemote(); | ||||
|     }, | ||||
|  | ||||
|     reboot: function() { | ||||
|         this._proxy.RestartRemote(); | ||||
|     }, | ||||
|  | ||||
|     suspend: function() { | ||||
|         this._upClient.suspend_sync(null); | ||||
|     }, | ||||
|  | ||||
|     hibernate: function() { | ||||
|         this._upClient.hibernate_sync(null); | ||||
|     } | ||||
| }); | ||||
| @@ -46,70 +46,8 @@ let _providersTable; | ||||
| function _getProvidersTable() { | ||||
|     if (_providersTable) | ||||
|         return _providersTable; | ||||
|     return _providersTable = Shell.mobile_providers_parse(null,null); | ||||
| } | ||||
|  | ||||
| function findProviderForMCCMNC(table, needle) { | ||||
|     let needlemcc = needle.substring(0, 3); | ||||
|     let needlemnc = needle.substring(3, needle.length); | ||||
|  | ||||
|     let name2, name3; | ||||
|     for (let iter in table) { | ||||
|         let country = table[iter]; | ||||
|         let providers = country.get_providers(); | ||||
|  | ||||
|         // Search through each country's providers | ||||
|         for (let i = 0; i < providers.length; i++) { | ||||
|             let provider = providers[i]; | ||||
|  | ||||
|             // Search through MCC/MNC list | ||||
|             let list = provider.get_gsm_mcc_mnc(); | ||||
|             for (let j = 0; j < list.length; j++) { | ||||
|                 let mccmnc = list[j]; | ||||
|  | ||||
|                 // Match both 2-digit and 3-digit MNC; prefer a | ||||
|                 // 3-digit match if found, otherwise a 2-digit one. | ||||
|                 if (mccmnc.mcc != needlemcc) | ||||
|                     continue;  // MCC was wrong | ||||
|  | ||||
|                 if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc) | ||||
|                     name3 = provider.name; | ||||
|  | ||||
|                 if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2)) | ||||
|                     name2 = provider.name; | ||||
|  | ||||
|                 if (name2 && name3) | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return name3 || name2 || null; | ||||
| } | ||||
|  | ||||
| function findProviderForSid(table, sid) { | ||||
|     if (sid == 0) | ||||
|         return null; | ||||
|  | ||||
|     // Search through each country | ||||
|     for (let iter in table) { | ||||
|         let country = table[iter]; | ||||
|         let providers = country.get_providers(); | ||||
|  | ||||
|         // Search through each country's providers | ||||
|         for (let i = 0; i < providers.length; i++) { | ||||
|             let provider = providers[i]; | ||||
|             let cdma_sid = provider.get_cdma_sid(); | ||||
|  | ||||
|             // Search through CDMA SID list | ||||
|             for (let j = 0; j < cdma_sid.length; j++) { | ||||
|                 if (cdma_sid[j] == sid) | ||||
|                     return provider.name; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return null; | ||||
|     let [providers, countryCodes] = Shell.mobile_providers_parse(); | ||||
|     return _providersTable = providers; | ||||
| } | ||||
|  | ||||
| const ModemGsm = new Lang.Class({ | ||||
| @@ -153,29 +91,64 @@ const ModemGsm = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _findOperatorName: function(name, opCode) { | ||||
|         if (name) { | ||||
|             if (name && name.length != 0 && (name.length > 6 || name.length < 5)) { | ||||
|                 // this looks like a valid name, i.e. not an MCCMNC (that some | ||||
|                 // devices return when not yet connected | ||||
|                 return name; | ||||
|             } | ||||
|             if (isNaN(parseInt(name))) { | ||||
|                 // name is definitely not a MCCMNC, so it may be a name | ||||
|                 // after all; return that | ||||
|                 return name; | ||||
|             } | ||||
|         if (name.length != 0 && (name.length > 6 || name.length < 5)) { | ||||
|             // this looks like a valid name, i.e. not an MCCMNC (that some | ||||
|             // devices return when not yet connected | ||||
|             return name; | ||||
|         } | ||||
|         if (isNaN(parseInt(name))) { | ||||
|             // name is definitely not a MCCMNC, so it may be a name | ||||
|             // after all; return that | ||||
|             return name; | ||||
|         } | ||||
|  | ||||
|         let needle; | ||||
|         if ((name == null || name.length == 0) && opCode) | ||||
|         if (name.length == 0 && opCode) | ||||
|             needle = opCode; | ||||
|         else if (name.length == 6 || name.length == 5) | ||||
|             needle = name; | ||||
|         else // nothing to search | ||||
|             return null; | ||||
|  | ||||
|         return this._findProviderForMCCMNC(needle); | ||||
|     }, | ||||
|  | ||||
|     _findProviderForMCCMNC: function(needle) { | ||||
|         let table = _getProvidersTable(); | ||||
|         return findProviderForMCCMNC(table, needle); | ||||
|         let needlemcc = needle.substring(0, 3); | ||||
|         let needlemnc = needle.substring(3, needle.length); | ||||
|  | ||||
|         let name2, name3; | ||||
|         for (let iter in table) { | ||||
|             let providers = table[iter]; | ||||
|  | ||||
|             // Search through each country's providers | ||||
|             for (let i = 0; i < providers.length; i++) { | ||||
|                 let provider = providers[i]; | ||||
|  | ||||
|                 // Search through MCC/MNC list | ||||
|                 let list = provider.get_gsm_mcc_mnc(); | ||||
|                 for (let j = 0; j < list.length; j++) { | ||||
|                     let mccmnc = list[j]; | ||||
|  | ||||
|                     // Match both 2-digit and 3-digit MNC; prefer a | ||||
|                     // 3-digit match if found, otherwise a 2-digit one. | ||||
|                     if (mccmnc.mcc != needlemcc) | ||||
|                         continue;  // MCC was wrong | ||||
|  | ||||
|                     if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc) | ||||
|                         name3 = provider.name; | ||||
|  | ||||
|                     if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2)) | ||||
|                         name2 = provider.name; | ||||
|  | ||||
|                     if (name2 && name3) | ||||
|                         break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return name3 || name2 || null; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(ModemGsm.prototype); | ||||
| @@ -216,14 +189,39 @@ const ModemCdma = new Lang.Class({ | ||||
|                 this.operator_name = null; | ||||
|             } else { | ||||
|                 let [bandClass, band, id] = result; | ||||
|                 if (name.length > 0) { | ||||
|                     let table = _getProvidersTable(); | ||||
|                     this.operator_name = findProviderForSid(table, id); | ||||
|                 } else | ||||
|                 if (name.length > 0) | ||||
|                     this.operator_name = this._findProviderForSid(id); | ||||
|                 else | ||||
|                     this.operator_name = null; | ||||
|             } | ||||
|             this.emit('notify::operator-name'); | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     _findProviderForSid: function(sid) { | ||||
|         if (sid == 0) | ||||
|             return null; | ||||
|  | ||||
|         let table = _getProvidersTable(); | ||||
|  | ||||
|         // Search through each country | ||||
|         for (let iter in table) { | ||||
|             let providers = table[iter]; | ||||
|  | ||||
|             // Search through each country's providers | ||||
|             for (let i = 0; i < providers.length; i++) { | ||||
|                 let provider = providers[i]; | ||||
|                 let cdma_sid = provider.get_cdma_sid(); | ||||
|  | ||||
|                 // Search through CDMA SID list | ||||
|                 for (let j = 0; j < cdma_sid.length; j++) { | ||||
|                     if (cdma_sid[j] == sid) | ||||
|                         return provider.name; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(ModemCdma.prototype); | ||||
|   | ||||
| @@ -84,7 +84,7 @@ function trySpawn(argv) | ||||
|     try { | ||||
|         [success, pid] = GLib.spawn_async(null, argv, null, | ||||
|                                           GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD, | ||||
|                                           null); | ||||
|                                           null, null); | ||||
|     } catch (err) { | ||||
|         /* Rewrite the error in case of ENOENT */ | ||||
|         if (err.matches(GLib.SpawnError, GLib.SpawnError.NOENT)) { | ||||
| @@ -288,31 +288,3 @@ function insertSorted(array, val, cmp) { | ||||
|  | ||||
|     return pos; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * wrapKeybinding: | ||||
|  * | ||||
|  * Wrap a keybinding handler so that | ||||
|  * it ignores an invocation if the shell is modal, but | ||||
|  * not when the overview is active, or when global | ||||
|  * keybindings are allowed by session mode. | ||||
|  * This function is only useful for keybindings installed | ||||
|  * with Meta.KeybindingFlags.HANDLE_WHEN_GRABBED | ||||
|  */ | ||||
| function wrapKeybinding(handler, onlyInOverview) { | ||||
|     return function() { | ||||
|         let handle; | ||||
|  | ||||
|         if (onlyInOverview) { | ||||
|             handle = Main.sessionMode.allowKeybindingsWhenModal || | ||||
|                 Main.modalCount == (Main.overview.visible ? 1 : 0); | ||||
|         } else { | ||||
|             handle = true; | ||||
|         } | ||||
|  | ||||
|         if (handle) | ||||
|             return handler.apply(this, arguments); | ||||
|         else | ||||
|             return false; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -115,10 +115,10 @@ function run() { | ||||
|  | ||||
|     for (let i = 0; i < 2; i++) { | ||||
|         Scripting.scriptEvent('applicationsShowStart'); | ||||
|         Main.overview._dash.showAppsButton.checked = true; | ||||
|         Main.overview._viewSelector.switchTab('applications'); | ||||
|         yield Scripting.waitLeisure(); | ||||
|         Scripting.scriptEvent('applicationsShowDone'); | ||||
|         Main.overview._dash.showAppsButton.checked = false; | ||||
|         Main.overview._viewSelector.switchTab('windows'); | ||||
|         yield Scripting.waitLeisure(); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -199,7 +199,7 @@ const AltTabPopup = new Lang.Class({ | ||||
|         this.actor.get_allocation_box(); | ||||
|  | ||||
|         // Make the initial selection | ||||
|         if (binding == 'internal-keybinding-switch-group') { | ||||
|         if (binding == 'switch-group') { | ||||
|             if (backward) { | ||||
|                 this._select(0, this._appIcons[0].cachedWindows.length - 1); | ||||
|             } else { | ||||
| @@ -208,9 +208,9 @@ const AltTabPopup = new Lang.Class({ | ||||
|                 else | ||||
|                     this._select(0, 0); | ||||
|             } | ||||
|         } else if (binding == 'internal-keybinding-switch-group-backward') { | ||||
|         } else if (binding == 'switch-group-backward') { | ||||
|             this._select(0, this._appIcons[0].cachedWindows.length - 1); | ||||
|         } else if (binding == 'internal-keybinding-switch-windows-backward') { | ||||
|         } else if (binding == 'switch-windows-backward') { | ||||
|             this._select(this._appIcons.length - 1); | ||||
|         } else if (this._appIcons.length == 1) { | ||||
|             this._select(0); | ||||
|   | ||||
| @@ -479,6 +479,7 @@ const AppWellIcon = new Lang.Class({ | ||||
|                                                 Lang.bind(this, | ||||
|                                                           this._onStateChanged)); | ||||
|         this._onStateChanged(); | ||||
|         this.isMenuUp = false; | ||||
|     }, | ||||
|  | ||||
|     _onDestroy: function() { | ||||
| @@ -560,8 +561,7 @@ const AppWellIcon = new Lang.Class({ | ||||
|             this._menuManager.addMenu(this._menu); | ||||
|         } | ||||
|  | ||||
|         this.emit('menu-state-changed', true); | ||||
|  | ||||
|         this.isMenuUp = true; | ||||
|         this.actor.set_hover(true); | ||||
|         this._menu.popup(); | ||||
|  | ||||
| @@ -578,7 +578,7 @@ const AppWellIcon = new Lang.Class({ | ||||
|  | ||||
|     _onMenuPoppedDown: function() { | ||||
|         this.actor.sync_hover(); | ||||
|         this.emit('menu-state-changed', false); | ||||
|         this.isMenuUp = false; | ||||
|     }, | ||||
|  | ||||
|     _onActivate: function (event) { | ||||
|   | ||||
| @@ -84,12 +84,9 @@ const AppFavorites = new Lang.Class({ | ||||
|  | ||||
|         let app = Shell.AppSystem.get_default().lookup_app(appId); | ||||
|  | ||||
|         Main.overview.setMessage(_("%s has been added to your favorites.").format(app.get_name()), | ||||
|                                  { forFeedback: true, | ||||
|                                    undoCallback: Lang.bind(this, function () { | ||||
|                                                                this._removeFavorite(appId); | ||||
|                                                            }) | ||||
|                                  }); | ||||
|         Main.overview.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () { | ||||
|             this._removeFavorite(appId); | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     addFavorite: function(appId) { | ||||
| @@ -119,11 +116,9 @@ const AppFavorites = new Lang.Class({ | ||||
|             return; | ||||
|  | ||||
|         Main.overview.setMessage(_("%s has been removed from your favorites.").format(app.get_name()), | ||||
|                                  { forFeedback: true, | ||||
|                                    undoCallback: Lang.bind(this, function () { | ||||
|                                                                this._addFavorite(appId, pos); | ||||
|                                                            }) | ||||
|                                  }); | ||||
|                                  Lang.bind(this, function () { | ||||
|             this._addFavorite(appId, pos); | ||||
|         })); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(AppFavorites.prototype); | ||||
|   | ||||
| @@ -5,12 +5,11 @@ const Mainloop = imports.mainloop; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gio = imports.gi.Gio; | ||||
| const Params = imports.misc.params; | ||||
| const Shell = imports.gi.Shell; | ||||
| 
 | ||||
| const GnomeSession = imports.misc.gnomeSession; | ||||
| const LoginManager = imports.misc.loginManager; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Main = imports.ui.main; | ||||
| const ShellMountOperation = imports.ui.shellMountOperation; | ||||
| const GnomeSession = imports.misc.gnomeSession; | ||||
| 
 | ||||
| const GNOME_SESSION_AUTOMOUNT_INHIBIT = 16; | ||||
| 
 | ||||
| @@ -20,6 +19,62 @@ const SETTING_ENABLE_AUTOMOUNT = 'automount'; | ||||
| 
 | ||||
| const AUTORUN_EXPIRE_TIMEOUT_SECS = 10; | ||||
| 
 | ||||
| const ConsoleKitSessionIface = <interface name="org.freedesktop.ConsoleKit.Session"> | ||||
| <method name="IsActive"> | ||||
|     <arg type="b" direction="out" /> | ||||
| </method> | ||||
| <signal name="ActiveChanged"> | ||||
|     <arg type="b" direction="out" /> | ||||
| </signal> | ||||
| </interface>; | ||||
| 
 | ||||
| const ConsoleKitSessionProxy = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface); | ||||
| 
 | ||||
| const ConsoleKitManagerIface = <interface name="org.freedesktop.ConsoleKit.Manager"> | ||||
| <method name="GetCurrentSession"> | ||||
|     <arg type="o" direction="out" /> | ||||
| </method> | ||||
| </interface>; | ||||
| 
 | ||||
| const ConsoleKitManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ConsoleKitManagerIface); | ||||
| 
 | ||||
| function ConsoleKitManager() { | ||||
|     var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system, | ||||
| 				   g_interface_name: ConsoleKitManagerInfo.name, | ||||
| 				   g_interface_info: ConsoleKitManagerInfo, | ||||
| 				   g_name: 'org.freedesktop.ConsoleKit', | ||||
| 				   g_object_path: '/org/freedesktop/ConsoleKit/Manager', | ||||
|                                    g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START | | ||||
|                                              Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) }); | ||||
| 
 | ||||
|     self._updateSessionActive = function() { | ||||
|         if (self.g_name_owner) { | ||||
|             self.GetCurrentSessionRemote(function([session]) { | ||||
|                 self._ckSession = new ConsoleKitSessionProxy(Gio.DBus.system, 'org.freedesktop.ConsoleKit', session); | ||||
| 
 | ||||
|                 self._ckSession.connectSignal('ActiveChanged', function(object, senderName, [isActive]) { | ||||
|                     self.sessionActive = isActive; | ||||
|                 }); | ||||
|                 self._ckSession.IsActiveRemote(function([isActive]) { | ||||
|                     self.sessionActive = isActive; | ||||
|                 }); | ||||
|             }); | ||||
|         } else { | ||||
|             self.sessionActive = true; | ||||
|         } | ||||
|     }; | ||||
|     self.connect('notify::g-name-owner', | ||||
|                  Lang.bind(self, self._updateSessionActive)); | ||||
| 
 | ||||
|     self._updateSessionActive(); | ||||
|     self.init(null); | ||||
|     return self; | ||||
| } | ||||
| 
 | ||||
| function haveSystemd() { | ||||
|     return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0; | ||||
| } | ||||
| 
 | ||||
| const AutomountManager = new Lang.Class({ | ||||
|     Name: 'AutomountManager', | ||||
| 
 | ||||
| @@ -33,31 +88,30 @@ const AutomountManager = new Lang.Class({ | ||||
|                                     Lang.bind(this, this._InhibitorsChanged)); | ||||
|         this._inhibited = false; | ||||
| 
 | ||||
|         this._loginManager = LoginManager.getLoginManager(); | ||||
|         if (!haveSystemd()) | ||||
|             this.ckListener = new ConsoleKitManager(); | ||||
| 
 | ||||
|         Main.screenShield.connect('lock-status-changed', Lang.bind(this, this._lockStatusChanged)); | ||||
| 
 | ||||
|         this._volumeMonitor = Gio.VolumeMonitor.get(); | ||||
|     }, | ||||
| 
 | ||||
|     enable: function() { | ||||
|         this._volumeAddedId = this._volumeMonitor.connect('volume-added', Lang.bind(this, this._onVolumeAdded)); | ||||
|         this._volumeRemovedId = this._volumeMonitor.connect('volume-removed', Lang.bind(this, this._onVolumeRemoved)); | ||||
|         this._driveConnectedId = this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._onDriveConnected)); | ||||
|         this._driveDisconnectedId = this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._onDriveDisconnected)); | ||||
|         this._driveEjectButtonId = this._volumeMonitor.connect('drive-eject-button', Lang.bind(this, this._onDriveEjectButton)); | ||||
|         this._volumeMonitor.connect('volume-added', | ||||
|                                     Lang.bind(this, | ||||
|                                               this._onVolumeAdded)); | ||||
|         this._volumeMonitor.connect('volume-removed', | ||||
|                                     Lang.bind(this, | ||||
|                                               this._onVolumeRemoved)); | ||||
|         this._volumeMonitor.connect('drive-connected', | ||||
|                                     Lang.bind(this, | ||||
|                                               this._onDriveConnected)); | ||||
|         this._volumeMonitor.connect('drive-disconnected', | ||||
|                                     Lang.bind(this, | ||||
|                                               this._onDriveDisconnected)); | ||||
|         this._volumeMonitor.connect('drive-eject-button', | ||||
|                                     Lang.bind(this, | ||||
|                                               this._onDriveEjectButton)); | ||||
| 
 | ||||
|         this._mountAllId = Mainloop.idle_add(Lang.bind(this, this._startupMountAll)); | ||||
|     }, | ||||
| 
 | ||||
|     disable: function() { | ||||
|         this._volumeMonitor.disconnect(this._volumeAddedId); | ||||
|         this._volumeMonitor.disconnect(this._volumeRemovedId); | ||||
|         this._volumeMonitor.disconnect(this._driveConnectedId); | ||||
|         this._volumeMonitor.disconnect(this._driveDisconnectedId); | ||||
|         this._volumeMonitor.disconnect(this._driveEjectButtonId); | ||||
| 
 | ||||
|         if (this._mountAllId > 0) { | ||||
|             Mainloop.source_remove(this._mountAllId); | ||||
|             this._mountAllId = 0; | ||||
|         } | ||||
|         Mainloop.idle_add(Lang.bind(this, this._startupMountAll)); | ||||
|     }, | ||||
| 
 | ||||
|     _InhibitorsChanged: function(object, senderName, [inhibtor]) { | ||||
| @@ -70,6 +124,17 @@ const AutomountManager = new Lang.Class({ | ||||
|                 })); | ||||
|     }, | ||||
| 
 | ||||
|     _lockStatusChanged: function(shield, locked) { | ||||
|         if (!locked) { | ||||
|             this._volumeQueue.forEach(Lang.bind(this, function(volume) { | ||||
|                 this._checkAndMountVolume(volume); | ||||
|             })); | ||||
|         } | ||||
| 
 | ||||
|         // clear the queue anyway
 | ||||
|         this._volumeQueue = []; | ||||
|     }, | ||||
| 
 | ||||
|     _startupMountAll: function() { | ||||
|         let volumes = this._volumeMonitor.get_volumes(); | ||||
|         volumes.forEach(Lang.bind(this, function(volume) { | ||||
| @@ -78,14 +143,27 @@ const AutomountManager = new Lang.Class({ | ||||
|                                                 allowAutorun: false }); | ||||
|         })); | ||||
| 
 | ||||
|         this._mountAllId = 0; | ||||
|         return false; | ||||
|     }, | ||||
| 
 | ||||
|     isSessionActive: function() { | ||||
|         // Return whether the current session is active, using the
 | ||||
|         // right mechanism: either systemd if available or ConsoleKit
 | ||||
|         // as fallback.
 | ||||
| 
 | ||||
|         if (haveSystemd()) | ||||
|             return Shell.session_is_active_for_systemd(); | ||||
| 
 | ||||
|         return this.ckListener.sessionActive; | ||||
|     }, | ||||
| 
 | ||||
|     _onDriveConnected: function() { | ||||
|         // if we're not in the current ConsoleKit session,
 | ||||
|         // or screensaver is active, don't play sounds
 | ||||
|         if (!this._loginManager.sessionActive) | ||||
|         if (!this.isSessionActive()) | ||||
|             return; | ||||
| 
 | ||||
|         if (Main.screenShield.locked) | ||||
|             return; | ||||
| 
 | ||||
|         global.play_theme_sound(0, 'device-added-media'); | ||||
| @@ -94,7 +172,10 @@ const AutomountManager = new Lang.Class({ | ||||
|     _onDriveDisconnected: function() { | ||||
|         // if we're not in the current ConsoleKit session,
 | ||||
|         // or screensaver is active, don't play sounds
 | ||||
|         if (!this._loginManager.sessionActive) | ||||
|         if (!this.isSessionActive()) | ||||
|             return; | ||||
| 
 | ||||
|         if (Main.screenShield.locked) | ||||
|             return; | ||||
| 
 | ||||
|         global.play_theme_sound(0, 'device-removed-media');         | ||||
| @@ -103,7 +184,7 @@ const AutomountManager = new Lang.Class({ | ||||
|     _onDriveEjectButton: function(monitor, drive) { | ||||
|         // TODO: this code path is not tested, as the GVfs volume monitor
 | ||||
|         // doesn't emit this signal just yet.
 | ||||
|         if (!this._loginManager.sessionActive) | ||||
|         if (!this.isSessionActive()) | ||||
|             return; | ||||
| 
 | ||||
|         // we force stop/eject in this case, so we don't have to pass a
 | ||||
| @@ -143,8 +224,15 @@ const AutomountManager = new Lang.Class({ | ||||
|         if (params.checkSession) { | ||||
|             // if we're not in the current ConsoleKit session,
 | ||||
|             // don't attempt automount
 | ||||
|             if (!this._loginManager.sessionActive) | ||||
|             if (!this.isSessionActive()) | ||||
|                 return; | ||||
| 
 | ||||
|             if (Main.screenShield.locked) { | ||||
|                 if (this._volumeQueue.indexOf(volume) == -1) | ||||
|                     this._volumeQueue.push(volume); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (this._inhibited) | ||||
| @@ -238,4 +326,3 @@ const AutomountManager = new Lang.Class({ | ||||
|         }); | ||||
|     } | ||||
| }); | ||||
| const Component = AutomountManager; | ||||
| @@ -4,7 +4,6 @@ const Lang = imports.lang; | ||||
| const Gio = imports.gi.Gio; | ||||
| const St = imports.gi.St; | ||||
| 
 | ||||
| const LoginManager = imports.misc.loginManager; | ||||
| const Main = imports.ui.main; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const ShellMountOperation = imports.ui.shellMountOperation; | ||||
| @@ -44,17 +43,6 @@ function isMountRootHidden(root) { | ||||
|     return (path.indexOf('/.') != -1); | ||||
| } | ||||
| 
 | ||||
| function isMountNonLocal(mount) { | ||||
|     // If the mount doesn't have an associated volume, that means it's
 | ||||
|     // an uninteresting filesystem. Most devices that we care about will
 | ||||
|     // have a mount, like media players and USB sticks.
 | ||||
|     let volume = mount.get_volume(); | ||||
|     if (volume == null) | ||||
|         return true; | ||||
| 
 | ||||
|     return (volume.get_identifier("class") == "network"); | ||||
| } | ||||
| 
 | ||||
| function startAppForMount(app, mount) { | ||||
|     let files = []; | ||||
|     let root = mount.get_root(); | ||||
| @@ -94,21 +82,13 @@ const ContentTypeDiscoverer = new Lang.Class({ | ||||
| 
 | ||||
|     _init: function(callback) { | ||||
|         this._callback = callback; | ||||
|         this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA }); | ||||
|     }, | ||||
| 
 | ||||
|     guessContentTypes: function(mount) { | ||||
|         let autorunEnabled = !this._settings.get_boolean(SETTING_DISABLE_AUTORUN); | ||||
|         let shouldScan = autorunEnabled && !isMountNonLocal(mount); | ||||
| 
 | ||||
|         if (shouldScan) { | ||||
|             // guess mount's content types using GIO
 | ||||
|             mount.guess_content_type(false, null, | ||||
|                                      Lang.bind(this, | ||||
|                                                this._onContentTypeGuessed)); | ||||
|         } else { | ||||
|             this._emitCallback(mount, []); | ||||
|         } | ||||
|         // guess mount's content types using GIO
 | ||||
|         mount.guess_content_type(false, null, | ||||
|                                  Lang.bind(this, | ||||
|                                            this._onContentTypeGuessed)); | ||||
|     }, | ||||
| 
 | ||||
|     _onContentTypeGuessed: function(mount, res) { | ||||
| @@ -162,69 +142,55 @@ const AutorunManager = new Lang.Class({ | ||||
|     Name: 'AutorunManager', | ||||
| 
 | ||||
|     _init: function() { | ||||
|         this._loginManager = LoginManager.getLoginManager(); | ||||
| 
 | ||||
|         this._volumeMonitor = Gio.VolumeMonitor.get(); | ||||
| 
 | ||||
|         this._transDispatcher = new AutorunTransientDispatcher(this); | ||||
|     }, | ||||
|         this._volumeMonitor.connect('mount-added', | ||||
|                                     Lang.bind(this, | ||||
|                                               this._onMountAdded)); | ||||
|         this._volumeMonitor.connect('mount-removed', | ||||
|                                     Lang.bind(this, | ||||
|                                               this._onMountRemoved)); | ||||
| 
 | ||||
|     _ensureResidentSource: function() { | ||||
|         if (this._residentSource) | ||||
|             return; | ||||
|         this._transDispatcher = new AutorunTransientDispatcher(); | ||||
|         this._createResidentSource(); | ||||
| 
 | ||||
|         this._residentSource = new AutorunResidentSource(this); | ||||
|         let destroyId = this._residentSource.connect('destroy', Lang.bind(this, function() { | ||||
|             this._residentSource.disconnect(destroyId); | ||||
|             this._residentSource = null; | ||||
|         })); | ||||
|     }, | ||||
| 
 | ||||
|     enable: function() { | ||||
|         this._scanMounts(); | ||||
| 
 | ||||
|         this._mountAddedId = this._volumeMonitor.connect('mount-added', Lang.bind(this, this._onMountAdded)); | ||||
|         this._mountRemovedId = this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._onMountRemoved)); | ||||
|     }, | ||||
| 
 | ||||
|     disable: function() { | ||||
|         if (this._residentSource) | ||||
|             this._residentSource.destroy(); | ||||
|         this._volumeMonitor.disconnect(this._mountAddedId); | ||||
|         this._volumeMonitor.disconnect(this._mountRemovedId); | ||||
|     }, | ||||
| 
 | ||||
|     _processMount: function(mount, hotplug) { | ||||
|         let discoverer = new ContentTypeDiscoverer(Lang.bind(this, function(mount, apps, contentTypes) { | ||||
|             this._ensureResidentSource(); | ||||
|             this._residentSource.addMount(mount, apps); | ||||
| 
 | ||||
|             if (hotplug) | ||||
|                 this._transDispatcher.addMount(mount, apps, contentTypes); | ||||
|         })); | ||||
|         discoverer.guessContentTypes(mount); | ||||
|     }, | ||||
| 
 | ||||
|     _scanMounts: function() { | ||||
|         let mounts = this._volumeMonitor.get_mounts(); | ||||
|         mounts.forEach(Lang.bind(this, function(mount) { | ||||
|             this._processMount(mount, false); | ||||
| 
 | ||||
|         mounts.forEach(Lang.bind(this, function (mount) { | ||||
|             let discoverer = new ContentTypeDiscoverer(Lang.bind (this,  | ||||
|                 function (mount, apps) { | ||||
|                     this._residentSource.addMount(mount, apps); | ||||
|                 })); | ||||
| 
 | ||||
|             discoverer.guessContentTypes(mount); | ||||
|         })); | ||||
|     }, | ||||
| 
 | ||||
|     _createResidentSource: function() { | ||||
|         this._residentSource = new AutorunResidentSource(); | ||||
|         this._residentSource.connect('destroy', | ||||
|                                      Lang.bind(this, | ||||
|                                                this._createResidentSource)); | ||||
|     }, | ||||
| 
 | ||||
|     _onMountAdded: function(monitor, mount) { | ||||
|         // don't do anything if our session is not the currently
 | ||||
|         // active one
 | ||||
|         if (!this._loginManager.sessionActive) | ||||
|         if (!Main.automountManager.isSessionActive()) | ||||
|             return; | ||||
| 
 | ||||
|         this._processMount(mount, true); | ||||
|         let discoverer = new ContentTypeDiscoverer(Lang.bind (this, | ||||
|             function (mount, apps, contentTypes) { | ||||
|                 this._transDispatcher.addMount(mount, apps, contentTypes); | ||||
|                 this._residentSource.addMount(mount, apps); | ||||
|             })); | ||||
| 
 | ||||
|         discoverer.guessContentTypes(mount); | ||||
|     }, | ||||
| 
 | ||||
|     _onMountRemoved: function(monitor, mount) { | ||||
|         this._transDispatcher.removeMount(mount); | ||||
|         if (this._residentSource) | ||||
|             this._residentSource.removeMount(mount); | ||||
|         this._residentSource.removeMount(mount); | ||||
|     }, | ||||
| 
 | ||||
|     ejectMount: function(mount) { | ||||
| @@ -291,18 +257,12 @@ const AutorunResidentSource = new Lang.Class({ | ||||
|     Name: 'AutorunResidentSource', | ||||
|     Extends: MessageTray.Source, | ||||
| 
 | ||||
|     _init: function(manager) { | ||||
|         this.parent(_("Removable Devices"), 'media-removable'); | ||||
|         this.showInLockScreen = false; | ||||
|     _init: function() { | ||||
|         this.parent(_("Removable Devices"), 'media-removable', St.IconType.FULLCOLOR); | ||||
| 
 | ||||
|         this._mounts = []; | ||||
| 
 | ||||
|         this._manager = manager; | ||||
|         this._notification = new AutorunResidentNotification(this._manager, this); | ||||
|     }, | ||||
| 
 | ||||
|     buildRightClickMenu: function() { | ||||
|         return null; | ||||
|         this._notification = new AutorunResidentNotification(this); | ||||
|     }, | ||||
| 
 | ||||
|     addMount: function(mount, apps) { | ||||
| @@ -352,7 +312,7 @@ const AutorunResidentNotification = new Lang.Class({ | ||||
|     Name: 'AutorunResidentNotification', | ||||
|     Extends: MessageTray.Notification, | ||||
| 
 | ||||
|     _init: function(manager, source) { | ||||
|     _init: function(source) { | ||||
|         this.parent(source, source.title, null, { customContent: true }); | ||||
| 
 | ||||
|         // set the notification as resident
 | ||||
| @@ -360,7 +320,6 @@ const AutorunResidentNotification = new Lang.Class({ | ||||
| 
 | ||||
|         this._layout = new St.BoxLayout ({ style_class: 'hotplug-resident-box', | ||||
|                                            vertical: true }); | ||||
|         this._manager = manager; | ||||
| 
 | ||||
|         this.addActor(this._layout, | ||||
|                       { x_expand: true, | ||||
| @@ -408,7 +367,7 @@ const AutorunResidentNotification = new Lang.Class({ | ||||
|                                 expand: true }); | ||||
| 
 | ||||
|         let ejectIcon =  | ||||
|             new St.Icon({ icon_name: 'media-eject-symbolic', | ||||
|             new St.Icon({ icon_name: 'media-eject', | ||||
|                           style_class: 'hotplug-resident-eject-icon' }); | ||||
| 
 | ||||
|         let ejectButton = | ||||
| @@ -423,7 +382,7 @@ const AutorunResidentNotification = new Lang.Class({ | ||||
|         })); | ||||
| 
 | ||||
|         ejectButton.connect('clicked', Lang.bind(this, function() { | ||||
|             this._manager.ejectMount(mount); | ||||
|             Main.autorunManager.ejectMount(mount); | ||||
|         })); | ||||
| 
 | ||||
|         return item; | ||||
| @@ -433,8 +392,7 @@ const AutorunResidentNotification = new Lang.Class({ | ||||
| const AutorunTransientDispatcher = new Lang.Class({ | ||||
|     Name: 'AutorunTransientDispatcher', | ||||
| 
 | ||||
|     _init: function(manager) { | ||||
|         this._manager = manager; | ||||
|     _init: function() { | ||||
|         this._sources = []; | ||||
|         this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA }); | ||||
|     }, | ||||
| @@ -477,7 +435,7 @@ const AutorunTransientDispatcher = new Lang.Class({ | ||||
|             return; | ||||
|       | ||||
|         // add a new source
 | ||||
|         this._sources.push(new AutorunTransientSource(this._manager, mount, apps)); | ||||
|         this._sources.push(new AutorunTransientSource(mount, apps)); | ||||
|     }, | ||||
| 
 | ||||
|     addMount: function(mount, apps, contentTypes) { | ||||
| @@ -530,22 +488,22 @@ const AutorunTransientSource = new Lang.Class({ | ||||
|     Name: 'AutorunTransientSource', | ||||
|     Extends: MessageTray.Source, | ||||
| 
 | ||||
|     _init: function(manager, mount, apps) { | ||||
|         this._manager = manager; | ||||
|     _init: function(mount, apps) { | ||||
|         this.mount = mount; | ||||
|         this.apps = apps; | ||||
| 
 | ||||
|         this.parent(mount.get_name()); | ||||
| 
 | ||||
|         this._notification = new AutorunTransientNotification(this._manager, this); | ||||
|         this._notification = new AutorunTransientNotification(this); | ||||
| 
 | ||||
|         // add ourselves as a source, and popup the notification
 | ||||
|         Main.messageTray.add(this); | ||||
|         this.notify(this._notification); | ||||
|     }, | ||||
| 
 | ||||
|     getIcon: function() { | ||||
|         return this.mount.get_icon(); | ||||
|     createIcon: function(size) { | ||||
|         return new St.Icon({ gicon: this.mount.get_icon(), | ||||
|                              icon_size: size }); | ||||
|     } | ||||
| }); | ||||
| 
 | ||||
| @@ -553,10 +511,9 @@ const AutorunTransientNotification = new Lang.Class({ | ||||
|     Name: 'AutorunTransientNotification', | ||||
|     Extends: MessageTray.Notification, | ||||
| 
 | ||||
|     _init: function(manager, source) { | ||||
|     _init: function(source) { | ||||
|         this.parent(source, source.title, null, { customContent: true }); | ||||
| 
 | ||||
|         this._manager = manager; | ||||
|         this._box = new St.BoxLayout({ style_class: 'hotplug-transient-box', | ||||
|                                        vertical: true }); | ||||
|         this.addActor(this._box); | ||||
| @@ -608,7 +565,7 @@ const AutorunTransientNotification = new Lang.Class({ | ||||
| 
 | ||||
|     _buttonForEject: function() { | ||||
|         let box = new St.BoxLayout(); | ||||
|         let icon = new St.Icon({ icon_name: 'media-eject-symbolic', | ||||
|         let icon = new St.Icon({ icon_name: 'media-eject', | ||||
|                                  style_class: 'hotplug-notification-item-icon' }); | ||||
|         box.add(icon); | ||||
| 
 | ||||
| @@ -625,11 +582,10 @@ const AutorunTransientNotification = new Lang.Class({ | ||||
|                                      style_class: 'hotplug-notification-item' }); | ||||
| 
 | ||||
|         button.connect('clicked', Lang.bind(this, function() { | ||||
|             this._manager.ejectMount(this._mount); | ||||
|             Main.autorunManager.ejectMount(this._mount); | ||||
|         })); | ||||
| 
 | ||||
|         return button; | ||||
|     } | ||||
| }); | ||||
| 
 | ||||
| const Component = AutorunManager; | ||||
| @@ -141,7 +141,6 @@ const BoxPointer = new Lang.Class({ | ||||
|  | ||||
|         this._muteInput(); | ||||
|  | ||||
|         Tweener.removeTweens(this); | ||||
|         Tweener.addTween(this, { opacity: fade ? 0 : 255, | ||||
|                                  xOffset: xOffset, | ||||
|                                  yOffset: yOffset, | ||||
| @@ -275,51 +274,14 @@ const BoxPointer = new Lang.Class({ | ||||
|         let [x1, y1] = [halfBorder, halfBorder]; | ||||
|         let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder]; | ||||
|  | ||||
|         let skipTopLeft = false; | ||||
|         let skipTopRight = false; | ||||
|         let skipBottomLeft = false; | ||||
|         let skipBottomRight = false; | ||||
|  | ||||
|         switch (this._arrowSide) { | ||||
|         case St.Side.TOP: | ||||
|             if (this._arrowOrigin == x1) | ||||
|                 skipTopLeft = true; | ||||
|             else if (this._arrowOrigin == x2) | ||||
|                 skipTopRight = true; | ||||
|             break; | ||||
|  | ||||
|         case St.Side.RIGHT: | ||||
|             if (this._arrowOrigin == y1) | ||||
|                 skipTopRight = true; | ||||
|             else if (this._arrowOrigin == y2) | ||||
|                 skipBottomRight = true; | ||||
|             break; | ||||
|  | ||||
|         case St.Side.BOTTOM: | ||||
|             if (this._arrowOrigin == x1) | ||||
|                 skipBottomLeft = true; | ||||
|             else if (this._arrowOrigin == x2) | ||||
|                 skipBottomRight = true; | ||||
|             break; | ||||
|  | ||||
|         case St.Side.LEFT: | ||||
|             if (this._arrowOrigin == y1) | ||||
|                 skipTopLeft = true; | ||||
|             else if (this._arrowOrigin == y2) | ||||
|                 skipBottomLeft = true; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         cr.moveTo(x1 + borderRadius, y1); | ||||
|         if (this._arrowSide == St.Side.TOP) { | ||||
|             if (skipTopLeft) { | ||||
|                 cr.moveTo(x1, y2 - borderRadius); | ||||
|                 cr.lineTo(x1, y1 - rise); | ||||
|                 cr.lineTo(x1 + halfBase, y1); | ||||
|             } else if (skipTopRight) { | ||||
|                 cr.lineTo(x2 - halfBase, y1); | ||||
|                 cr.lineTo(x2, y1 - rise); | ||||
|                 cr.lineTo(x2, y1 + borderRadius); | ||||
|             if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) { | ||||
|                 cr.lineTo(this._arrowOrigin, y1 - rise); | ||||
|                 cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y1); | ||||
|             } else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) { | ||||
|                 cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y1); | ||||
|                 cr.lineTo(this._arrowOrigin, y1 - rise); | ||||
|             } else { | ||||
|                 cr.lineTo(this._arrowOrigin - halfBase, y1); | ||||
|                 cr.lineTo(this._arrowOrigin, y1 - rise); | ||||
| @@ -327,20 +289,19 @@ const BoxPointer = new Lang.Class({ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (!skipTopRight) { | ||||
|             cr.lineTo(x2 - borderRadius, y1); | ||||
|             cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius, | ||||
|                    3*Math.PI/2, Math.PI*2); | ||||
|         } | ||||
|         cr.lineTo(x2 - borderRadius, y1); | ||||
|  | ||||
|         // top-right corner | ||||
|         cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius, | ||||
|                3*Math.PI/2, Math.PI*2); | ||||
|  | ||||
|         if (this._arrowSide == St.Side.RIGHT) { | ||||
|             if (skipTopRight) { | ||||
|                 cr.lineTo(x2 + rise, y1); | ||||
|                 cr.lineTo(x2 + rise, y1 + halfBase); | ||||
|             } else if (skipBottomRight) { | ||||
|                 cr.lineTo(x2, y2 - halfBase); | ||||
|                 cr.lineTo(x2 + rise, y2); | ||||
|                 cr.lineTo(x2 - borderRadius, y2); | ||||
|             if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) { | ||||
|                 cr.lineTo(x2 + rise, this._arrowOrigin); | ||||
|                 cr.lineTo(x2, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase); | ||||
|             } else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) { | ||||
|                 cr.lineTo(x2, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase); | ||||
|                 cr.lineTo(x2 + rise, this._arrowOrigin); | ||||
|             } else { | ||||
|                 cr.lineTo(x2, this._arrowOrigin - halfBase); | ||||
|                 cr.lineTo(x2 + rise, this._arrowOrigin); | ||||
| @@ -348,20 +309,19 @@ const BoxPointer = new Lang.Class({ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (!skipBottomRight) { | ||||
|             cr.lineTo(x2, y2 - borderRadius); | ||||
|             cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius, | ||||
|                    0, Math.PI/2); | ||||
|         } | ||||
|         cr.lineTo(x2, y2 - borderRadius); | ||||
|  | ||||
|         // bottom-right corner | ||||
|         cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius, | ||||
|                0, Math.PI/2); | ||||
|  | ||||
|         if (this._arrowSide == St.Side.BOTTOM) { | ||||
|             if (skipBottomLeft) { | ||||
|                 cr.lineTo(x1 + halfBase, y2); | ||||
|                 cr.lineTo(x1, y2 + rise); | ||||
|                 cr.lineTo(x1, y2 - borderRadius); | ||||
|             } else if (skipBottomRight) { | ||||
|                 cr.lineTo(x2, y2 + rise); | ||||
|                 cr.lineTo(x2 - halfBase, y2); | ||||
|             if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) { | ||||
|                 cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y2); | ||||
|                 cr.lineTo(this._arrowOrigin, y2 + rise); | ||||
|             } else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) { | ||||
|                 cr.lineTo(this._arrowOrigin, y2 + rise); | ||||
|                 cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y2); | ||||
|             } else { | ||||
|                 cr.lineTo(this._arrowOrigin + halfBase, y2); | ||||
|                 cr.lineTo(this._arrowOrigin, y2 + rise); | ||||
| @@ -369,20 +329,19 @@ const BoxPointer = new Lang.Class({ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (!skipBottomLeft) { | ||||
|             cr.lineTo(x1 + borderRadius, y2); | ||||
|             cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius, | ||||
|                    Math.PI/2, Math.PI); | ||||
|         } | ||||
|         cr.lineTo(x1 + borderRadius, y2); | ||||
|  | ||||
|         // bottom-left corner | ||||
|         cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius, | ||||
|                Math.PI/2, Math.PI); | ||||
|  | ||||
|         if (this._arrowSide == St.Side.LEFT) { | ||||
|             if (skipTopLeft) { | ||||
|                 cr.lineTo(x1, y1 + halfBase); | ||||
|                 cr.lineTo(x1 - rise, y1); | ||||
|                 cr.lineTo(x1 + borderRadius, y1); | ||||
|             } else if (skipBottomLeft) { | ||||
|                 cr.lineTo(x1 - rise, y2) | ||||
|                 cr.lineTo(x1 - rise, y2 - halfBase); | ||||
|             if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) { | ||||
|                 cr.lineTo(x1, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase); | ||||
|                 cr.lineTo(x1 - rise, this._arrowOrigin); | ||||
|             } else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) { | ||||
|                 cr.lineTo(x1 - rise, this._arrowOrigin); | ||||
|                 cr.lineTo(x1, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase); | ||||
|             } else { | ||||
|                 cr.lineTo(x1, this._arrowOrigin + halfBase); | ||||
|                 cr.lineTo(x1 - rise, this._arrowOrigin); | ||||
| @@ -390,11 +349,11 @@ const BoxPointer = new Lang.Class({ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (!skipTopLeft) { | ||||
|             cr.lineTo(x1, y1 + borderRadius); | ||||
|             cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius, | ||||
|                    Math.PI, 3*Math.PI/2); | ||||
|         } | ||||
|         cr.lineTo(x1, y1 + borderRadius); | ||||
|  | ||||
|         // top-left corner | ||||
|         cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius, | ||||
|                Math.PI, 3*Math.PI/2); | ||||
|  | ||||
|         Clutter.cairo_set_source_color(cr, backgroundColor); | ||||
|         cr.fillPreserve(); | ||||
| @@ -447,9 +406,10 @@ const BoxPointer = new Lang.Class({ | ||||
|         let arrowBase = themeNode.get_length('-arrow-base'); | ||||
|         let borderRadius = themeNode.get_length('-arrow-border-radius'); | ||||
|         let margin = (4 * borderRadius + borderWidth + arrowBase); | ||||
|         let halfMargin = margin / 2; | ||||
|  | ||||
|         let themeNode = this.actor.get_theme_node(); | ||||
|         let gap = themeNode.get_length('-boxpointer-gap'); | ||||
|         let padding = themeNode.get_length('-arrow-rise'); | ||||
|  | ||||
|         let resX, resY; | ||||
|  | ||||
| @@ -468,66 +428,29 @@ const BoxPointer = new Lang.Class({ | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         // Now align and position the pointing axis, making sure it fits on | ||||
|         // screen. If the arrowOrigin is so close to the edge that the arrow | ||||
|         // will not be isosceles, we try to compensate as follows: | ||||
|         //   - We skip the rounded corner and settle for a right angled arrow | ||||
|         //     as shown below. See _drawBorder for further details. | ||||
|         //     |\_____ | ||||
|         //     | | ||||
|         //     | | ||||
|         //   - If the arrow was going to be acute angled, we move the position | ||||
|         //     of the box to maintain the arrow's accuracy. | ||||
|  | ||||
|         let arrowOrigin; | ||||
|         let halfBase = Math.floor(arrowBase/2); | ||||
|         let halfBorder = borderWidth / 2; | ||||
|         let halfMargin = margin / 2; | ||||
|         let [x1, y1] = [halfBorder, halfBorder]; | ||||
|         let [x2, y2] = [natWidth - halfBorder, natHeight - halfBorder]; | ||||
|  | ||||
|         // Now align and position the pointing axis, making sure | ||||
|         // it fits on screen | ||||
|         switch (this._arrowSide) { | ||||
|         case St.Side.TOP: | ||||
|         case St.Side.BOTTOM: | ||||
|             resX = sourceCenterX - (halfMargin + (natWidth - margin) * alignment); | ||||
|  | ||||
|             resX = Math.max(resX, monitor.x + padding); | ||||
|             resX = Math.min(resX, monitor.x + monitor.width - (padding + natWidth)); | ||||
|  | ||||
|             arrowOrigin = sourceCenterX - resX; | ||||
|             if (arrowOrigin <= (x1 + (borderRadius + halfBase))) { | ||||
|                 if (arrowOrigin > x1) | ||||
|                     resX += (arrowOrigin - x1); | ||||
|                 arrowOrigin = x1; | ||||
|             } else if (arrowOrigin >= (x2 - (borderRadius + halfBase))) { | ||||
|                 if (arrowOrigin < x2) | ||||
|                     resX -= (x2 - arrowOrigin); | ||||
|                 arrowOrigin = x2; | ||||
|             } | ||||
|             resX = Math.max(resX, monitor.x + 10); | ||||
|             resX = Math.min(resX, monitor.x + monitor.width - (10 + natWidth)); | ||||
|             this.setArrowOrigin(sourceCenterX - resX); | ||||
|             break; | ||||
|  | ||||
|         case St.Side.LEFT: | ||||
|         case St.Side.RIGHT: | ||||
|             resY = sourceCenterY - (halfMargin + (natHeight - margin) * alignment); | ||||
|  | ||||
|             resY = Math.max(resY, monitor.y + padding); | ||||
|             resY = Math.min(resY, monitor.y + monitor.height - (padding + natHeight)); | ||||
|             resY = Math.max(resY, monitor.y + 10); | ||||
|             resY = Math.min(resY, monitor.y + monitor.height - (10 + natHeight)); | ||||
|  | ||||
|             arrowOrigin = sourceCenterY - resY; | ||||
|             if (arrowOrigin <= (y1 + (borderRadius + halfBase))) { | ||||
|                 if (arrowOrigin > y1) | ||||
|                     resY += (arrowOrigin - y1); | ||||
|                 arrowOrigin = y1; | ||||
|             } else if (arrowOrigin >= (y2 - (borderRadius + halfBase))) { | ||||
|                 if (arrowOrigin < y2) | ||||
|                     resX -= (y2 - arrowOrigin); | ||||
|                 arrowOrigin = y2; | ||||
|             } | ||||
|             this.setArrowOrigin(sourceCenterY - resY); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         this.setArrowOrigin(arrowOrigin); | ||||
|  | ||||
|         let parent = this.actor.get_parent(); | ||||
|         let success, x, y; | ||||
|         while (!success) { | ||||
|   | ||||
| @@ -204,11 +204,12 @@ const CalendarServerInfo  = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIfac | ||||
|  | ||||
| function CalendarServer() { | ||||
|     var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session, | ||||
|                                    g_interface_name: CalendarServerInfo.name, | ||||
|                                    g_interface_info: CalendarServerInfo, | ||||
|                                    g_name: 'org.gnome.Shell.CalendarServer', | ||||
|                                    g_object_path: '/org/gnome/Shell/CalendarServer', | ||||
|                                    g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES }); | ||||
| 				   g_interface_name: CalendarServerInfo.name, | ||||
| 				   g_interface_info: CalendarServerInfo, | ||||
| 				   g_name: 'org.gnome.Shell.CalendarServer', | ||||
| 				   g_object_path: '/org/gnome/Shell/CalendarServer', | ||||
|                                    g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START | | ||||
|                                              Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) }); | ||||
|  | ||||
|     self.init(null); | ||||
|     return self; | ||||
| @@ -270,9 +271,8 @@ const DBusEventSource = new Lang.Class({ | ||||
|         this._loadEvents(false); | ||||
|     }, | ||||
|  | ||||
|     _onEventsReceived: function(results, error) { | ||||
|     _onEventsReceived: function([appointments]) { | ||||
|         let newEvents = []; | ||||
|         let appointments = results ? results[0] : null; | ||||
|         if (appointments != null) { | ||||
|             for (let n = 0; n < appointments.length; n++) { | ||||
|                 let a = appointments[n]; | ||||
| @@ -340,10 +340,22 @@ const DBusEventSource = new Lang.Class({ | ||||
| }); | ||||
| Signals.addSignalMethods(DBusEventSource.prototype); | ||||
|  | ||||
| // Calendar: | ||||
| // @eventSource: is an object implementing the EventSource API, e.g. the | ||||
| // requestRange(), getEvents(), hasEvents() methods and the ::changed signal. | ||||
| const Calendar = new Lang.Class({ | ||||
|     Name: 'Calendar', | ||||
|  | ||||
|     _init: function() { | ||||
|     _init: function(eventSource) { | ||||
|         if (eventSource) { | ||||
|             this._eventSource = eventSource; | ||||
|  | ||||
|             this._eventSource.connect('changed', Lang.bind(this, | ||||
|                                                            function() { | ||||
|                                                                this._update(false); | ||||
|                                                            })); | ||||
|         } | ||||
|  | ||||
|         this._weekStart = Shell.util_get_week_start(); | ||||
|         this._weekdate = NaN; | ||||
|         this._digitWidth = NaN; | ||||
| @@ -380,24 +392,6 @@ const Calendar = new Lang.Class({ | ||||
|         this._buildHeader (); | ||||
|     }, | ||||
|  | ||||
|     // @eventSource: is an object implementing the EventSource API, e.g. the | ||||
|     // requestRange(), getEvents(), hasEvents() methods and the ::changed signal. | ||||
|     setEventSource: function(eventSource) { | ||||
|         if (this._eventSource) { | ||||
|             this._eventSource.disconnect(this._eventSourceChangedId); | ||||
|             this._eventSource = null; | ||||
|         } | ||||
|  | ||||
|         this._eventSource = eventSource; | ||||
|  | ||||
|         if (this._eventSource) { | ||||
|             this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, function() { | ||||
|                 this._update(false); | ||||
|             })); | ||||
|             this._update(true); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     // Sets the calendar to show a specific date | ||||
|     setDate: function(date, forceReload) { | ||||
|         if (!_sameDay(date, this._selectedDate)) { | ||||
| @@ -628,25 +622,16 @@ Signals.addSignalMethods(Calendar.prototype); | ||||
| const EventsList = new Lang.Class({ | ||||
|     Name: 'EventsList', | ||||
|  | ||||
|     _init: function() { | ||||
|     _init: function(eventSource) { | ||||
|         this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'}); | ||||
|         this._date = new Date(); | ||||
|         this._eventSource = eventSource; | ||||
|         this._eventSource.connect('changed', Lang.bind(this, this._update)); | ||||
|         this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' }); | ||||
|         this._desktopSettings.connect('changed', Lang.bind(this, this._update)); | ||||
|         this._weekStart = Shell.util_get_week_start(); | ||||
|     }, | ||||
|  | ||||
|     setEventSource: function(eventSource) { | ||||
|         if (this._eventSource) { | ||||
|             this._eventSource.disconnect(this._eventSourceChangedId); | ||||
|             this._eventSource = null; | ||||
|         } | ||||
|  | ||||
|         this._eventSource = eventSource; | ||||
|  | ||||
|         if (this._eventSource) { | ||||
|             this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, this._update)); | ||||
|         } | ||||
|         this._update(); | ||||
|     }, | ||||
|  | ||||
|     _addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) { | ||||
| @@ -732,15 +717,13 @@ const EventsList = new Lang.Class({ | ||||
|         let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000); | ||||
|         this._addPeriod(_("Tomorrow"), tomorrowBegin, tomorrowEnd, false, true); | ||||
|  | ||||
|         let dayInWeek = (dayEnd.getDay() - this._weekStart + 7) % 7; | ||||
|  | ||||
|         if (dayInWeek < 5) { | ||||
|         if (dayEnd.getDay() <= 4 + this._weekStart) { | ||||
|             /* If now is within the first 5 days we show "This week" and | ||||
|              * include events up until and including Saturday/Sunday | ||||
|              * (depending on whether a week starts on Sunday/Monday). | ||||
|              */ | ||||
|             let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000); | ||||
|             let thisWeekEnd = new Date(dayEnd.getTime() + (6 - dayInWeek) * 86400 * 1000); | ||||
|             let thisWeekEnd = new Date(dayEnd.getTime() + (6 + this._weekStart - dayEnd.getDay()) * 86400 * 1000); | ||||
|             this._addPeriod(_("This week"), thisWeekBegin, thisWeekEnd, true, false); | ||||
|         } else { | ||||
|             /* otherwise it's one of the two last days of the week ... show | ||||
| @@ -748,7 +731,7 @@ const EventsList = new Lang.Class({ | ||||
|              * Saturday/Sunday | ||||
|              */ | ||||
|             let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000); | ||||
|             let nextWeekEnd = new Date(dayEnd.getTime() + (13 - dayInWeek) * 86400 * 1000); | ||||
|             let nextWeekEnd = new Date(dayEnd.getTime() + (13 + this._weekStart - dayEnd.getDay()) * 86400 * 1000); | ||||
|             this._addPeriod(_("Next week"), nextWeekBegin, nextWeekEnd, true, false); | ||||
|         } | ||||
|     }, | ||||
|   | ||||
| @@ -1,65 +0,0 @@ | ||||
|  | ||||
| const Lang = imports.lang; | ||||
| const Main = imports.ui.main; | ||||
|  | ||||
| const ComponentManager = new Lang.Class({ | ||||
|     Name: 'ComponentManager', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._allComponents = {}; | ||||
|         this._enabledComponents = []; | ||||
|  | ||||
|         Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); | ||||
|         this._sessionUpdated(); | ||||
|     }, | ||||
|  | ||||
|     _sessionUpdated: function() { | ||||
|         let newEnabledComponents = Main.sessionMode.components; | ||||
|  | ||||
|         newEnabledComponents.filter(Lang.bind(this, function(name) { | ||||
|             return this._enabledComponents.indexOf(name) == -1; | ||||
|         })).forEach(Lang.bind(this, function(name) { | ||||
|             this._enableComponent(name); | ||||
|         })); | ||||
|  | ||||
|         this._enabledComponents.filter(Lang.bind(this, function(name) { | ||||
|             return newEnabledComponents.indexOf(name) == -1; | ||||
|         })).forEach(Lang.bind(this, function(name) { | ||||
|             this._disableComponent(name); | ||||
|         })); | ||||
|  | ||||
|         this._enabledComponents = newEnabledComponents; | ||||
|     }, | ||||
|  | ||||
|     _importComponent: function(name) { | ||||
|         let module = imports.ui.components[name]; | ||||
|         return module.Component; | ||||
|     }, | ||||
|  | ||||
|     _ensureComponent: function(name) { | ||||
|         let component = this._allComponents[name]; | ||||
|         if (component) | ||||
|             return component; | ||||
|  | ||||
| 	if (Main.sessionMode.isLocked) | ||||
| 	    return null; | ||||
|  | ||||
|         let constructor = this._importComponent(name); | ||||
|         component = new constructor(); | ||||
|         this._allComponents[name] = component; | ||||
|         return component; | ||||
|     }, | ||||
|  | ||||
|     _enableComponent: function(name) { | ||||
|         let component = this._ensureComponent(name); | ||||
| 	if (component) | ||||
|             component.enable(); | ||||
|     }, | ||||
|  | ||||
|     _disableComponent: function(name) { | ||||
|         let component = this._allComponents[name]; | ||||
|         if (component == null) | ||||
|             return; | ||||
|         component.disable(); | ||||
|     } | ||||
| }); | ||||
| @@ -1,647 +0,0 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gdk = imports.gi.Gdk; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gio = imports.gi.Gio; | ||||
| const Lang = imports.lang; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const ShellMountOperation  = imports.ui.shellMountOperation; | ||||
| const Tweener = imports.ui.tweener; | ||||
| const Util = imports.misc.util; | ||||
|  | ||||
| const INTERFACE_SETTINGS = 'org.gnome.desktop.interface'; | ||||
| const POWER_SETTINGS = 'org.gnome.settings-daemon.plugins.power'; | ||||
| const XSETTINGS_SETTINGS = 'org.gnome.settings-daemon.plugins.xsettings'; | ||||
| const TOUCHPAD_SETTINGS = 'org.gnome.settings-daemon.peripherals.touchpad'; | ||||
| const KEYBINDING_SETTINGS = 'org.gnome.settings-daemon.plugins.media-keys'; | ||||
| const CUSTOM_KEYBINDING_SETTINGS = 'org.gnome.settings-daemon.plugins.media-keys.custom-keybinding'; | ||||
| const A11Y_SETTINGS = 'org.gnome.desktop.a11y.applications'; | ||||
| const MAGNIFIER_SETTINGS = 'org.gnome.desktop.a11y.magnifier'; | ||||
| const INPUT_SOURCE_SETTINGS = 'org.gnome.desktop.input-sources'; | ||||
|  | ||||
| const MediaKeysInterface = <interface name='org.gnome.SettingsDaemon.MediaKeys'> | ||||
| <method name='GrabMediaPlayerKeys'> | ||||
|     <arg name='application' direction='in' type='s'/> | ||||
|     <arg name='time' direction='in' type='u'/> | ||||
| </method> | ||||
| <method name='ReleaseMediaPlayerKeys'> | ||||
|     <arg name='application' direction='in' type='s'/> | ||||
| </method> | ||||
| <signal name='MediaPlayerKeyPressed'> | ||||
|     <arg name='application' type='s'/> | ||||
|     <arg name='key' type='s'/> | ||||
| </signal> | ||||
| </interface>; | ||||
|  | ||||
| /* [ actionName, setting, hardcodedKeysym, overviewOnly, args ] */ | ||||
| /* (overviewOnly means that the keybinding is handled when the shell is not | ||||
|    modal, or when the overview is active, but not when other modal operations | ||||
|    are active; otherwise the keybinding is always handled) */ | ||||
| const DEFAULT_KEYBINDINGS = [ | ||||
|     [ 'doTouchpadToggle', null, 'XF86TouchpadToggle', false ], | ||||
|     [ 'doTouchpadSet', null, 'XF86TouchpadOn', false, [ true ] ], | ||||
|     [ 'doTouchpadSet', null, 'XF86TouchpadOff', false, [ false ] ], | ||||
|     [ 'doMute', 'volume-mute', null, false, [ false ] ], | ||||
|     [ 'doVolumeAdjust', 'volume-down', null, false, [ Clutter.ScrollDirection.DOWN, false ] ], | ||||
|     [ 'doVolumeAdjust', 'volume-up', null, false, [ Clutter.ScrollDirection.UP, false ] ], | ||||
|     [ 'doMute', null, '<Alt>XF86AudioMute', false, [ true ] ], | ||||
|     [ 'doVolumeAdjust', null, '<Alt>XF86AudioLowerVolume', false, [ Clutter.ScrollDirection.DOWN, true ] ], | ||||
|     [ 'doVolumeAdjust', null, '<Alt>XF86AudioRaiseVolume', false, [ Clutter.ScrollDirection.UP, true ] ], | ||||
|     [ 'doLogout', 'logout', null, true ], | ||||
|     [ 'doEject', 'eject', null, false ], | ||||
|     [ 'doHome', 'home', null, true ], | ||||
|     [ 'doLaunchMimeHandler', 'media', null, true, [ 'application/x-vorbis+ogg' ] ], | ||||
|     [ 'doLaunchApp', 'calculator', null, true, [ 'gcalcltool.desktop' ] ], | ||||
|     [ 'doLaunchApp', 'search', null, true, [ 'tracker-needle.desktop' ] ], | ||||
|     [ 'doLaunchMimeHandler', 'email', null, true, [ 'x-scheme-handler/mailto' ] ], | ||||
|     [ 'doScreensaver', 'screensaver', null, true ], | ||||
|     [ 'doScreensaver', null, 'XF86ScreenSaver', true ], | ||||
|     [ 'doLaunchApp', 'help', null, true, [ 'yelp.desktop' ] ], | ||||
|     [ 'doSpawn', 'screenshot', null, true, [ ['gnome-screenshot'] ] ], | ||||
|     [ 'doSpawn', 'window-screenshot', null, true, [ ['gnome-screenshot', '--window'] ] ], | ||||
|     [ 'doSpawn', 'area-screenshot', null, true, [ ['gnome-screenshot', '--area'] ] ], | ||||
|     [ 'doSpawn', 'screenshot-clip', null, true, [ ['gnome-screenshot', '--clipboard'] ] ], | ||||
|     [ 'doSpawn', 'window-screenshot-clip', null, true, [ ['gnome-screenshot', '--window', '--clipboard'] ] ], | ||||
|     [ 'doSpawn', 'area-screenshot-clip', null, true, [ ['gnome-screenshot', '--area', '--clipboard'] ] ], | ||||
|     [ 'doLaunchMimeHandler', 'www', null, true, [ 'x-scheme-handler/http' ] ], | ||||
|     [ 'doMediaKey', 'play', null, true, [ 'Play' ] ], | ||||
|     [ 'doMediaKey', 'pause', null, true, [ 'Pause' ] ], | ||||
|     [ 'doMediaKey', 'stop', null, true, [ 'Stop' ] ], | ||||
|     [ 'doMediaKey', 'previous', null, true, [ 'Previous' ] ], | ||||
|     [ 'doMediaKey', 'next', null, true, [ 'Next' ] ], | ||||
|     [ 'doMediaKey', null, 'XF86AudioRewind', true, [ 'Rewind' ] ], | ||||
|     [ 'doMediaKey', null, 'XF86AudioForward', true, [ 'FastForward' ] ], | ||||
|     [ 'doMediaKey', null, 'XF86AudioRepeat', true, [ 'Repeat' ] ], | ||||
|     [ 'doMediaKey', null, 'XF86AudioRandomPlay', true, [ 'Shuffle' ] ], | ||||
|     [ 'doXRandRAction', null, '<Super>p', false, [ 'VideoModeSwitch' ] ], | ||||
|     /* Key code of the XF86Display key (Fn-F7 on Thinkpads, Fn-F4 on HP machines, etc.) */ | ||||
|     [ 'doXRandRAction', null, 'XF86Display', false, [ 'VideoModeSwitch' ] ], | ||||
|     /* Key code of the XF86RotateWindows key (present on some tablets) */ | ||||
|     [ 'doXRandRAction', null, 'XF86RotateWindows', false, [ 'Rotate' ] ], | ||||
|     [ 'doA11yAction', 'magnifier', null, true, [ 'screen-magnifier-enabled' ] ], | ||||
|     [ 'doA11yAction', 'screenreader', null, true, [ 'screen-reader-enabled' ] ], | ||||
|     [ 'doA11yAction', 'on-screen-keyboard', null, true, [ 'screen-keyboard-enabled' ] ], | ||||
|     [ 'doTextSize', 'increase-text-size', null, true, [ 1 ] ], | ||||
|     [ 'doTextSize', 'decrease-text-size', null, true, [ -1 ] ], | ||||
|     [ 'doToggleContrast', 'toggle-contrast', null, true ], | ||||
|     [ 'doMagnifierZoom', 'magnifier-zoom-in', null, true, [ 1 ] ], | ||||
|     [ 'doMagnifierZoom', 'magnifier-zoom-out', null, true, [ -1 ] ], | ||||
|     [ 'doPowerAction', null, 'XF86PowerOff', true, [ 'button-power' ] ], | ||||
|     /* the kernel / Xorg names really are like this... */ | ||||
|     [ 'doPowerAction', null, 'XF86Suspend', false, [ 'button-sleep' ] ], | ||||
|     [ 'doPowerAction', null, 'XF86Sleep', false, [ 'button-suspend' ] ], | ||||
|     [ 'doPowerAction', null, 'XF86Hibernate', false, [ 'button-hibernate' ] ], | ||||
|     [ 'doBrightness', null, 'XF86MonBrightnessUp', false, [ 'Screen', 'StepUp' ] ], | ||||
|     [ 'doBrightness', null, 'XF86MonBrightnessDown', false, [ 'Screen', 'StepDown' ] ], | ||||
|     [ 'doBrightness', null, 'XF86KbdBrightnessUp', false, [ 'Keyboard', 'StepUp' ] ], | ||||
|     [ 'doBrightness', null, 'XF86KbdBrightnessDown', false, [ 'Keyboard', 'StepDown' ] ], | ||||
|     [ 'doBrightnessToggle', null, 'XF86KbdLightOnOff', false, ], | ||||
|     [ 'doInputSource', 'switch-input-source', null, false, [ +1 ] ], | ||||
|     [ 'doInputSource', 'switch-input-source-backward', null, false, [ -1 ] ], | ||||
|     [ 'doLaunchApp', null, 'XF86Battery', true, [ 'gnome-power-statistics.desktop' ] ] | ||||
| ]; | ||||
|  | ||||
| var osdWin; | ||||
| const OSDWindow = new Lang.Class({ | ||||
|     Name: 'OSDWindow', | ||||
|  | ||||
|     FADE_TIMEOUT: 1500, | ||||
|     FADE_DURATION: 100, | ||||
|  | ||||
|     _init: function(iconName, value) { | ||||
|         /* assume 130x130 on a 640x480 display and scale from there */ | ||||
|         let monitor = Main.layoutManager.primaryMonitor; | ||||
|         let scalew = monitor.width / 640.0; | ||||
|         let scaleh = monitor.height / 480.0; | ||||
|         let scale = Math.min(scalew, scaleh); | ||||
|         let size = 130 * Math.max(1, scale); | ||||
|  | ||||
|         this.actor = new St.BoxLayout({ style_class: 'osd-window', | ||||
|                                         vertical: true, | ||||
|                                         reactive: false, | ||||
|                                         visible: false, | ||||
|                                         width: size, | ||||
|                                         height: size, | ||||
|                                       }); | ||||
|  | ||||
|         this._icon = new St.Icon({ icon_name: iconName, | ||||
|                                    icon_size: size / 2, | ||||
|                                  }); | ||||
|         this.actor.add(this._icon, { expand: true, | ||||
|                                      x_align: St.Align.MIDDLE, | ||||
|                                      y_align: St.Align.MIDDLE }); | ||||
|  | ||||
|         this._value = value; | ||||
|         this._progressBar = new St.DrawingArea({ style_class: 'osd-progress-bar' }); | ||||
|         this._progressBar.connect('repaint', Lang.bind(this, this._drawProgress)); | ||||
|         this.actor.add(this._progressBar, { expand: true, x_fill: true, y_fill: false }); | ||||
|         this._progressBar.visible = value !== undefined; | ||||
|  | ||||
|         Main.layoutManager.addChrome(this.actor); | ||||
|  | ||||
|         /* Position in the middle of primary monitor */ | ||||
|         let [width, height] = this.actor.get_size(); | ||||
|         this.actor.x = ((monitor.width - width) / 2) + monitor.x; | ||||
|         this.actor.y = monitor.y + (monitor.height / 2) + (monitor.height / 2 - height) / 2; | ||||
|     }, | ||||
|  | ||||
|     show: function() { | ||||
|         this.actor.show(); | ||||
|         Tweener.addTween(this.actor, | ||||
|                          { opacity: 255, | ||||
|                            time: this.FADE_DURATION / 1000, | ||||
|                            transition: 'easeInQuad' }); | ||||
|  | ||||
|         if (this._timeoutId) | ||||
|             GLib.source_remove(this._timeoutId); | ||||
|  | ||||
|         this._timeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, this.FADE_TIMEOUT, Lang.bind(this, this.hide)); | ||||
|     }, | ||||
|  | ||||
|     hide: function() { | ||||
|         Tweener.addTween(this.actor, | ||||
|                          { opacity: 0, | ||||
|                            time: this.FADE_DURATION / 1000, | ||||
|                            transition: 'easeOutQuad', | ||||
|                            onComplete: function() { | ||||
|                                this.actor.destroy(); | ||||
|                                this.actor = null; | ||||
|                                osdWin = null; | ||||
|                            }, | ||||
|                            onCompleteScope: this }); | ||||
|  | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     setIcon: function(name) { | ||||
|         this._icon.icon_name = name; | ||||
|     }, | ||||
|  | ||||
|     setValue: function(value) { | ||||
|         if (value == this._value) | ||||
|             return; | ||||
|  | ||||
|         this._value = value; | ||||
|         this._progressBar.visible = value !== undefined; | ||||
|         this._progressBar.queue_repaint(); | ||||
|     }, | ||||
|  | ||||
|     _drawProgress: function(area) { | ||||
|         let cr = area.get_context(); | ||||
|  | ||||
|         let themeNode = this.actor.get_theme_node(); | ||||
|         let color = themeNode.get_foreground_color(); | ||||
|         Clutter.cairo_set_source_color(cr, color); | ||||
|  | ||||
|         let [width, height] = area.get_surface_size(); | ||||
|         width = width * this._value; | ||||
|  | ||||
|         cr.moveTo(0,0); | ||||
|         cr.lineTo(width, 0); | ||||
|         cr.lineTo(width, height); | ||||
|         cr.lineTo(0, height); | ||||
|         cr.fill(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| function showOSD(icon, value) { | ||||
|     if (osdWin) { | ||||
|         osdWin.setIcon(icon); | ||||
|         osdWin.setValue(value); | ||||
|     } else { | ||||
|         osdWin = new OSDWindow(icon, value); | ||||
|     } | ||||
|  | ||||
|     osdWin.show(); | ||||
| } | ||||
|  | ||||
| const MediaKeysGrabber = new Lang.Class({ | ||||
|     Name: 'MediaKeysGrabber', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(MediaKeysInterface, this); | ||||
|         this._apps = []; | ||||
|     }, | ||||
|  | ||||
|     enable: function() { | ||||
|         this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SettingsDaemon/MediaKeys'); | ||||
|     }, | ||||
|  | ||||
|     disable: function() { | ||||
|         this._dbusImpl.unexport(); | ||||
|     }, | ||||
|  | ||||
|     GrabMediaPlayerKeysAsync: function(parameters, invocation) { | ||||
|         let [appName, time] = parameters; | ||||
|  | ||||
|         /* I'm not sure of this code, but it is in gnome-settings-daemon | ||||
|            (letting alone that the introspection is wrong in glib...) | ||||
|         */ | ||||
|         if (time == Gdk.CURRENT_TIME) { | ||||
|             let tv = new GLib.TimeVal; | ||||
|             GLib.get_current_time(tv); | ||||
|             time = tv.tv_sec * 1000 + tv.tv_usec / 1000; | ||||
|         } | ||||
|  | ||||
|         let pos = -1; | ||||
|         for (let i = 0; i < this._apps.length; i++) { | ||||
|             if (this._apps[i].appName == appName) { | ||||
|                 pos = i; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (pos != -1) | ||||
|             this._freeMediaPlayer(pos); | ||||
|  | ||||
|         let app = { | ||||
|             appName: appName, | ||||
|             name: invocation.get_sender(), | ||||
|             time: time, | ||||
|             watchId: Gio.DBus.session.watch_name(invocation.get_sender(), | ||||
|                                                  Gio.BusNameWatcherFlags.NONE, | ||||
|                                                  null, | ||||
|                                                  Lang.bind(this, this._onNameVanished)), | ||||
|         }; | ||||
|         Util.insertSorted(this._apps, app, function(a, b) { | ||||
|             return b.time-a.time; | ||||
|         }); | ||||
|  | ||||
|         invocation.return_value(GLib.Variant.new('()', [])); | ||||
|     }, | ||||
|  | ||||
|     ReleaseMediaPlayerAsync: function(parameters, invocation) { | ||||
|         let name = invocation.get_sender(); | ||||
|         let [appName] = parameters; | ||||
|  | ||||
|         let pos = -1; | ||||
|         for (let i = 0; i < this._apps.length; i++) { | ||||
|             if (this._apps[i].appName == appName) { | ||||
|                 pos = i; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (pos == -1) { | ||||
|             for (let i = 0; i < this._apps.length; i++) { | ||||
|                 if (this._apps[i].name == name) { | ||||
|                     pos = i; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (pos != -1) | ||||
|             this._freeMediaPlayer(pos); | ||||
|  | ||||
|         invocation.return_value(GLib.Variant.new('()', [])); | ||||
|     }, | ||||
|  | ||||
|     _freeMediaPlayer: function(pos) { | ||||
|         let app = this._apps[pos]; | ||||
|         Gio.bus_unwatch_name(app.watchId) | ||||
|  | ||||
|         this._apps.splice(pos, 1); | ||||
|     }, | ||||
|  | ||||
|     mediaKeyPressed: function(key) { | ||||
|         if (this._apps.length == 0) { | ||||
|             showOSD('action-unavailable-symbolic'); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let app = this._apps[0]; | ||||
|         Gio.DBus.session.emit_signal(app.name, | ||||
|                                      '/org/gnome/SettingsDaemon/MediaKeys', | ||||
|                                      'org.gnome.SettingsDaemon.MediaKeys', | ||||
|                                      'MediaPlayerKeyPressed', | ||||
|                                      GLib.Variant.new('(ss)', [app.appName || '', | ||||
|                                                                key])); | ||||
|     }, | ||||
| }); | ||||
|  | ||||
| const MediaKeysManager = new Lang.Class({ | ||||
|     Name: 'MediaKeysManager', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._a11yControl = Main.panel.statusArea.a11y; | ||||
|         this._volumeControl = Main.panel.statusArea.volume; | ||||
|         this._userMenu = Main.panel.statusArea.userMenu; | ||||
|         this._mediaPlayerKeys = new MediaKeysGrabber(); | ||||
|  | ||||
|         this._keybindingSettings = new Gio.Settings({ schema: KEYBINDING_SETTINGS }); | ||||
|     }, | ||||
|  | ||||
|     enable: function() { | ||||
|         for (let i = 0; i < DEFAULT_KEYBINDINGS.length; i++) { | ||||
|             let [action, setting, keyval, overviewOnly, args] = DEFAULT_KEYBINDINGS[i]; | ||||
|             let func = this[action]; | ||||
|             if (!func) { | ||||
|                 log('Keybinding action %s is missing'.format(action)); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             let name = setting ? setting : 'media-keys-keybindings-%d'.format(i); | ||||
|             let ok; | ||||
|             func = Util.wrapKeybinding(Lang.bind.apply(null, [this, func].concat(args)), overviewOnly); | ||||
|             if (setting) | ||||
|                 ok = global.display.add_keybinding(setting, this._keybindingSettings, | ||||
|                                                    Meta.KeyBindingFlags.BUILTIN | | ||||
|                                                    Meta.KeyBindingFlags.IS_SINGLE | | ||||
|                                                    Meta.KeyBindingFlags.HANDLE_WHEN_GRABBED, func); | ||||
|             else | ||||
|                 ok = global.display.add_grabbed_key(name, keyval, | ||||
|                                                     Meta.KeyBindingFlags.HANDLE_WHEN_GRABBED, func); | ||||
|  | ||||
|             if (!ok) | ||||
|                 log('Installing keybinding %s failed'.format(name)); | ||||
|         } | ||||
|  | ||||
|         this._customKeybindings = []; | ||||
|         this._changedId = this._keybindingSettings.connect('changed::custom-keybindings', | ||||
|                                                            Lang.bind(this, this._reloadCustomKeybindings)); | ||||
|         this._reloadCustomKeybindings(); | ||||
|  | ||||
|         this._mediaPlayerKeys.enable(); | ||||
|     }, | ||||
|  | ||||
|     disable: function() { | ||||
|         for (let i = 0; i < DEFAULT_KEYBINDINGS.length; i++) { | ||||
|             let [action, setting, keyval, overviewOnly, args] = DEFAULT_KEYBINDINGS[i]; | ||||
|  | ||||
|             let name = setting ? setting : 'media-keys-keybindings-%d'.format(i); | ||||
|             if (setting) | ||||
|                 global.display.remove_keybinding(setting, this._keybindingSettings); | ||||
|             else | ||||
|                 global.display.remove_grabbed_key(name); | ||||
|         } | ||||
|  | ||||
|         this._clearCustomKeybindings(); | ||||
|         this._keybindingSettings.disconnect(this._changedId); | ||||
|  | ||||
|         this._mediaPlayerKeys.disable(); | ||||
|     }, | ||||
|  | ||||
|     _clearCustomKeybindings: function() { | ||||
|         for (let i = 0; i < this._customKeybindings.length; i++) | ||||
|             global.display.remove_keybinding('binding', this._customKeybindings[i]); | ||||
|  | ||||
|         this._customKeybindings = []; | ||||
|     }, | ||||
|  | ||||
|     _reloadCustomKeybindings: function() { | ||||
|         this._clearCustomKeybindings(); | ||||
|  | ||||
|         let paths = this._keybindingSettings.get_strv('custom-keybindings'); | ||||
|         for (let i = 0; i < paths.length; i++) { | ||||
|             let setting = new Gio.Settings({ schema: CUSTOM_KEYBINDING_SETTINGS, | ||||
|                                              path: paths[i] }); | ||||
|             let func = Util.wrapKeybinding(Lang.bind(this, this.doCustom, setting), true); | ||||
|  | ||||
|             global.display.add_keybinding('binding', setting, | ||||
|                                           Meta.KeyBindingFlags.IS_SINGLE | | ||||
|                                           Meta.KeyBindingFlags.HANDLE_WHEN_GRABBED, func); | ||||
|             this._customKeybindings.push(setting); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     doCustom: function(display, screen, window, binding, settings) { | ||||
|         let command = settings.get_string('command'); | ||||
|         Util.spawnCommandLine(command); | ||||
|     }, | ||||
|  | ||||
|     doTouchpadToggle: function(display, screen, window, binding) { | ||||
|         let settings = new Gio.Settings({ schema: TOUCHPAD_SETTINGS }); | ||||
|         let enabled = settings.get_boolean('touchpad-enabled'); | ||||
|  | ||||
|         this.doTouchpadSet(display, screen, window, binding, !enabled); | ||||
|         settings.set_boolean(!enabled); | ||||
|  | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     doTouchpadSet: function(display, screen, window, binding, enabled) { | ||||
|         showOSD(enabled ? 'input-touchpad-symbolic' : 'touchpad-disabled-symbolic'); | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     doMute: function(display, screen, window, binding, quiet) { | ||||
|         let [icon, value] = this._volumeControl.volumeMenu.toggleMute(quiet); | ||||
|         showOSD(icon, value); | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     doVolumeAdjust: function(display, screen, window, binding, direction, quiet) { | ||||
|         let [icon, value] = this._volumeControl.volumeMenu.scroll(direction, quiet); | ||||
|         showOSD(icon, value); | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     doLogout: function(display, screen, window, binding) { | ||||
|         this._userMenu.logOut(); | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     doEject: function(display, screen, window, binding) { | ||||
|         let volumeMonitor = Gio.VolumeMonitor.get(); | ||||
|  | ||||
|         let drives = volumeMonitor.get_connected_drives(); | ||||
|         let score = 0, drive; | ||||
|         for (let i = 0; i < drives.length; i++) { | ||||
|             if (!drives[i].can_eject()) | ||||
|                 continue; | ||||
|             if (!drives[i].is_media_removable()) | ||||
|                 continue; | ||||
|             if (score < 1) { | ||||
|                 drive = drives[i]; | ||||
|                 score = 1; | ||||
|             } | ||||
|             if (!drives[i].has_media()) | ||||
|                 continue; | ||||
|             if (score < 2) { | ||||
|                 drive = drives[i]; | ||||
|                 score = 2; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         showOSD('media-eject-custom-symbolic'); | ||||
|  | ||||
|         if (!drive) | ||||
|             return true; | ||||
|  | ||||
|         let mountOp = new ShellMountOperation.ShellMountOperation(drive); | ||||
|         drive.eject_with_operation(Gio.MountUnmountFlags.FORCE, | ||||
|                                    mountOp.mountOp, null, null); | ||||
|  | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     doHome: function() { | ||||
|         let homeFile = Gio.file_new_for_path (GLib.get_home_dir()); | ||||
|         let homeUri = homeFile.get_uri(); | ||||
|         Gio.app_info_launch_default_for_uri(homeUri, null); | ||||
|  | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     doLaunchMimeHandler: function(display, screen, window, binding, mimeType) { | ||||
|         let gioApp = Gio.AppInfo.get_default_for_type(mimeType, false); | ||||
|         if (gioApp != null) { | ||||
|             let app = Shell.AppSystem.get_default().lookup_app(gioApp.get_id()); | ||||
|             app.open_new_window(-1); | ||||
|         } else { | ||||
|             log('Could not find default application for \'%s\' mime-type'.format(mimeType)); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     doLaunchApp: function(display, screen, window, binding, appId) { | ||||
|         let app = Shell.AppSystem.get_default().lookup_app(appId); | ||||
|         app.open_new_window(-1); | ||||
|  | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     doScreensaver: function() { | ||||
|         // FIXME: handled in house, to the screenshield! | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     doSpawn: function(display, screen, window, binding, argv) { | ||||
|         Util.spawn(argv); | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     doMediaKey: function(display, screen, window, binding, key) { | ||||
|         this._mediaPlayerKeys.mediaKeyPressed(key); | ||||
|     }, | ||||
|  | ||||
|     _onXRandRFinished: function(connection, result) { | ||||
|         connection.call_finish(result); | ||||
|         this._XRandRCancellable = null; | ||||
|     }, | ||||
|  | ||||
|     doXRandRAction: function(display, screen, window, binding, action) { | ||||
|         if (this._XRandRCancellable) | ||||
|             this._XRandRCancellable.cancel(); | ||||
|  | ||||
|         this._XRandRCancellable = new Gio.Cancellable(); | ||||
|         Gio.DBus.session.call('org.gnome.SettingsDaemon', | ||||
|                               '/org/gnome/SettingsDaemon/XRANDR', | ||||
|                               'org.gnome.SettingsDaemon.XRANDR_2', | ||||
|                               action, | ||||
|                               GLib.Variant.new('(x)', [global.get_current_time()]), | ||||
|                               null, /* reply type */ | ||||
|                               Gio.DBusCallFlags.NONE, | ||||
|                               -1, | ||||
|                               this._XRandRCancellable, | ||||
|                               Lang.bind(this, this._onXRandRFinished)); | ||||
|     }, | ||||
|  | ||||
|     doA11yAction: function(display, screen, window, binding, key) { | ||||
|         let settings = new Gio.Settings({ schema: A11Y_SETTINGS }); | ||||
|         let enabled = settings.get_boolean(key); | ||||
|         settings.set_boolean(key, !enabled); | ||||
|     }, | ||||
|  | ||||
|     doTextSize: function(display, screen, window, binding, multiplier) { | ||||
|         // Same values used in the Seeing tab of the Universal Access panel | ||||
|         const FACTORS = [ 0.75, 1.0, 1.25, 1.5 ]; | ||||
|  | ||||
| 	// Figure out the current DPI scaling factor | ||||
|         let settings = new Gio.Settings({ schema: INTERFACE_SETTINGS }); | ||||
|         let factor = settings.get_double('text-scaling-factor'); | ||||
|         factor += multiplier * 0.25; | ||||
|  | ||||
|         /* Try to find a matching value */ | ||||
|         let distance = 1e6; | ||||
|         let best = 1.0; | ||||
|         for (let i = 0; i < FACTORS.length; i++) { | ||||
|             let d = Math.abs(factor - FACTORS[i]); | ||||
|             if (d < distance) { | ||||
|                 best = factors[i]; | ||||
|                 distance = d; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (best == 1.0) | ||||
|             settings.reset('text-scaling-factor'); | ||||
|         else | ||||
|             settings.set_double('text-scaling-factor', best); | ||||
|     }, | ||||
|  | ||||
|     doToggleContrast: function(display, screen, window, binding) { | ||||
|         this._a11yControl.toggleHighContrast(); | ||||
|     }, | ||||
|  | ||||
|     doMagnifierZoom: function(display, screen, window, binding, offset) { | ||||
|         let settings = new Gio.Settings({ schema: MAGNIFIER_SETTINGS }); | ||||
|  | ||||
|         let value = settings.get_value('mag-factor'); | ||||
|         value = Math.round(value + offset); | ||||
|         settings.set_value('mag-factor', value); | ||||
|     }, | ||||
|  | ||||
|     doPowerAction: function(display, screen, window, binding, action) { | ||||
|         let settings = new Gio.Settings({ schema: POWER_SETTINGS }); | ||||
|         switch (settings.get_string(action)) { | ||||
|         case 'suspend': | ||||
|             this._userMenu.suspend(); | ||||
|             break; | ||||
|         case 'interactive': | ||||
|         case 'shutdown': | ||||
|             this._userMenu.shutdown(); | ||||
|             break; | ||||
|         case 'hibernate': | ||||
|             this._userMenu.hibernate(); | ||||
|             break; | ||||
|         case 'blank': | ||||
|         case 'default': | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onBrightnessFinished: function(connection, result, kind) { | ||||
|         let [percentage] = connection.call_finish(result).deep_unpack(); | ||||
|  | ||||
|         let icon = kind == 'Keyboard' ? 'keyboard-brightness-symbolic' : 'display-brightness-symbolic'; | ||||
|         showOSD(icon, percentage / 100); | ||||
|     }, | ||||
|  | ||||
|     doBrightness: function(display, screen, window, binding, kind, action) { | ||||
|         let iface = 'org.gnome.SettingsDaemon.Power.' + kind; | ||||
|         let objectPath = '/org/gnome/SettingsDaemon/Power'; | ||||
|  | ||||
|         Gio.DBus.session.call('org.gnome.SettingsDaemon', | ||||
|                               objectPath, iface, action, | ||||
|                               null, null, /* parameters, reply type */ | ||||
|                               Gio.DBusCallFlags.NONE, -1, null, | ||||
|                               Lang.bind(this, this._onBrightnessFinished, kind)); | ||||
|     }, | ||||
|  | ||||
|     doInputSource: function(display, screen, window, binding, offset) { | ||||
|         let settings = new Gio.Settings({ schema: INPUT_SOURCE_SETTINGS }); | ||||
|  | ||||
|         let current = settings.get_uint('current'); | ||||
|         let max = settings.get_strv('sources').length - 1; | ||||
|  | ||||
|         current += offset; | ||||
|         if (current < 0) | ||||
|             current = 0; | ||||
|         else if (current > max) | ||||
|             current = max; | ||||
|  | ||||
|         settings.set_uint('current', current); | ||||
|     }, | ||||
| }); | ||||
|  | ||||
| const Component = MediaKeysManager; | ||||
| @@ -1,60 +0,0 @@ | ||||
|  | ||||
| const Lang = imports.lang; | ||||
|  | ||||
| const Gio = imports.gi.Gio; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Shell = imports.gi.Shell; | ||||
|  | ||||
| const Recorder = new Lang.Class({ | ||||
|     Name: 'Recorder', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' }); | ||||
|         this._desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' }); | ||||
|         this._bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' }); | ||||
|         this._recorder = null; | ||||
|     }, | ||||
|  | ||||
|     enable: function() { | ||||
|         global.display.add_keybinding('toggle-recording', | ||||
|                                       this._bindingSettings, | ||||
|                                       Meta.KeyBindingFlags.NONE, Lang.bind(this, this._toggleRecorder)); | ||||
|     }, | ||||
|  | ||||
|     disable: function() { | ||||
|         global.display.remove_keybinding('toggle-recording'); | ||||
|     }, | ||||
|  | ||||
|     _ensureRecorder: function() { | ||||
|         if (this._recorder == null) | ||||
|             this._recorder = new Shell.Recorder({ stage: global.stage }); | ||||
|         return this._recorder; | ||||
|     }, | ||||
|  | ||||
|     _toggleRecorder: function() { | ||||
|         let recorder = this._ensureRecorder(); | ||||
|         if (recorder.is_recording()) { | ||||
|             recorder.close(); | ||||
|             Meta.enable_unredirect_for_screen(global.screen); | ||||
|         } else if (!this._desktopLockdownSettings.get_boolean('disable-save-to-disk')) { | ||||
|             // read the parameters from GSettings always in case they have changed | ||||
|             recorder.set_framerate(this._recorderSettings.get_int('framerate')); | ||||
|             /* Translators: this is a filename used for screencast recording */ | ||||
|             // xgettext:no-c-format | ||||
|             recorder.set_file_template(_("Screencast from %d %t") + '.' + this._recorderSettings.get_string('file-extension')); | ||||
|             let pipeline = this._recorderSettings.get_string('pipeline'); | ||||
|  | ||||
|             if (!pipeline.match(/^\s*$/)) | ||||
|                 recorder.set_pipeline(pipeline); | ||||
|             else | ||||
|                 recorder.set_pipeline(null); | ||||
|  | ||||
|             Meta.disable_unredirect_for_screen(global.screen); | ||||
|             recorder.record(); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const Component = Recorder; | ||||
| @@ -27,6 +27,7 @@ const CtrlAltTabManager = new Lang.Class({ | ||||
|  | ||||
|     _init: function() { | ||||
|         this._items = []; | ||||
|         this._focusManager = St.FocusManager.get_for_stage(global.stage); | ||||
|     }, | ||||
|  | ||||
|     addGroup: function(root, name, icon, params) { | ||||
| @@ -40,11 +41,11 @@ const CtrlAltTabManager = new Lang.Class({ | ||||
|  | ||||
|         this._items.push(item); | ||||
|         root.connect('destroy', Lang.bind(this, function() { this.removeGroup(root); })); | ||||
|         global.focus_manager.add_group(root); | ||||
|         this._focusManager.add_group(root); | ||||
|     }, | ||||
|  | ||||
|     removeGroup: function(root) { | ||||
|         global.focus_manager.remove_group(root); | ||||
|         this._focusManager.remove_group(root); | ||||
|         for (let i = 0; i < this._items.length; i++) { | ||||
|             if (this._items[i].root == root) { | ||||
|                 this._items.splice(i, 1); | ||||
| @@ -54,17 +55,16 @@ const CtrlAltTabManager = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     focusGroup: function(item) { | ||||
|         if (item.window) { | ||||
|             Main.activateWindow(item.window); | ||||
|         } else if (item.focusCallback) { | ||||
|             item.focusCallback(); | ||||
|         } else { | ||||
|             if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE || | ||||
|                 global.stage_input_mode == Shell.StageInputMode.NORMAL) | ||||
|                 global.set_stage_input_mode(Shell.StageInputMode.FOCUSED); | ||||
|         if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE || | ||||
|             global.stage_input_mode == Shell.StageInputMode.NORMAL) | ||||
|             global.set_stage_input_mode(Shell.StageInputMode.FOCUSED); | ||||
|  | ||||
|         if (item.window) | ||||
|             Main.activateWindow(item.window); | ||||
|         else if (item.focusCallback) | ||||
|             item.focusCallback(); | ||||
|         else | ||||
|             item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     // Sort the items into a consistent order; panel first, tray last, | ||||
| @@ -319,6 +319,7 @@ const CtrlAltTabSwitcher = new Lang.Class({ | ||||
|         let icon = item.iconActor; | ||||
|         if (!icon) { | ||||
|             icon = new St.Icon({ icon_name: item.iconName, | ||||
|                                  icon_type: St.IconType.SYMBOLIC, | ||||
|                                  icon_size: POPUP_APPICON_SIZE }); | ||||
|         } | ||||
|         box.add(icon, { x_fill: false, y_fill: false } ); | ||||
|   | ||||
							
								
								
									
										282
									
								
								js/ui/dash.js
									
									
									
									
									
								
							
							
						
						
									
										282
									
								
								js/ui/dash.js
									
									
									
									
									
								
							| @@ -21,17 +21,6 @@ const DASH_ITEM_LABEL_SHOW_TIME = 0.15; | ||||
| const DASH_ITEM_LABEL_HIDE_TIME = 0.1; | ||||
| const DASH_ITEM_HOVER_TIMEOUT = 300; | ||||
|  | ||||
| function getAppFromSource(source) { | ||||
|     if (source instanceof AppDisplay.AppWellIcon) { | ||||
|         return source.app; | ||||
|     } else if (source.metaWindow) { | ||||
|         let tracker = Shell.WindowTracker.get_default(); | ||||
|         return tracker.get_window_app(source.metaWindow); | ||||
|     } else { | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
|  | ||||
| // A container like StBin, but taking the child's scale into account | ||||
| // when requesting a size | ||||
| const DashItemContainer = new Lang.Class({ | ||||
| @@ -47,11 +36,7 @@ const DashItemContainer = new Lang.Class({ | ||||
|                            Lang.bind(this, this._allocate)); | ||||
|         this.actor._delegate = this; | ||||
|  | ||||
|         this._labelText = ""; | ||||
|         this.label = new St.Label({ style_class: 'dash-label'}); | ||||
|         this.label.hide(); | ||||
|         Main.layoutManager.addChrome(this.label); | ||||
|         this.actor.label_actor = this.label; | ||||
|         this.label = null; | ||||
|  | ||||
|         this.child = null; | ||||
|         this._childScale = 1; | ||||
| @@ -106,10 +91,9 @@ const DashItemContainer = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     showLabel: function() { | ||||
|         if (!this._labelText) | ||||
|         if (this.label == null) | ||||
|             return; | ||||
|  | ||||
|         this.label.set_text(this._labelText); | ||||
|         this.label.opacity = 0; | ||||
|         this.label.show(); | ||||
|  | ||||
| @@ -140,8 +124,12 @@ const DashItemContainer = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     setLabelText: function(text) { | ||||
|         this._labelText = text; | ||||
|         this.child.accessible_name = text; | ||||
|         if (this.label == null) | ||||
|             this.label = new St.Label({ style_class: 'dash-label'}); | ||||
|  | ||||
|         this.label.set_text(text); | ||||
|         Main.layoutManager.addChrome(this.label); | ||||
|         this.label.hide(); | ||||
|     }, | ||||
|  | ||||
|     hideLabel: function () { | ||||
| @@ -238,76 +226,52 @@ const DashItemContainer = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const ShowAppsIcon = new Lang.Class({ | ||||
|     Name: 'ShowAppsIcon', | ||||
| const RemoveFavoriteIcon = new Lang.Class({ | ||||
|     Name: 'RemoveFavoriteIcon', | ||||
|     Extends: DashItemContainer, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent(); | ||||
|  | ||||
|         this.toggleButton = new St.Button({ style_class: 'show-apps', | ||||
|                                             track_hover: true, | ||||
|                                             can_focus: true, | ||||
|                                             toggle_mode: true }); | ||||
|         this._iconBin = new St.Bin({ style_class: 'remove-favorite' }); | ||||
|         this._iconActor = null; | ||||
|         this.icon = new IconGrid.BaseIcon(_("Show Applications"), | ||||
|         this.icon = new IconGrid.BaseIcon(_("Remove"), | ||||
|                                            { setSizeManually: true, | ||||
|                                              showLabel: false, | ||||
|                                              createIcon: Lang.bind(this, this._createIcon) }); | ||||
|         this.toggleButton.add_actor(this.icon.actor); | ||||
|         this.toggleButton._delegate = this; | ||||
|         this._iconBin.set_child(this.icon.actor); | ||||
|         this._iconBin._delegate = this; | ||||
|  | ||||
|         this.setChild(this.toggleButton); | ||||
|         this.setDragApp(null); | ||||
|         this.setChild(this._iconBin); | ||||
|     }, | ||||
|  | ||||
|     _createIcon: function(size) { | ||||
|         this._iconActor = new St.Icon({ icon_name: 'view-grid-symbolic', | ||||
|                                         icon_size: size, | ||||
|                                         style_class: 'show-apps-icon', | ||||
|                                         track_hover: true }); | ||||
|         this._iconActor = new St.Icon({ icon_name: 'user-trash', | ||||
|                                         style_class: 'remove-favorite-icon', | ||||
|                                         icon_size: size }); | ||||
|         return this._iconActor; | ||||
|     }, | ||||
|  | ||||
|     _canRemoveApp: function(app) { | ||||
|         if (app == null) | ||||
|             return false; | ||||
|  | ||||
|         let id = app.get_id(); | ||||
|         let isFavorite = AppFavorites.getAppFavorites().isFavorite(id); | ||||
|         return isFavorite; | ||||
|     }, | ||||
|  | ||||
|     setDragApp: function(app) { | ||||
|         let canRemove = this._canRemoveApp(app); | ||||
|  | ||||
|         this.toggleButton.set_hover(canRemove); | ||||
|     setHover: function(hovered) { | ||||
|         this._iconBin.set_hover(hovered); | ||||
|         if (this._iconActor) | ||||
|             this._iconActor.set_hover(canRemove); | ||||
|  | ||||
|         if (canRemove) | ||||
|             this.setLabelText(_("Remove from Favorites")); | ||||
|         else | ||||
|             this.setLabelText(_("Show Applications")); | ||||
|             this._iconActor.set_hover(hovered); | ||||
|     }, | ||||
|  | ||||
|     // Rely on the dragged item being a favorite | ||||
|     handleDragOver: function(source, actor, x, y, time) { | ||||
|         let app = getAppFromSource(source); | ||||
|         if (app == null) | ||||
|             return DND.DragMotionResult.NO_DROP; | ||||
|  | ||||
|         let id = app.get_id(); | ||||
|         let isFavorite = AppFavorites.getAppFavorites().isFavorite(id); | ||||
|         if (!isFavorite) | ||||
|             return DND.DragMotionResult.NO_DROP; | ||||
|  | ||||
|         return DND.DragMotionResult.MOVE_DROP; | ||||
|     }, | ||||
|  | ||||
|     acceptDrop: function(source, actor, x, y, time) { | ||||
|         let app = getAppFromSource(source); | ||||
|         if (app == null) | ||||
|             return false; | ||||
|         let app = null; | ||||
|         if (source instanceof AppDisplay.AppWellIcon) { | ||||
|             let appSystem = Shell.AppSystem.get_default(); | ||||
|             app = appSystem.lookup_app(source.getId()); | ||||
|         } else if (source.metaWindow) { | ||||
|             let tracker = Shell.WindowTracker.get_default(); | ||||
|             app = tracker.get_window_app(source.metaWindow); | ||||
|         } | ||||
|  | ||||
|         let id = app.get_id(); | ||||
|  | ||||
| @@ -331,39 +295,6 @@ const DragPlaceholderItem = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const DashActor = new Lang.Class({ | ||||
|     Name: 'DashActor', | ||||
|     Extends: St.Widget, | ||||
|  | ||||
|     _init: function() { | ||||
|         let layout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.VERTICAL }); | ||||
|         this.parent({ name: 'dash', | ||||
|                       layout_manager: layout, | ||||
|                       clip_to_allocation: true }); | ||||
|     }, | ||||
|  | ||||
|     vfunc_allocate: function(box, flags) { | ||||
|         let contentBox = this.get_theme_node().get_content_box(box); | ||||
|         let availWidth = contentBox.x2 - contentBox.x1; | ||||
|  | ||||
|         this.set_allocation(box, flags); | ||||
|  | ||||
|         let [appIcons, showAppsButton] = this.get_children(); | ||||
|         let [showAppsMinHeight, showAppsNatHeight] = showAppsButton.get_preferred_height(availWidth); | ||||
|  | ||||
|         let childBox = new Clutter.ActorBox(); | ||||
|         childBox.x1 = contentBox.x1; | ||||
|         childBox.y1 = contentBox.y1; | ||||
|         childBox.x2 = contentBox.x2; | ||||
|         childBox.y2 = contentBox.y2 - showAppsNatHeight; | ||||
|         appIcons.allocate(childBox, flags); | ||||
|  | ||||
|         childBox.y1 = contentBox.y2 - showAppsNatHeight; | ||||
|         childBox.y2 = contentBox.y2; | ||||
|         showAppsButton.allocate(childBox, flags); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const Dash = new Lang.Class({ | ||||
|     Name: 'Dash', | ||||
|  | ||||
| @@ -375,25 +306,17 @@ const Dash = new Lang.Class({ | ||||
|         this._dragPlaceholder = null; | ||||
|         this._dragPlaceholderPos = -1; | ||||
|         this._animatingPlaceholdersCount = 0; | ||||
|         this._favRemoveTarget = null; | ||||
|         this._showLabelTimeoutId = 0; | ||||
|         this._resetHoverTimeoutId = 0; | ||||
|         this._labelShowing = false; | ||||
|  | ||||
|         this._container = new DashActor(); | ||||
|         this._box = new St.BoxLayout({ vertical: true, | ||||
|         this._box = new St.BoxLayout({ name: 'dash', | ||||
|                                        vertical: true, | ||||
|                                        clip_to_allocation: true }); | ||||
|         this._box._delegate = this; | ||||
|         this._container.add_actor(this._box); | ||||
|  | ||||
|         this._showAppsIcon = new ShowAppsIcon(); | ||||
|         this._showAppsIcon.icon.setIconSize(this.iconSize); | ||||
|         this._hookUpLabel(this._showAppsIcon); | ||||
|  | ||||
|         this.showAppsButton = this._showAppsIcon.toggleButton; | ||||
|  | ||||
|         this._container.add_actor(this._showAppsIcon.actor); | ||||
|  | ||||
|         this.actor = new St.Bin({ child: this._container }); | ||||
|         this.actor = new St.Bin({ y_align: St.Align.START, child: this._box }); | ||||
|         this.actor.connect('notify::height', Lang.bind(this, | ||||
|             function() { | ||||
|                 if (this._maxHeight != this.actor.height) | ||||
| @@ -403,6 +326,7 @@ const Dash = new Lang.Class({ | ||||
|  | ||||
|         this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay)); | ||||
|  | ||||
|         this._tracker = Shell.WindowTracker.get_default(); | ||||
|         this._appSystem = Shell.AppSystem.get_default(); | ||||
|  | ||||
|         this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay)); | ||||
| @@ -445,25 +369,54 @@ const Dash = new Lang.Class({ | ||||
|  | ||||
|     _endDrag: function() { | ||||
|         this._clearDragPlaceholder(); | ||||
|         this._showAppsIcon.setDragApp(null); | ||||
|         if (this._favRemoveTarget) { | ||||
|             this._favRemoveTarget.animateOutAndDestroy(); | ||||
|             this._favRemoveTarget.actor.connect('destroy', Lang.bind(this, | ||||
|                 function() { | ||||
|                     this._favRemoveTarget = null; | ||||
|                 })); | ||||
|             this._adjustIconSize(); | ||||
|         } | ||||
|         DND.removeDragMonitor(this._dragMonitor); | ||||
|     }, | ||||
|  | ||||
|     _onDragMotion: function(dragEvent) { | ||||
|         let app = getAppFromSource(dragEvent.source); | ||||
|         if (app == null) | ||||
|         let app = null; | ||||
|         if (dragEvent.source instanceof AppDisplay.AppWellIcon) | ||||
|             app = this._appSystem.lookup_app(dragEvent.source.getId()); | ||||
|         else if (dragEvent.source.metaWindow) | ||||
|             app = this._tracker.get_window_app(dragEvent.source.metaWindow); | ||||
|         else | ||||
|             return DND.DragMotionResult.CONTINUE; | ||||
|  | ||||
|         let showAppsHovered = | ||||
|                 this._showAppsIcon.actor.contains(dragEvent.targetActor); | ||||
|         let id = app.get_id(); | ||||
|  | ||||
|         if (!this._box.contains(dragEvent.targetActor) || showAppsHovered) | ||||
|         let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); | ||||
|  | ||||
|         let srcIsFavorite = (id in favorites); | ||||
|  | ||||
|         if (srcIsFavorite && | ||||
|             app.get_state() != Shell.AppState.RUNNING && | ||||
|             dragEvent.source.actor && | ||||
|             this.actor.contains (dragEvent.source.actor) && | ||||
|             this._favRemoveTarget == null) { | ||||
|                 this._favRemoveTarget = new RemoveFavoriteIcon(); | ||||
|                 this._favRemoveTarget.icon.setIconSize(this.iconSize); | ||||
|                 this._box.add(this._favRemoveTarget.actor); | ||||
|                 this._adjustIconSize(); | ||||
|                 this._favRemoveTarget.animateIn(); | ||||
|         } | ||||
|  | ||||
|         let favRemoveHovered = false; | ||||
|         if (this._favRemoveTarget) | ||||
|             favRemoveHovered = | ||||
|                 this._favRemoveTarget.actor.contains(dragEvent.targetActor); | ||||
|  | ||||
|         if (!this._box.contains(dragEvent.targetActor) || favRemoveHovered) | ||||
|             this._clearDragPlaceholder(); | ||||
|  | ||||
|         if (showAppsHovered) | ||||
|             this._showAppsIcon.setDragApp(app); | ||||
|         else | ||||
|             this._showAppsIcon.setDragApp(null); | ||||
|         if (this._favRemoveTarget) | ||||
|             this._favRemoveTarget.setHover(favRemoveHovered); | ||||
|  | ||||
|         return DND.DragMotionResult.CONTINUE; | ||||
|     }, | ||||
| @@ -479,63 +432,44 @@ const Dash = new Lang.Class({ | ||||
|         Main.queueDeferredWork(this._workId); | ||||
|     }, | ||||
|  | ||||
|     _hookUpLabel: function(item) { | ||||
|         item.child.connect('notify::hover', Lang.bind(this, function() { | ||||
|             this._onHover(item); | ||||
|         })); | ||||
|  | ||||
|         Main.overview.connect('hiding', Lang.bind(this, function() { | ||||
|             this._labelShowing = false; | ||||
|             item.hideLabel(); | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     _createAppItem: function(app) { | ||||
|         let appIcon = new AppDisplay.AppWellIcon(app, | ||||
|         let display = new AppDisplay.AppWellIcon(app, | ||||
|                                                  { setSizeManually: true, | ||||
|                                                    showLabel: false }); | ||||
|         appIcon._draggable.connect('drag-begin', | ||||
|         display._draggable.connect('drag-begin', | ||||
|                                    Lang.bind(this, function() { | ||||
|                                        appIcon.actor.opacity = 50; | ||||
|                                        display.actor.opacity = 50; | ||||
|                                    })); | ||||
|         appIcon._draggable.connect('drag-end', | ||||
|         display._draggable.connect('drag-end', | ||||
|                                    Lang.bind(this, function() { | ||||
|                                        appIcon.actor.opacity = 255; | ||||
|                                        display.actor.opacity = 255; | ||||
|                                    })); | ||||
|         appIcon.connect('menu-state-changed', | ||||
|                         Lang.bind(this, function(appIcon, opened) { | ||||
|                             this._itemMenuStateChanged(item, opened); | ||||
|                         })); | ||||
|  | ||||
|         let item = new DashItemContainer(); | ||||
|         item.setChild(appIcon.actor); | ||||
|         item.setChild(display.actor); | ||||
|  | ||||
|         // Override default AppWellIcon label_actor, now the | ||||
|         // accessible_name is set at DashItemContainer.setLabelText | ||||
|         appIcon.actor.label_actor = null; | ||||
|         item.setLabelText(app.get_name()); | ||||
|         // Override default AppWellIcon label_actor | ||||
|         display.actor.label_actor = item.label; | ||||
|  | ||||
|         appIcon.icon.setIconSize(this.iconSize); | ||||
|         this._hookUpLabel(item); | ||||
|  | ||||
|         display.icon.setIconSize(this.iconSize); | ||||
|         display.actor.connect('notify::hover', | ||||
|                                Lang.bind(this, function() { | ||||
|                                    this._onHover(item, display) | ||||
|                                })); | ||||
|  | ||||
|         Main.overview.connect('hiding', | ||||
|                               Lang.bind(this, function() { | ||||
|                                   this._labelShowing = false; | ||||
|                                   item.hideLabel(); | ||||
|                               })); | ||||
|  | ||||
|         return item; | ||||
|     }, | ||||
|  | ||||
|     _itemMenuStateChanged: function(item, opened) { | ||||
|         // When the menu closes, it calls sync_hover, which means | ||||
|         // that the notify::hover handler does everything we need to. | ||||
|         if (opened) { | ||||
|             if (this._showLabelTimeoutId > 0) { | ||||
|                 Mainloop.source_remove(this._showLabelTimeoutId); | ||||
|                 this._showLabelTimeoutId = 0; | ||||
|             } | ||||
|  | ||||
|             item.hideLabel(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onHover: function (item) { | ||||
|         if (item.child.get_hover()) { | ||||
|     _onHover: function (item, display) { | ||||
|         if (display.actor.get_hover() && !display.isMenuUp) { | ||||
|             if (this._showLabelTimeoutId == 0) { | ||||
|                 let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT; | ||||
|                 this._showLabelTimeoutId = Mainloop.timeout_add(timeout, | ||||
| @@ -576,12 +510,18 @@ const Dash = new Lang.Class({ | ||||
|                    !actor._delegate.animatingOut; | ||||
|         }); | ||||
|  | ||||
|         iconChildren.push(this._showAppsIcon.actor); | ||||
|         if (iconChildren.length == 0) { | ||||
|             this._box.add_style_pseudo_class('empty'); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._box.remove_style_pseudo_class('empty'); | ||||
|  | ||||
|         if (this._maxHeight == -1) | ||||
|             return; | ||||
|  | ||||
|         let themeNode = this._container.get_theme_node(); | ||||
|  | ||||
|         let themeNode = this._box.get_theme_node(); | ||||
|         let maxAllocation = new Clutter.ActorBox({ x1: 0, y1: 0, | ||||
|                                                    x2: 42 /* whatever */, | ||||
|                                                    y2: this._maxHeight }); | ||||
| @@ -607,6 +547,7 @@ const Dash = new Lang.Class({ | ||||
|             [minHeight, natHeight] = iconChildren[0].get_preferred_height(-1); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // Subtract icon padding and box spacing from the available height | ||||
|         availHeight -= iconChildren.length * (natHeight - this.iconSize) + | ||||
|                        (iconChildren.length - 1) * spacing; | ||||
| @@ -795,7 +736,11 @@ const Dash = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     handleDragOver : function(source, actor, x, y, time) { | ||||
|         let app = getAppFromSource(source); | ||||
|         let app = null; | ||||
|         if (source instanceof AppDisplay.AppWellIcon) | ||||
|             app = this._appSystem.lookup_app(source.getId()); | ||||
|         else if (source.metaWindow) | ||||
|             app = this._tracker.get_window_app(source.metaWindow); | ||||
|  | ||||
|         // Don't allow favoriting of transient apps | ||||
|         if (app == null || app.is_window_backed()) | ||||
| @@ -876,7 +821,12 @@ const Dash = new Lang.Class({ | ||||
|  | ||||
|     // Draggable target interface | ||||
|     acceptDrop : function(source, actor, x, y, time) { | ||||
|         let app = getAppFromSource(source); | ||||
|         let app = null; | ||||
|         if (source instanceof AppDisplay.AppWellIcon) { | ||||
|             app = this._appSystem.lookup_app(source.getId()); | ||||
|         } else if (source.metaWindow) { | ||||
|             app = this._tracker.get_window_app(source.metaWindow); | ||||
|         } | ||||
|  | ||||
|         // Don't allow favoriting of transient apps | ||||
|         if (app == null || app.is_window_backed()) { | ||||
|   | ||||
| @@ -70,8 +70,16 @@ const DateMenuButton = new Lang.Class({ | ||||
|         this._date.style_class = 'datemenu-date-label'; | ||||
|         vbox.add(this._date); | ||||
|  | ||||
|         this._eventList = new Calendar.EventsList(); | ||||
|         this._calendar = new Calendar.Calendar(); | ||||
|         if (Main.sessionMode.showCalendarEvents) { | ||||
|             this._eventSource = new Calendar.DBusEventSource(); | ||||
|             this._eventList = new Calendar.EventsList(this._eventSource); | ||||
|         } else { | ||||
|             this._eventSource = null; | ||||
|             this._eventList = null; | ||||
|         } | ||||
|  | ||||
|         // Calendar | ||||
|         this._calendar = new Calendar.Calendar(this._eventSource); | ||||
|  | ||||
|         this._calendar.connect('selected-date-changed', | ||||
|                                Lang.bind(this, function(calendar, date) { | ||||
| @@ -89,34 +97,31 @@ const DateMenuButton = new Lang.Class({ | ||||
|             separator.setColumnWidths(1); | ||||
|             vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false}); | ||||
|  | ||||
|             item.actor.show_on_set_parent = false; | ||||
|             item.actor.can_focus = false; | ||||
|             item.actor.reparent(vbox); | ||||
|             this._dateAndTimeSeparator = separator; | ||||
|         } | ||||
|  | ||||
|         this._separator = new St.DrawingArea({ style_class: 'calendar-vertical-separator', | ||||
|                                                pseudo_class: 'highlighted' }); | ||||
|         this._separator.connect('repaint', Lang.bind(this, _onVertSepRepaint)); | ||||
|         hbox.add(this._separator); | ||||
|         if (Main.sessionMode.showCalendarEvents) { | ||||
|             // Add vertical separator | ||||
|  | ||||
|         // Fill up the second column | ||||
|         vbox = new St.BoxLayout({ name: 'calendarEventsArea', | ||||
|                                   vertical: true }); | ||||
|         hbox.add(vbox, { expand: true }); | ||||
|             item = new St.DrawingArea({ style_class: 'calendar-vertical-separator', | ||||
|                                         pseudo_class: 'highlighted' }); | ||||
|             item.connect('repaint', Lang.bind(this, _onVertSepRepaint)); | ||||
|             hbox.add(item); | ||||
|  | ||||
|         // Event list | ||||
|         vbox.add(this._eventList.actor, { expand: true }); | ||||
|             // Fill up the second column | ||||
|             vbox = new St.BoxLayout({name:     'calendarEventsArea', | ||||
|                                      vertical: true}); | ||||
|             hbox.add(vbox, { expand: true }); | ||||
|  | ||||
|         this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar")); | ||||
|         this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate)); | ||||
|         this._openCalendarItem.actor.can_focus = false; | ||||
|         vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false}); | ||||
|             // Event list | ||||
|             vbox.add(this._eventList.actor, { expand: true }); | ||||
|  | ||||
|         this._calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' }); | ||||
|         this._calendarSettings.connect('changed::exec', | ||||
|                                        Lang.bind(this, this._calendarSettingsChanged)); | ||||
|         this._calendarSettingsChanged(); | ||||
|             item = new PopupMenu.PopupMenuItem(_("Open Calendar")); | ||||
|             item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate)); | ||||
|             item.actor.can_focus = false; | ||||
|             vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false}); | ||||
|         } | ||||
|  | ||||
|         // Whenever the menu is opened, select today | ||||
|         this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) { | ||||
| @@ -146,51 +151,6 @@ const DateMenuButton = new Lang.Class({ | ||||
|         this._clock = new GnomeDesktop.WallClock(); | ||||
|         this._clock.connect('notify::clock', Lang.bind(this, this._updateClockAndDate)); | ||||
|         this._updateClockAndDate(); | ||||
|  | ||||
|         Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); | ||||
|         this._sessionUpdated(); | ||||
|     }, | ||||
|  | ||||
|     _calendarSettingsChanged: function() { | ||||
|         let exec = this._calendarSettings.get_string('exec'); | ||||
|         let fullExec = GLib.find_program_in_path(exec); | ||||
|         this._openCalendarItem.actor.visible = fullExec != null; | ||||
|     }, | ||||
|  | ||||
|     _setEventsVisibility: function(visible) { | ||||
|         this._openCalendarItem.actor.visible = visible; | ||||
|         this._separator.visible = visible; | ||||
|         if (visible) { | ||||
|           let alignment = 0.25; | ||||
|           if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) | ||||
|             alignment = 1.0 - alignment; | ||||
|           this.menu._arrowAlignment = alignment; | ||||
|           this._eventList.actor.get_parent().show(); | ||||
|         } else { | ||||
|           this.menu._arrowAlignment = 0.5; | ||||
|           this._eventList.actor.get_parent().hide(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _setEventSource: function(eventSource) { | ||||
|         this._calendar.setEventSource(eventSource); | ||||
|         this._eventList.setEventSource(eventSource); | ||||
|     }, | ||||
|  | ||||
|     _sessionUpdated: function() { | ||||
|         let eventSource; | ||||
|         let showEvents = Main.sessionMode.showCalendarEvents; | ||||
|         if (showEvents) { | ||||
|             eventSource = new Calendar.DBusEventSource(); | ||||
|         } else { | ||||
|             eventSource = null; | ||||
|         } | ||||
|         this._setEventSource(eventSource); | ||||
|         this._setEventsVisibility(showEvents); | ||||
|  | ||||
|         // This needs to be handled manually, as the code to | ||||
|         // autohide separators doesn't work across the vbox | ||||
|         this._dateAndTimeSeparator.actor.visible = Main.sessionMode.allowSettings; | ||||
|     }, | ||||
|  | ||||
|     _updateClockAndDate: function() { | ||||
| @@ -205,13 +165,14 @@ const DateMenuButton = new Lang.Class({ | ||||
|  | ||||
|     _onOpenCalendarActivate: function() { | ||||
|         this.menu.close(); | ||||
|         let tool = this._calendarSettings.get_string('exec'); | ||||
|         let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' }); | ||||
|         let tool = calendarSettings.get_string('exec'); | ||||
|         if (tool.length == 0 || tool.substr(0, 9) == 'evolution') { | ||||
|             // TODO: pass the selected day | ||||
|             let app = Shell.AppSystem.get_default().lookup_app('evolution-calendar.desktop'); | ||||
|             app.activate(); | ||||
|         } else { | ||||
|             let needTerm = this._calendarSettings.get_boolean('needs-term'); | ||||
|             let needTerm = calendarSettings.get_boolean('needs-term'); | ||||
|             if (needTerm) { | ||||
|                 let terminalSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.terminal' }); | ||||
|                 let term = terminalSettings.get_string('exec'); | ||||
|   | ||||
							
								
								
									
										13
									
								
								js/ui/dnd.js
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								js/ui/dnd.js
									
									
									
									
									
								
							| @@ -235,10 +235,6 @@ const _Draggable = new Lang.Class({ | ||||
|  | ||||
|         if (this.actor._delegate && this.actor._delegate.getDragActor) { | ||||
|             this._dragActor = this.actor._delegate.getDragActor(this._dragStartX, this._dragStartY); | ||||
|             this._dragActor.reparent(Main.uiGroup); | ||||
|             this._dragActor.raise_top(); | ||||
|             Shell.util_set_hidden_from_pick(this._dragActor, true); | ||||
|  | ||||
|             // Drag actor does not always have to be the same as actor. For example drag actor | ||||
|             // can be an image that's part of the actor. So to perform "snap back" correctly we need | ||||
|             // to know what was the drag actor source. | ||||
| @@ -267,17 +263,12 @@ const _Draggable = new Lang.Class({ | ||||
|             this._dragOffsetY = this._dragActor.y - this._dragStartY; | ||||
|         } else { | ||||
|             this._dragActor = this.actor; | ||||
|  | ||||
|             this._dragActorSource = undefined; | ||||
|             this._dragOrigParent = this.actor.get_parent(); | ||||
|             this._dragOrigX = this._dragActor.x; | ||||
|             this._dragOrigY = this._dragActor.y; | ||||
|             this._dragOrigScale = this._dragActor.scale_x; | ||||
|  | ||||
|             this._dragActor.reparent(Main.uiGroup); | ||||
|             this._dragActor.raise_top(); | ||||
|             Shell.util_set_hidden_from_pick(this._dragActor, true); | ||||
|  | ||||
|             let [actorStageX, actorStageY] = this.actor.get_transformed_position(); | ||||
|             this._dragOffsetX = actorStageX - this._dragStartX; | ||||
|             this._dragOffsetY = actorStageY - this._dragStartY; | ||||
| @@ -289,6 +280,10 @@ const _Draggable = new Lang.Class({ | ||||
|                                  scaledHeight / this.actor.height); | ||||
|         } | ||||
|  | ||||
|         this._dragActor.reparent(Main.uiGroup); | ||||
|         this._dragActor.raise_top(); | ||||
|         Shell.util_set_hidden_from_pick(this._dragActor, true); | ||||
|  | ||||
|         this._dragOrigOpacity = this._dragActor.opacity; | ||||
|         if (this._dragActorOpacity != undefined) | ||||
|             this._dragActor.opacity = this._dragActorOpacity; | ||||
|   | ||||
| @@ -34,7 +34,6 @@ const GnomeSession = imports.misc.gnomeSession; | ||||
| const Main = imports.ui.main; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
| const Tweener = imports.ui.tweener; | ||||
| const UserMenu = imports.ui.userMenu; | ||||
|  | ||||
| let _endSessionDialog = null; | ||||
|  | ||||
| @@ -90,7 +89,7 @@ const shutdownDialogContent = { | ||||
|                        label:  C_("button", "Restart") }, | ||||
|                      { signal: 'ConfirmedShutdown', | ||||
|                        label:  C_("button", "Power Off") }], | ||||
|     iconName: 'system-shutdown-symbolic', | ||||
|     iconName: 'system-shutdown', | ||||
|     iconStyleClass: 'end-session-dialog-shutdown-icon' | ||||
| }; | ||||
|  | ||||
| @@ -105,7 +104,7 @@ const restartDialogContent = { | ||||
|     endDescription: _("Restarting the system."), | ||||
|     confirmButtons: [{ signal: 'ConfirmedReboot', | ||||
|                        label:  C_("button", "Restart") }], | ||||
|     iconName: 'view-refresh-symbolic', | ||||
|     iconName: 'system-shutdown', | ||||
|     iconStyleClass: 'end-session-dialog-shutdown-icon' | ||||
| }; | ||||
|  | ||||
| @@ -161,7 +160,6 @@ const ListItem = new Lang.Class({ | ||||
|  | ||||
|         this._descriptionLabel = new St.Label({ text:        this._reason, | ||||
|                                                 style_class: 'end-session-dialog-app-list-item-description' }); | ||||
|         this.actor.label_actor = this._nameLabel; | ||||
|         textLayout.add(this._descriptionLabel, | ||||
|                        { expand: true, | ||||
|                          x_fill: true }); | ||||
| @@ -304,6 +302,41 @@ const EndSessionDialog = new Lang.Class({ | ||||
|         this._user.disconnect(this._userChangedId); | ||||
|     }, | ||||
|  | ||||
|     _setIconFromFile: function(iconFile, styleClass) { | ||||
|         if (styleClass) | ||||
|             this._iconBin.set_style_class_name(styleClass); | ||||
|         this._iconBin.set_style(null); | ||||
|  | ||||
|         this._iconBin.child = null; | ||||
|         if (iconFile) { | ||||
|             this._iconBin.show(); | ||||
|             this._iconBin.set_style('background-image: url("' + iconFile + '");' + | ||||
|                                     'background-size: contain;'); | ||||
|         } else { | ||||
|             this._iconBin.hide(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _setIconFromName: function(iconName, styleClass) { | ||||
|         if (styleClass) | ||||
|             this._iconBin.set_style_class_name(styleClass); | ||||
|         this._iconBin.set_style(null); | ||||
|  | ||||
|         if (iconName != null) { | ||||
|             let textureCache = St.TextureCache.get_default(); | ||||
|             let icon = textureCache.load_icon_name(this._iconBin.get_theme_node(), | ||||
|                                                    iconName, | ||||
|                                                    St.IconType.SYMBOLIC, | ||||
|                                                    _DIALOG_ICON_SIZE); | ||||
|  | ||||
|             this._iconBin.child = icon; | ||||
|             this._iconBin.show(); | ||||
|         } else { | ||||
|             this._iconBin.child = null; | ||||
|             this._iconBin.hide(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _updateDescription: function() { | ||||
|         if (this.state != ModalDialog.State.OPENING && | ||||
|             this.state != ModalDialog.State.OPENED) | ||||
| @@ -352,16 +385,16 @@ const EndSessionDialog = new Lang.Class({ | ||||
|             return; | ||||
|  | ||||
|         let dialogContent = DialogContent[this._type]; | ||||
|         if (dialogContent.iconName) { | ||||
|             this._iconBin.child = new St.Icon({ icon_name: dialogContent.iconName, | ||||
|                                                 icon_size: _DIALOG_ICON_SIZE, | ||||
|                                                 style_class: dialogContent.iconStyleClass }); | ||||
|         } else { | ||||
|             let avatarWidget = new UserMenu.UserAvatarWidget(this._user, | ||||
|                                                              { iconSize: _DIALOG_ICON_SIZE, | ||||
|                                                                styleClass: dialogContent.iconStyleClass }); | ||||
|             this._iconBin.child = avatarWidget.actor; | ||||
|             avatarWidget.update(); | ||||
|  | ||||
|         if (this._user.is_loaded && !dialogContent.iconName) { | ||||
|             let iconFile = this._user.get_icon_file(); | ||||
|             if (GLib.file_test(iconFile, GLib.FileTest.EXISTS)) | ||||
|                 this._setIconFromFile(iconFile, dialogContent.iconStyleClass); | ||||
|             else | ||||
|                 this._setIconFromName('avatar-default', dialogContent.iconStyleClass); | ||||
|         } else if (dialogContent.iconName) { | ||||
|             this._setIconFromName(dialogContent.iconName, | ||||
|                                   dialogContent.iconStyleClass); | ||||
|         } | ||||
|  | ||||
|         this._updateDescription(); | ||||
|   | ||||
| @@ -60,7 +60,7 @@ function uninstallExtension(uuid) { | ||||
|     if (extension.type != ExtensionUtils.ExtensionType.PER_USER) | ||||
|         return false; | ||||
|  | ||||
|     if (!ExtensionSystem.unloadExtension(extension)) | ||||
|     if (!ExtensionSystem.unloadExtension(uuid)) | ||||
|         return false; | ||||
|  | ||||
|     FileUtils.recursivelyDeleteDir(extension.dir, true); | ||||
| @@ -82,7 +82,7 @@ function gotExtensionZipFile(session, message, uuid, dir, callback, errback) { | ||||
|     } | ||||
|  | ||||
|     let [file, stream] = Gio.File.new_tmp('XXXXXX.shell-extension.zip'); | ||||
|     let contents = message.response_body.flatten().get_as_bytes(); | ||||
|     let contents = message.response_body.flatten().as_bytes(); | ||||
|     stream.output_stream.write_bytes(contents, null); | ||||
|     stream.close(null); | ||||
|     let [success, pid] = GLib.spawn_async(null, | ||||
| @@ -124,7 +124,7 @@ function updateExtension(uuid) { | ||||
|             let oldExtension = ExtensionUtils.extensions[uuid]; | ||||
|             let extensionDir = oldExtension.dir; | ||||
|  | ||||
|             if (!ExtensionSystem.unloadExtension(oldExtension)) | ||||
|             if (!ExtensionSystem.unloadExtension(uuid)) | ||||
|                 return; | ||||
|  | ||||
|             FileUtils.recursivelyMoveDir(extensionDir, oldExtensionTmpDir); | ||||
| @@ -135,7 +135,7 @@ function updateExtension(uuid) { | ||||
|             try { | ||||
|                 ExtensionSystem.loadExtension(extension); | ||||
|             } catch(e) { | ||||
|                 ExtensionSystem.unloadExtension(extension); | ||||
|                 ExtensionSystem.unloadExtension(uuid); | ||||
|  | ||||
|                 logError(e, 'Error loading extension %s'.format(uuid)); | ||||
|  | ||||
|   | ||||
| @@ -8,7 +8,6 @@ const Gio = imports.gi.Gio; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const ExtensionUtils = imports.misc.extensionUtils; | ||||
| const Main = imports.ui.main; | ||||
|  | ||||
| const ExtensionState = { | ||||
|     ENABLED: 1, | ||||
| @@ -39,9 +38,6 @@ const disconnect = Lang.bind(_signals, _signals.disconnect); | ||||
|  | ||||
| const ENABLED_EXTENSIONS_KEY = 'enabled-extensions'; | ||||
|  | ||||
| var initted = false; | ||||
| var enabled; | ||||
|  | ||||
| function disableExtension(uuid) { | ||||
|     let extension = ExtensionUtils.extensions[uuid]; | ||||
|     if (!extension) | ||||
| @@ -106,6 +102,8 @@ function enableExtension(uuid) { | ||||
|  | ||||
|     extensionOrder.push(uuid); | ||||
|  | ||||
|     extension.stateObj.enable(); | ||||
|  | ||||
|     let stylesheetFile = extension.dir.get_child('stylesheet.css'); | ||||
|     if (stylesheetFile.query_exists(null)) { | ||||
|         let theme = St.ThemeContext.get_for_stage(global.stage).get_theme(); | ||||
| @@ -113,8 +111,6 @@ function enableExtension(uuid) { | ||||
|         extension.stylesheet = stylesheetFile; | ||||
|     } | ||||
|  | ||||
|     extension.stateObj.enable(); | ||||
|  | ||||
|     extension.state = ExtensionState.ENABLED; | ||||
|     _signals.emit('extension-state-changed', extension); | ||||
| } | ||||
| @@ -162,32 +158,23 @@ function loadExtension(extension) { | ||||
|     _signals.emit('extension-state-changed', extension); | ||||
| } | ||||
|  | ||||
| function unloadExtension(extension) { | ||||
| function unloadExtension(uuid) { | ||||
|     let extension = ExtensionUtils.extensions[uuid]; | ||||
|     if (!extension) | ||||
|         return false; | ||||
|  | ||||
|     // Try to disable it -- if it's ERROR'd, we can't guarantee that, | ||||
|     // but it will be removed on next reboot, and hopefully nothing | ||||
|     // broke too much. | ||||
|     disableExtension(extension.uuid); | ||||
|     disableExtension(uuid); | ||||
|  | ||||
|     extension.state = ExtensionState.UNINSTALLED; | ||||
|     _signals.emit('extension-state-changed', extension); | ||||
|  | ||||
|     delete ExtensionUtils.extensions[extension.uuid]; | ||||
|     delete ExtensionUtils.extensions[uuid]; | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| function reloadExtension(oldExtension) { | ||||
|     // Grab the things we'll need to pass to createExtensionObject | ||||
|     // to reload it. | ||||
|     let { uuid: uuid, dir: dir, type: type } = oldExtension; | ||||
|  | ||||
|     // Then unload the old extension. | ||||
|     unloadExtension(oldExtension); | ||||
|  | ||||
|     // Now, recreate the extension and load it. | ||||
|     let newExtension = ExtensionUtils.createExtensionObject(uuid, dir, type); | ||||
|     loadExtension(newExtension); | ||||
| } | ||||
|  | ||||
| function initExtension(uuid) { | ||||
|     let extension = ExtensionUtils.extensions[uuid]; | ||||
|     let dir = extension.dir; | ||||
| @@ -220,9 +207,6 @@ function initExtension(uuid) { | ||||
| function onEnabledExtensionsChanged() { | ||||
|     let newEnabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY); | ||||
|  | ||||
|     if (!enabled) | ||||
|         return; | ||||
|  | ||||
|     // Find and enable all the newly enabled extensions: UUIDs found in the | ||||
|     // new setting, but not in the old one. | ||||
|     newEnabledExtensions.filter(function(uuid) { | ||||
| @@ -250,7 +234,7 @@ function onEnabledExtensionsChanged() { | ||||
|     enabledExtensions = newEnabledExtensions; | ||||
| } | ||||
|  | ||||
| function _loadExtensions() { | ||||
| function loadExtensions() { | ||||
|     global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged); | ||||
|     enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY); | ||||
|  | ||||
| @@ -264,43 +248,3 @@ function _loadExtensions() { | ||||
|     }); | ||||
|     finder.scanExtensions(); | ||||
| } | ||||
|  | ||||
| function enableAllExtensions() { | ||||
|     if (enabled) | ||||
|         return; | ||||
|  | ||||
|     if (!initted) { | ||||
|         _loadExtensions(); | ||||
|         initted = true; | ||||
|     } else { | ||||
|         enabledExtensions.forEach(function(uuid) { | ||||
|             enableExtension(uuid); | ||||
|         }); | ||||
|     } | ||||
|     enabled = true; | ||||
| } | ||||
|  | ||||
| function disableAllExtensions() { | ||||
|     if (!enabled) | ||||
|         return; | ||||
|  | ||||
|     if (initted) { | ||||
|         enabledExtensions.forEach(function(uuid) { | ||||
|             disableExtension(uuid); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     enabled = false; | ||||
| } | ||||
|  | ||||
| function _sessionUpdated() { | ||||
|     if (Main.sessionMode.allowExtensions) | ||||
|         enableAllExtensions(); | ||||
|     else | ||||
|         disableAllExtensions(); | ||||
| } | ||||
|  | ||||
| function init() { | ||||
|     Main.sessionMode.connect('updated', _sessionUpdated); | ||||
|     _sessionUpdated(); | ||||
| } | ||||
|   | ||||
| @@ -1,358 +0,0 @@ | ||||
| /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Lang = imports.lang; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const Params = imports.misc.params; | ||||
|  | ||||
| function _navigateActor(actor) { | ||||
|     if (!actor) | ||||
|         return; | ||||
|  | ||||
|     let needsGrab = true; | ||||
|     if (actor instanceof St.Widget) | ||||
|         needsGrab = !actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); | ||||
|     if (needsGrab) | ||||
|         actor.grab_key_focus(); | ||||
| } | ||||
|  | ||||
| // GrabHelper: | ||||
| // @owner: the actor that owns the GrabHelper | ||||
| // | ||||
| // Creates a new GrabHelper object, for dealing with keyboard and pointer grabs | ||||
| // associated with a set of actors. | ||||
| // | ||||
| // Note that the grab can be automatically dropped at any time by the user, and | ||||
| // your code just needs to deal with it; you shouldn't adjust behavior directly | ||||
| // after you call ungrab(), but instead pass an 'onUngrab' callback when you | ||||
| // call grab(). | ||||
| const GrabHelper = new Lang.Class({ | ||||
|     Name: 'GrabHelper', | ||||
|  | ||||
|     _init: function(owner) { | ||||
|         this._owner = owner; | ||||
|  | ||||
|         this._grabStack = []; | ||||
|  | ||||
|         this._actors = []; | ||||
|         this._capturedEventId = 0; | ||||
|         this._eventId = 0; | ||||
|         this._keyFocusNotifyId = 0; | ||||
|         this._focusWindowChangedId = 0; | ||||
|         this._ignoreRelease = false; | ||||
|  | ||||
|         this._modalCount = 0; | ||||
|         this._grabFocusCount = 0; | ||||
|     }, | ||||
|  | ||||
|     // addActor: | ||||
|     // @actor: an actor | ||||
|     // | ||||
|     // Adds @actor to the set of actors that are allowed to process events | ||||
|     // during a grab. | ||||
|     addActor: function(actor) { | ||||
|         actor.__grabHelperDestroyId = actor.connect('destroy', Lang.bind(this, function() { this.removeActor(actor); })); | ||||
|         this._actors.push(actor); | ||||
|     }, | ||||
|  | ||||
|     // removeActor: | ||||
|     // @actor: an actor | ||||
|     // | ||||
|     // Removes @actor from the set of actors that are allowed to | ||||
|     // process events during a grab. | ||||
|     removeActor: function(actor) { | ||||
|         let index = this._actors.indexOf(actor); | ||||
|         if (index != -1) | ||||
|             this._actors.splice(index, 1); | ||||
|         if (actor.__grabHelperDestroyId) { | ||||
|             actor.disconnect(actor.__grabHelperDestroyId); | ||||
|             delete actor.__grabHelperDestroyId; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _isWithinGrabbedActor: function(actor) { | ||||
|         while (actor) { | ||||
|             if (this._actors.indexOf(actor) != -1) | ||||
|                 return true; | ||||
|             actor = actor.get_parent(); | ||||
|         } | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     get currentGrab() { | ||||
|         return this._grabStack[this._grabStack.length - 1] || {}; | ||||
|     }, | ||||
|  | ||||
|     _findStackIndex: function(actor) { | ||||
|         if (!actor) | ||||
|             return -1; | ||||
|  | ||||
|         for (let i = 0; i < this._grabStack.length; i++) { | ||||
|             if (this._grabStack[i].actor === actor) | ||||
|                 return i; | ||||
|         } | ||||
|         return -1; | ||||
|     }, | ||||
|  | ||||
|     isActorGrabbed: function(actor) { | ||||
|         return this._findStackIndex(actor) >= 0; | ||||
|     }, | ||||
|  | ||||
|     // grab: | ||||
|     // @params: A bunch of parameters, see below | ||||
|     // | ||||
|     // Grabs the mouse and keyboard, according to the GrabHelper's | ||||
|     // parameters. If @newFocus is not %null, then the keyboard focus | ||||
|     // is moved to the first #StWidget:can-focus widget inside it. | ||||
|     // | ||||
|     // The grab will automatically be dropped if: | ||||
|     //   - The user clicks outside the grabbed actors | ||||
|     //   - The user types Escape | ||||
|     //   - The keyboard focus is moved outside the grabbed actors | ||||
|     //   - A window is focused | ||||
|     // | ||||
|     // If @params.actor is not null, then it will be focused as the | ||||
|     // new actor. If you attempt to grab an already focused actor, the | ||||
|     // request to be focused will be ignored. The actor will not be | ||||
|     // added to the grab stack, so do not call a paired ungrab(). | ||||
|     // | ||||
|     // If @params contains { modal: true }, then grab() will push a modal | ||||
|     // on the owner of the GrabHelper. As long as there is at least one | ||||
|     // { modal: true } actor on the grab stack, the grab will be kept. | ||||
|     // When the last { modal: true } actor is ungrabbed, then the modal | ||||
|     // will be dropped. A modal grab can fail if there is already a grab | ||||
|     // in effect from aother application; in this case the function returns | ||||
|     // false and nothing happens. Non-modal grabs can never fail. | ||||
|     // | ||||
|     // If @params contains { grabFocus: true }, then if you call grab() | ||||
|     // while the shell is outside the overview, it will set the stage | ||||
|     // input mode to %Shell.StageInputMode.FOCUSED, and ungrab() will | ||||
|     // revert it back, and re-focus the previously-focused window (if | ||||
|     // another window hasn't been explicitly focused before then). | ||||
|     grab: function(params) { | ||||
|         params = Params.parse(params, { actor: null, | ||||
|                                         modal: false, | ||||
|                                         grabFocus: false, | ||||
|                                         onUngrab: null }); | ||||
|  | ||||
|         let focus = global.stage.key_focus; | ||||
|         let hadFocus = focus && this._isWithinGrabbedActor(focus); | ||||
|         let newFocus = params.actor; | ||||
|  | ||||
|         if (this.isActorGrabbed(params.actor)) | ||||
|             return true; | ||||
|  | ||||
|         params.savedFocus = focus; | ||||
|  | ||||
|         if (params.modal && !this._takeModalGrab()) | ||||
|             return false; | ||||
|  | ||||
|         if (params.grabFocus && !this._takeFocusGrab(hadFocus)) | ||||
|             return false; | ||||
|  | ||||
|         if (hadFocus || params.grabFocus) | ||||
|             _navigateActor(newFocus); | ||||
|  | ||||
|         this._grabStack.push(params); | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _takeModalGrab: function() { | ||||
|         let firstGrab = (this._modalCount == 0); | ||||
|         if (firstGrab) { | ||||
|             if (!Main.pushModal(this._owner)) | ||||
|                 return false; | ||||
|  | ||||
|             this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent)); | ||||
|             this._eventId = global.stage.connect('event', Lang.bind(this, this._onEvent)); | ||||
|         } | ||||
|  | ||||
|         this._modalCount++; | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _releaseModalGrab: function() { | ||||
|         this._modalCount--; | ||||
|         if (this._modalCount > 0) | ||||
|             return; | ||||
|  | ||||
|         if (this._capturedEventId > 0) { | ||||
|             global.stage.disconnect(this._capturedEventId); | ||||
|             this._capturedEventId = 0; | ||||
|         } | ||||
|  | ||||
|         if (this._eventId > 0) { | ||||
|             global.stage.disconnect(this._eventId); | ||||
|             this._eventId = 0; | ||||
|         } | ||||
|  | ||||
|         Main.popModal(this._owner); | ||||
|         global.sync_pointer(); | ||||
|     }, | ||||
|  | ||||
|     _takeFocusGrab: function(hadFocus) { | ||||
|         let firstGrab = (this._grabFocusCount == 0); | ||||
|         if (firstGrab) { | ||||
|             let metaDisplay = global.screen.get_display(); | ||||
|  | ||||
|             this._grabbedFromKeynav = hadFocus; | ||||
|             this._preGrabInputMode = global.stage_input_mode; | ||||
|             this._prevFocusedWindow = metaDisplay.focus_window; | ||||
|  | ||||
|             if (this._preGrabInputMode == Shell.StageInputMode.NONREACTIVE || | ||||
|                 this._preGrabInputMode == Shell.StageInputMode.NORMAL) { | ||||
|                 global.set_stage_input_mode(Shell.StageInputMode.FOCUSED); | ||||
|             } | ||||
|  | ||||
|             this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged)); | ||||
|             this._focusWindowChangedId = metaDisplay.connect('notify::focus-window', Lang.bind(this, this._focusWindowChanged)); | ||||
|         } | ||||
|  | ||||
|         this._grabFocusCount++; | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _releaseFocusGrab: function() { | ||||
|         this._grabFocusCount--; | ||||
|         if (this._grabFocusCount > 0) | ||||
|             return; | ||||
|  | ||||
|         if (this._keyFocusNotifyId > 0) { | ||||
|             global.stage.disconnect(this._keyFocusNotifyId); | ||||
|             this._keyFocusNotifyId = 0; | ||||
|         } | ||||
|  | ||||
|         if (!this._focusWindowChanged > 0) { | ||||
|             let metaDisplay = global.screen.get_display(); | ||||
|             metaDisplay.disconnect(this._focusWindowChangedId); | ||||
|             this._focusWindowChangedId = 0; | ||||
|         } | ||||
|  | ||||
|         let prePopInputMode = global.stage_input_mode; | ||||
|  | ||||
|         if (this._grabbedFromKeynav) { | ||||
|             if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED && | ||||
|                 prePopInputMode != Shell.StageInputMode.FULLSCREEN) | ||||
|                 global.set_stage_input_mode(Shell.StageInputMode.FOCUSED); | ||||
|         } | ||||
|  | ||||
|         if (this._prevFocusedWindow) { | ||||
|             let metaDisplay = global.screen.get_display(); | ||||
|             if (!metaDisplay.focus_window) { | ||||
|                 metaDisplay.set_input_focus_window(this._prevFocusedWindow, | ||||
|                                                    false, global.get_current_time()); | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     // ignoreRelease: | ||||
|     // | ||||
|     // Make sure that the next button release event evaluated by the | ||||
|     // capture event handler returns false. This is designed for things | ||||
|     // like the ComboBoxMenu that go away on press, but need to eat | ||||
|     // the next release event. | ||||
|     ignoreRelease: function() { | ||||
|         this._ignoreRelease = true; | ||||
|     }, | ||||
|  | ||||
|     // ungrab: | ||||
|     // @params: The parameters for the grab; see below. | ||||
|     // | ||||
|     // Pops an actor from the grab stack, potentially dropping the grab. | ||||
|     // | ||||
|     // If the actor that was popped from the grab stack was not the actor | ||||
|     // That was passed in, this call is ignored. | ||||
|     ungrab: function(params) { | ||||
|         params = Params.parse(params, { actor: this.currentGrab.actor }); | ||||
|  | ||||
|         let grabStackIndex = this._findStackIndex(params.actor); | ||||
|         if (grabStackIndex < 0) | ||||
|             return; | ||||
|  | ||||
|         let focus = global.stage.key_focus; | ||||
|         let hadFocus = focus && this._isWithinGrabbedActor(focus); | ||||
|  | ||||
|         let poppedGrabs = this._grabStack.slice(grabStackIndex); | ||||
|         // "Pop" all newly ungrabbed actors off the grab stack | ||||
|         // by truncating the array. | ||||
|         this._grabStack.length = grabStackIndex; | ||||
|  | ||||
|         for (let i = poppedGrabs.length - 1; i >= 0; i--) { | ||||
|             let poppedGrab = poppedGrabs[i]; | ||||
|  | ||||
|             if (poppedGrab.onUngrab) | ||||
|                 poppedGrab.onUngrab(); | ||||
|  | ||||
|             if (poppedGrab.modal) | ||||
|                 this._releaseModalGrab(); | ||||
|  | ||||
|             if (poppedGrab.grabFocus) | ||||
|                 this._releaseFocusGrab(); | ||||
|         } | ||||
|  | ||||
|         if (hadFocus) { | ||||
|             let poppedGrab = poppedGrabs[0]; | ||||
|             _navigateActor(poppedGrab.savedFocus); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onCapturedEvent: function(actor, event) { | ||||
|         let type = event.type(); | ||||
|         let press = type == Clutter.EventType.BUTTON_PRESS; | ||||
|         let release = type == Clutter.EventType.BUTTON_RELEASE; | ||||
|         let button = press || release; | ||||
|  | ||||
|         if (release && this._ignoreRelease) { | ||||
|             this._ignoreRelease = false; | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (!button && this._modalCount == 0) | ||||
|             return false; | ||||
|  | ||||
|         if (this._isWithinGrabbedActor(event.get_source())) | ||||
|             return false; | ||||
|  | ||||
|         if (Main.keyboard.shouldTakeEvent(event)) | ||||
|             return false; | ||||
|  | ||||
|         if (button) { | ||||
|             // If we have a press event, ignore the next event, | ||||
|             // which should be a release event. | ||||
|             if (press) | ||||
|                 this._ignoreRelease = true; | ||||
|             this.ungrab({ actor: this._grabStack[0].actor }); | ||||
|         } | ||||
|  | ||||
|         return this._modalCount > 0; | ||||
|     }, | ||||
|  | ||||
|     // We catch 'event' rather than 'key-press-event' so that we get | ||||
|     // a chance to run before the overview's own Escape check | ||||
|     _onEvent: function(actor, event) { | ||||
|         if (event.type() == Clutter.EventType.KEY_PRESS && | ||||
|             event.get_key_symbol() == Clutter.KEY_Escape) { | ||||
|             this.ungrab(); | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     _onKeyFocusChanged: function() { | ||||
|         let focus = global.stage.key_focus; | ||||
|         if (!focus || !this._isWithinGrabbedActor(focus)) | ||||
|             this.ungrab(); | ||||
|     }, | ||||
|  | ||||
|     _focusWindowChanged: function() { | ||||
|         let metaDisplay = global.screen.get_display(); | ||||
|         if (metaDisplay.focus_window != null) | ||||
|             this.ungrab(); | ||||
|     } | ||||
| }); | ||||
| @@ -20,6 +20,25 @@ const KEYBOARD_TYPE = 'keyboard-type'; | ||||
| const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications'; | ||||
| const SHOW_KEYBOARD = 'screen-keyboard-enabled'; | ||||
|  | ||||
| // Key constants taken from Antler | ||||
| // FIXME: ought to be moved into libcaribou | ||||
| const PRETTY_KEYS = { | ||||
|     'BackSpace': '\u232b', | ||||
|     'space': ' ', | ||||
|     'Return': '\u23ce', | ||||
|     'Caribou_Prefs': '\u2328', | ||||
|     'Caribou_ShiftUp': '\u2b06', | ||||
|     'Caribou_ShiftDown': '\u2b07', | ||||
|     'Caribou_Emoticons': '\u263a', | ||||
|     'Caribou_Symbols': '123', | ||||
|     'Caribou_Symbols_More': '{#*', | ||||
|     'Caribou_Alpha': 'Abc', | ||||
|     'Tab': 'Tab', | ||||
|     'Escape': 'Esc', | ||||
|     'Control_L': 'Ctrl', | ||||
|     'Alt_L': 'Alt' | ||||
| }; | ||||
|  | ||||
| const CaribouKeyboardIface = <interface name='org.gnome.Caribou.Keyboard'> | ||||
| <method name='Show'> | ||||
|     <arg type='u' direction='in' /> | ||||
| @@ -79,7 +98,17 @@ const Key = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _makeKey: function () { | ||||
|         let label = GLib.markup_escape_text(this._key.label, -1); | ||||
|         let label = this._key.name; | ||||
|  | ||||
|         if (label.length > 1) { | ||||
|             let pretty = PRETTY_KEYS[label]; | ||||
|             if (pretty) | ||||
|                 label = pretty; | ||||
|             else | ||||
|                 label = this._getUnichar(this._key); | ||||
|         } | ||||
|  | ||||
|         label = GLib.markup_escape_text(label, -1); | ||||
|         let button = new St.Button ({ label: label, | ||||
|                                       style_class: 'keyboard-key' }); | ||||
|  | ||||
| @@ -171,10 +200,8 @@ const Keyboard = new Lang.Class({ | ||||
|         this._impl.export(Gio.DBus.session, '/org/gnome/Caribou/Keyboard'); | ||||
|  | ||||
|         this.actor = null; | ||||
|         this._focusInTray = false; | ||||
|         this._focusInExtendedKeys = false; | ||||
|  | ||||
|         this._timestamp = global.display.get_current_time_roundtrip(); | ||||
|         this._timestamp = global.get_current_time(); | ||||
|         Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw)); | ||||
|  | ||||
|         this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA }); | ||||
| @@ -261,15 +288,7 @@ const Keyboard = new Lang.Class({ | ||||
|  | ||||
|         // Showing an extended key popup and clicking a key from the extended keys | ||||
|         // will grab focus, but ignore that | ||||
|         let extendedKeysWereFocused = this._focusInExtendedKeys; | ||||
|         this._focusInExtendedKeys = focus && (focus._extended_keys || focus.extended_key); | ||||
|         if (this._focusInExtendedKeys || extendedKeysWereFocused) | ||||
|             return; | ||||
|  | ||||
|         // Ignore focus changes caused by message tray showing/hiding | ||||
|         let trayWasFocused = this._focusInTray; | ||||
|         this._focusInTray = (focus && Main.messageTray.actor.contains(focus)); | ||||
|         if (this._focusInTray || trayWasFocused) | ||||
|         if (focus && (focus._extended_keys || (focus._key && focus._key.extended_key))) | ||||
|             return; | ||||
|  | ||||
|         let time = global.get_current_time(); | ||||
| @@ -320,13 +339,6 @@ const Keyboard = new Lang.Class({ | ||||
|             trayButton.reactive = true; | ||||
|             trayButton.remove_style_pseudo_class('grayed'); | ||||
|         })); | ||||
|         Main.sessionMode.connect('updated', Lang.bind(this, function() { | ||||
|             trayButton.reactive = !Main.sessionMode.isLocked; | ||||
|             if (Main.sessionMode.isLocked) | ||||
|                 trayButton.add_style_pseudo_class('grayed'); | ||||
|             else | ||||
|                 trayButton.remove_style_pseudo_class('grayed'); | ||||
|         })); | ||||
|  | ||||
|         return trayButton; | ||||
|     }, | ||||
| @@ -456,12 +468,6 @@ const Keyboard = new Lang.Class({ | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     shouldTakeEvent: function(event) { | ||||
|         let actor = event.get_source(); | ||||
|         return Main.layoutManager.keyboardBox.contains(actor) || | ||||
|                actor._extended_keys || actor.extended_key; | ||||
|     }, | ||||
|  | ||||
|     show: function () { | ||||
|         this._redraw(); | ||||
|  | ||||
| @@ -488,30 +494,15 @@ const Keyboard = new Lang.Class({ | ||||
|             this._moveTemporarily(); | ||||
|     }, | ||||
|  | ||||
|     // _compareTimestamp: | ||||
|     // | ||||
|     // Compare two timestamps taking into account | ||||
|     // CURRENT_TIME (0) | ||||
|     _compareTimestamp: function(one, two) { | ||||
|         if (one == two) | ||||
|             return 0; | ||||
|         if (one == Clutter.CURRENT_TIME) | ||||
|             return 1; | ||||
|         if (two == Clutter.CURRENT_TIME) | ||||
|             return -1; | ||||
|         return one - two; | ||||
|     }, | ||||
|  | ||||
|     // D-Bus methods | ||||
|     Show: function(timestamp) { | ||||
|         if (!this._enableKeyboard) | ||||
|             return; | ||||
|  | ||||
|         if (this._compareTimestamp(timestamp, this._timestamp) < 0) | ||||
|         if (timestamp - this._timestamp < 0) | ||||
|             return; | ||||
|  | ||||
|         if (timestamp != Clutter.CURRENT_TIME) | ||||
|             this._timestamp = timestamp; | ||||
|         this._timestamp = timestamp; | ||||
|         this.show(); | ||||
|     }, | ||||
|  | ||||
| @@ -519,11 +510,10 @@ const Keyboard = new Lang.Class({ | ||||
|         if (!this._enableKeyboard) | ||||
|             return; | ||||
|  | ||||
|         if (this._compareTimestamp(timestamp, this._timestamp) < 0) | ||||
|         if (timestamp - this._timestamp < 0) | ||||
|             return; | ||||
|  | ||||
|         if (timestamp != Clutter.CURRENT_TIME) | ||||
|             this._timestamp = timestamp; | ||||
|         this._timestamp = timestamp; | ||||
|         this.hide(); | ||||
|     }, | ||||
|  | ||||
| @@ -552,8 +542,7 @@ const KeyboardSource = new Lang.Class({ | ||||
|  | ||||
|     _init: function(keyboard) { | ||||
|         this._keyboard = keyboard; | ||||
|         this.parent(_("Keyboard"), 'input-keyboard-symbolic'); | ||||
|         this.keepTrayOnSummaryClick = true; | ||||
|         this.parent(_("Keyboard"), 'input-keyboard', St.IconType.SYMBOLIC); | ||||
|     }, | ||||
|  | ||||
|     handleSummaryClick: function() { | ||||
|   | ||||
| @@ -84,10 +84,7 @@ const KeyringDialog = new Lang.Class({ | ||||
|         if (this.prompt.password_visible) { | ||||
|             let label = new St.Label(({ style_class: 'prompt-dialog-password-label' })); | ||||
|             label.set_text(_("Password:")); | ||||
|             table.add(label, { row: row, col: 0, | ||||
|                                x_expand: false, x_fill: true, | ||||
|                                x_align: St.Align.START, | ||||
|                                y_fill: false, y_align: St.Align.MIDDLE }); | ||||
|             table.add(label, { row: row, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START }); | ||||
|             this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry', | ||||
|                                                  text: '', | ||||
|                                                  can_focus: true}); | ||||
| @@ -103,10 +100,7 @@ const KeyringDialog = new Lang.Class({ | ||||
|         if (this.prompt.confirm_visible) { | ||||
|             var label = new St.Label(({ style_class: 'prompt-dialog-password-label' })); | ||||
|             label.set_text(_("Type again:")); | ||||
|             table.add(label, { row: row, col: 0, | ||||
|                                x_expand: false, x_fill: true, | ||||
|                                x_align: St.Align.START, | ||||
|                                y_fill: false, y_align: St.Align.MIDDLE }); | ||||
|             table.add(label, { row: row, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START }); | ||||
|             this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry', | ||||
|                                                 text: '', | ||||
|                                                 can_focus: true}); | ||||
| @@ -192,36 +186,23 @@ const KeyringDialog = new Lang.Class({ | ||||
|     }, | ||||
| 
 | ||||
|     _onContinueButton: function() { | ||||
|         this.prompt.complete(); | ||||
|         this.prompt.complete() | ||||
|     }, | ||||
| 
 | ||||
|     _onCancelButton: function() { | ||||
|         this.prompt.cancel(); | ||||
|         this.prompt.cancel() | ||||
|     }, | ||||
| }); | ||||
| 
 | ||||
| const KeyringPrompter = new Lang.Class({ | ||||
|     Name: 'KeyringPrompter', | ||||
| function init() { | ||||
|     prompter = new Gcr.SystemPrompter(); | ||||
|     prompter.connect('new-prompt', function(prompter) { | ||||
|         let dialog = new KeyringDialog(); | ||||
|         return dialog.prompt; | ||||
|     }); | ||||
| 
 | ||||
|     _init: function() { | ||||
|         this._prompter = new Gcr.SystemPrompter(); | ||||
|         this._prompter.connect('new-prompt', function(prompter) { | ||||
|             let dialog = new KeyringDialog(); | ||||
|             return dialog.prompt; | ||||
|         }); | ||||
|         this._dbusId = null; | ||||
|     }, | ||||
| 
 | ||||
|     enable: function() { | ||||
|         this._prompter.register(Gio.DBus.session); | ||||
|         this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter', | ||||
|                                                  Gio.BusNameOwnerFlags.REPLACE, null, null); | ||||
|     }, | ||||
| 
 | ||||
|     disable: function() { | ||||
|         this._prompter.unregister(false); | ||||
|         Gio.DBus.session.unown_name(this._dbusId); | ||||
|     } | ||||
| }); | ||||
| 
 | ||||
| const Component = KeyringPrompter; | ||||
|     let connection = Gio.DBus.session; | ||||
|     prompter.register(connection); | ||||
|     Gio.bus_own_name_on_connection (connection, 'org.gnome.keyring.SystemPrompter', | ||||
|                                     Gio.BusNameOwnerFlags.REPLACE, null, null); | ||||
| } | ||||
							
								
								
									
										164
									
								
								js/ui/layout.js
									
									
									
									
									
								
							
							
						
						
									
										164
									
								
								js/ui/layout.js
									
									
									
									
									
								
							| @@ -1,7 +1,6 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GObject = imports.gi.GObject; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Meta = imports.gi.Meta; | ||||
| @@ -17,83 +16,6 @@ const Tweener = imports.ui.tweener; | ||||
| const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5; | ||||
| const STARTUP_ANIMATION_TIME = 0.2; | ||||
| const KEYBOARD_ANIMATION_TIME = 0.5; | ||||
| const PLYMOUTH_TRANSITION_TIME = 1; | ||||
|  | ||||
| const MonitorConstraint = new Lang.Class({ | ||||
|     Name: 'MonitorConstraint', | ||||
|     Extends: Clutter.Constraint, | ||||
|     Properties: {'primary': GObject.ParamSpec.boolean('primary',  | ||||
|                                                       'Primary', 'Track primary monitor', | ||||
|                                                       GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE, | ||||
|                                                       false), | ||||
|                  'index': GObject.ParamSpec.int('index', | ||||
|                                                 'Monitor index', 'Track specific monitor', | ||||
|                                                 GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE, | ||||
|                                                 -1, 64, -1)}, | ||||
|  | ||||
|     _init: function(props) { | ||||
|         this._primary = false; | ||||
|         this._index = -1; | ||||
|  | ||||
|         this.parent(props); | ||||
|     }, | ||||
|  | ||||
|     get primary() { | ||||
|         return this._primary; | ||||
|     }, | ||||
|  | ||||
|     set primary(v) { | ||||
|         if (v) | ||||
|             this._index = -1; | ||||
|         this._primary = v; | ||||
|         if (this.actor) | ||||
|             this.actor.queue_relayout(); | ||||
|         this.notify('primary'); | ||||
|     }, | ||||
|  | ||||
|     get index() { | ||||
|         return this._index; | ||||
|     }, | ||||
|  | ||||
|     set index(v) { | ||||
|         this._primary = false; | ||||
|         this._index = v; | ||||
|         if (this.actor) | ||||
|             this.actor.queue_relayout(); | ||||
|         this.notify('index'); | ||||
|     }, | ||||
|  | ||||
|     vfunc_set_actor: function(actor) { | ||||
|         if (actor) { | ||||
|             if (!this._monitorsChangedId) { | ||||
|                 this._monitorsChangedId = Main.layoutManager.connect('monitors-changed', Lang.bind(this, function() { | ||||
|                     this.actor.queue_relayout(); | ||||
|                 })); | ||||
|             } | ||||
|         } else { | ||||
|             if (this._monitorsChangedId) | ||||
|                 Main.layoutManager.disconnect(this._monitorsChangedId); | ||||
|             this._monitorsChangedId = 0; | ||||
|         } | ||||
|  | ||||
|         this.parent(actor); | ||||
|     }, | ||||
|  | ||||
|     vfunc_update_allocation: function(actor, actorBox) { | ||||
|         if (!this._primary && this._index < 0) | ||||
|             return; | ||||
|  | ||||
|         let monitor; | ||||
|         if (this._primary) { | ||||
|             monitor = Main.layoutManager.primaryMonitor; | ||||
|         } else { | ||||
|             let index = Math.min(this._index, Main.layoutManager.monitors.length - 1); | ||||
|             monitor = Main.layoutManager.monitors[index]; | ||||
|         } | ||||
|  | ||||
|         actorBox.init_rect(monitor.x, monitor.y, monitor.width, monitor.height); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const LayoutManager = new Lang.Class({ | ||||
|     Name: 'LayoutManager', | ||||
| @@ -104,7 +26,6 @@ const LayoutManager = new Lang.Class({ | ||||
|         this.primaryMonitor = null; | ||||
|         this.primaryIndex = -1; | ||||
|         this._hotCorners = []; | ||||
|         this._background = null; | ||||
|         this._leftPanelBarrier = 0; | ||||
|         this._rightPanelBarrier = 0; | ||||
|         this._trayBarrier = 0; | ||||
| @@ -114,7 +35,6 @@ const LayoutManager = new Lang.Class({ | ||||
|         this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup', | ||||
|                                                  visible: false, | ||||
|                                                  clip_to_allocation: true, | ||||
|                                                  layout_manager: new Clutter.BinLayout(), | ||||
|                                                }); | ||||
|         this.addChrome(this.screenShieldGroup); | ||||
|  | ||||
| @@ -125,9 +45,10 @@ const LayoutManager = new Lang.Class({ | ||||
|         this.panelBox.connect('allocation-changed', | ||||
|                               Lang.bind(this, this._updatePanelBarriers)); | ||||
|  | ||||
|         this.trayBox = new St.Widget({ name: 'trayBox', | ||||
|                                        layout_manager: new Clutter.BinLayout() });  | ||||
|         this.trayBox = new St.BoxLayout({ name: 'trayBox' });  | ||||
|         this.addChrome(this.trayBox); | ||||
|         this.trayBox.connect('allocation-changed', | ||||
|                              Lang.bind(this, this._updateTrayBarrier)); | ||||
|  | ||||
|         this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox', | ||||
|                                               reactive: true, | ||||
| @@ -278,6 +199,22 @@ const LayoutManager = new Lang.Class({ | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _updateTrayBarrier: function() { | ||||
|         let monitor = this.bottomMonitor; | ||||
|  | ||||
|         if (this._trayBarrier) | ||||
|             global.destroy_pointer_barrier(this._trayBarrier); | ||||
|  | ||||
|         if (Main.messageTray) { | ||||
|             this._trayBarrier = | ||||
|                 global.create_pointer_barrier(monitor.x + monitor.width, monitor.y + monitor.height - Main.messageTray.actor.height, | ||||
|                                               monitor.x + monitor.width, monitor.y + monitor.height, | ||||
|                                               4 /* BarrierNegativeX */); | ||||
|         } else { | ||||
|             this._trayBarrier = 0; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _monitorsChanged: function() { | ||||
|         this._updateMonitors(); | ||||
|         this._updateBoxes(); | ||||
| @@ -302,45 +239,14 @@ const LayoutManager = new Lang.Class({ | ||||
|  | ||||
|     get currentMonitor() { | ||||
|         let index = global.screen.get_current_monitor(); | ||||
|         return this.monitors[index]; | ||||
|         return Main.layoutManager.monitors[index]; | ||||
|     }, | ||||
|  | ||||
|     _startupAnimation: function() { | ||||
|         this.panelBox.anchor_y = this.panelBox.height; | ||||
|  | ||||
|         let plymouthTransitionRunning = false; | ||||
|  | ||||
|         // If we're the greeter, put up the xrootpmap actor | ||||
|         // and fade it out to have a nice transition from plymouth | ||||
|         // to the greeter. Otherwise, we'll just animate the panel, | ||||
|         // as usual. | ||||
|         if (Main.sessionMode.isGreeter) { | ||||
|             this._background = Meta.BackgroundActor.new_for_screen(global.screen); | ||||
|             if (this._background != null) { | ||||
|                 Main.uiGroup.add_actor(this._background); | ||||
|                 Tweener.addTween(this._background, | ||||
|                                  { opacity: 0, | ||||
|                                    time: PLYMOUTH_TRANSITION_TIME, | ||||
|                                    transition: 'linear', | ||||
|                                    onComplete: this._fadeBackgroundComplete, | ||||
|                                    onCompleteScope: this }); | ||||
|                 plymouthTransitionRunning = true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (!plymouthTransitionRunning) | ||||
|             this._fadeBackgroundComplete(); | ||||
|     }, | ||||
|  | ||||
|     _fadeBackgroundComplete: function() { | ||||
|         // Don't animate the strut | ||||
|         this._chrome.freezeUpdateRegions(); | ||||
|  | ||||
|         if (this._background != null) { | ||||
|             this._background.destroy(); | ||||
|             this._background = null; | ||||
|         } | ||||
|  | ||||
|         this.panelBox.anchor_y = this.panelBox.height; | ||||
|         Tweener.addTween(this.panelBox, | ||||
|                          { anchor_y: 0, | ||||
|                            time: STARTUP_ANIMATION_TIME, | ||||
| @@ -355,6 +261,7 @@ const LayoutManager = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     showKeyboard: function () { | ||||
|         Main.messageTray.hide(); | ||||
|         this.keyboardBox.raise_top(); | ||||
|         Tweener.addTween(this.keyboardBox, | ||||
|                          { anchor_y: this.keyboardBox.height, | ||||
| @@ -368,8 +275,6 @@ const LayoutManager = new Lang.Class({ | ||||
|                            time: KEYBOARD_ANIMATION_TIME, | ||||
|                            transition: 'easeOutQuad' | ||||
|                          }); | ||||
|  | ||||
|         this.emit('keyboard-visible-changed', true); | ||||
|     }, | ||||
|  | ||||
|     _showKeyboardComplete: function() { | ||||
| @@ -384,6 +289,7 @@ const LayoutManager = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     hideKeyboard: function (immediate) { | ||||
|         Main.messageTray.hide(); | ||||
|         if (this._keyboardHeightNotifyId) { | ||||
|             this.keyboardBox.disconnect(this._keyboardHeightNotifyId); | ||||
|             this._keyboardHeightNotifyId = 0; | ||||
| @@ -400,8 +306,6 @@ const LayoutManager = new Lang.Class({ | ||||
|                            time: immediate ? 0 : KEYBOARD_ANIMATION_TIME, | ||||
|                            transition: 'easeOutQuad' | ||||
|                          }); | ||||
|  | ||||
|         this.emit('keyboard-visible-changed', false); | ||||
|     }, | ||||
|  | ||||
|     _hideKeyboardComplete: function() { | ||||
| @@ -660,6 +564,7 @@ const Chrome = new Lang.Class({ | ||||
|  | ||||
|         this._monitors = []; | ||||
|         this._inOverview = false; | ||||
|         this._isLocked = false; | ||||
|         this._updateRegionIdle = 0; | ||||
|         this._freezeUpdateCount = 0; | ||||
|  | ||||
| @@ -678,9 +583,12 @@ const Chrome = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     init: function() { | ||||
|         Main.overview.connect('showing', Lang.bind(this, this._overviewShowing)); | ||||
|         Main.overview.connect('hidden', Lang.bind(this, this._overviewHidden)); | ||||
|         Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); | ||||
|         Main.overview.connect('showing', | ||||
|                              Lang.bind(this, this._overviewShowing)); | ||||
|         Main.overview.connect('hidden', | ||||
|                              Lang.bind(this, this._overviewHidden)); | ||||
|         Main.screenShield.connect('lock-status-changed', | ||||
|                                   Lang.bind(this, this._lockStatusChanged)); | ||||
|     }, | ||||
|  | ||||
|     addActor: function(actor, params) { | ||||
| @@ -766,13 +674,10 @@ const Chrome = new Lang.Class({ | ||||
|  | ||||
|     _actorReparented: function(actor, oldParent) { | ||||
|         let newParent = actor.get_parent(); | ||||
|         if (!newParent) { | ||||
|         if (!newParent) | ||||
|             this._untrackActor(actor); | ||||
|         } else { | ||||
|             let i = this._findActor(actor); | ||||
|             let actorData = this._trackedActors[i]; | ||||
|         else | ||||
|             actorData.isToplevel = (newParent == Main.uiGroup); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _updateVisibility: function() { | ||||
| @@ -783,7 +688,7 @@ const Chrome = new Lang.Class({ | ||||
|             if (!actorData.isToplevel) | ||||
|                 continue; | ||||
|  | ||||
|             if (this._inOverview || !Main.sessionMode.hasWindows) | ||||
|             if (this._inOverview || this._isLocked) | ||||
|                 visible = true; | ||||
|             else if (this.findMonitorForActor(actorData.actor).inFullscreen) | ||||
|                 visible = false; | ||||
| @@ -805,7 +710,8 @@ const Chrome = new Lang.Class({ | ||||
|         this._queueUpdateRegions(); | ||||
|     }, | ||||
|  | ||||
|     _sessionUpdated: function() { | ||||
|     _lockStatusChanged: function(shield, locked) { | ||||
|         this._isLocked = locked; | ||||
|         this._updateVisibility(); | ||||
|         this._queueUpdateRegions(); | ||||
|     }, | ||||
|   | ||||
| @@ -38,6 +38,7 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' + | ||||
|                     /* Utility functions...we should probably be able to use these | ||||
|                      * in the shell core code too. */ | ||||
|                     'const stage = global.stage; ' + | ||||
|                     'const color = function(pixel) { let c= new Clutter.Color(); c.from_pixel(pixel); return c; }; ' + | ||||
|                     /* Special lookingGlass functions */ | ||||
|                     'const it = Main.lookingGlass.getIt(); ' + | ||||
|                     'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); '; | ||||
| @@ -371,8 +372,7 @@ const ObjInspector = new Lang.Class({ | ||||
|  | ||||
|         this._parentList = []; | ||||
|  | ||||
|         this.actor = new St.ScrollView({ pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }), | ||||
|                                          x_fill: true, y_fill: true }); | ||||
|         this.actor = new St.ScrollView({ x_fill: true, y_fill: true }); | ||||
|         this.actor.get_hscroll_bar().hide(); | ||||
|         this._container = new St.BoxLayout({ name: 'LookingGlassPropertyInspector', | ||||
|                                              style_class: 'lg-dialog', | ||||
| @@ -444,6 +444,10 @@ const ObjInspector = new Lang.Class({ | ||||
|         this.actor.show(); | ||||
|         if (sourceActor) { | ||||
|             this.actor.set_scale(0, 0); | ||||
|             let [sourceX, sourceY] = sourceActor.get_transformed_position(); | ||||
|             let [sourceWidth, sourceHeight] = sourceActor.get_transformed_size(); | ||||
|             this.actor.move_anchor_point(Math.floor(sourceX + sourceWidth / 2), | ||||
|                                          Math.floor(sourceY + sourceHeight / 2)); | ||||
|             Tweener.addTween(this.actor, { scale_x: 1, scale_y: 1, | ||||
|                                            transition: 'easeOutQuad', | ||||
|                                            time: 0.2 }); | ||||
| @@ -865,6 +869,7 @@ const LookingGlass = new Lang.Class({ | ||||
|         let toolbar = new St.BoxLayout({ name: 'Toolbar' }); | ||||
|         this.actor.add_actor(toolbar); | ||||
|         let inspectIcon = new St.Icon({ icon_name: 'gtk-color-picker', | ||||
|                                         icon_type: St.IconType.FULLCOLOR, | ||||
|                                         icon_size: 24 }); | ||||
|         toolbar.add_actor(inspectIcon); | ||||
|         inspectIcon.reactive = true; | ||||
| @@ -1089,8 +1094,8 @@ const LookingGlass = new Lang.Class({ | ||||
|         this.actor.width = myWidth; | ||||
|         this.actor.height = myHeight; | ||||
|         this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8)); | ||||
|         this._objInspector.actor.set_position(primary.x + this.actor.x + Math.floor(myWidth * 0.1), | ||||
|                                               primary.y + this._targetY + Math.floor(myHeight * 0.1)); | ||||
|         this._objInspector.actor.set_position(this.actor.x + Math.floor(myWidth * 0.1), | ||||
|                                               this._targetY + Math.floor(myHeight * 0.1)); | ||||
|     }, | ||||
|  | ||||
|     insertObject: function(obj) { | ||||
|   | ||||
| @@ -12,7 +12,7 @@ const Signals = imports.signals; | ||||
| const Main = imports.ui.main; | ||||
| const MagnifierDBus = imports.ui.magnifierDBus; | ||||
| const Params = imports.misc.params; | ||||
| const PointerWatcher = imports.ui.pointerWatcher; | ||||
|  | ||||
|  | ||||
| const MOUSE_POLL_FREQUENCY = 50; | ||||
| const CROSSHAIRS_CLIP_SIZE = [100, 100]; | ||||
| @@ -56,7 +56,7 @@ const Magnifier = new Lang.Class({ | ||||
|         let xfixesCursor = Shell.XFixesCursor.get_for_stage(global.stage); | ||||
|         this._mouseSprite = new Clutter.Texture(); | ||||
|         xfixesCursor.update_texture_image(this._mouseSprite); | ||||
|         this._cursorRoot = new Clutter.Actor(); | ||||
|         this._cursorRoot = new Clutter.Group(); | ||||
|         this._cursorRoot.add_actor(this._mouseSprite); | ||||
|  | ||||
|         // Create the first ZoomRegion and initialize it according to the | ||||
| @@ -136,8 +136,11 @@ const Magnifier = new Lang.Class({ | ||||
|      * Turn on mouse tracking, if not already doing so. | ||||
|      */ | ||||
|     startTrackingMouse: function() { | ||||
|         if (!this._pointerWatch) | ||||
|             this._pointerWatch = PointerWatcher.getPointerWatcher().addWatch(MOUSE_POLL_FREQUENCY, Lang.bind(this, this.scrollToMousePos)); | ||||
|         if (!this._mouseTrackingId) | ||||
|             this._mouseTrackingId = Mainloop.timeout_add( | ||||
|                 MOUSE_POLL_FREQUENCY, | ||||
|                 Lang.bind(this, this.scrollToMousePos) | ||||
|             ); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
| @@ -145,10 +148,10 @@ const Magnifier = new Lang.Class({ | ||||
|      * Turn off mouse tracking, if not already doing so. | ||||
|      */ | ||||
|     stopTrackingMouse: function() { | ||||
|         if (this._pointerWatch) | ||||
|             this._pointerWatch.remove(); | ||||
|         if (this._mouseTrackingId) | ||||
|             Mainloop.source_remove(this._mouseTrackingId); | ||||
|  | ||||
|         this._pointerWatch = null; | ||||
|         this._mouseTrackingId = null; | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
| @@ -300,7 +303,8 @@ const Magnifier = new Lang.Class({ | ||||
|      */ | ||||
|     setCrosshairsColor: function(color) { | ||||
|         if (this._crossHairs) { | ||||
|             let [res, clutterColor] = Clutter.Color.from_string(color); | ||||
|             let clutterColor = new Clutter.Color(); | ||||
|             clutterColor.from_string(color); | ||||
|             this._crossHairs.setColor(clutterColor); | ||||
|         } | ||||
|     }, | ||||
| @@ -1077,21 +1081,21 @@ const ZoomRegion = new Lang.Class({ | ||||
|         // hide the magnified region from CLUTTER_PICK_ALL | ||||
|         Shell.util_set_hidden_from_pick (this._magView, true); | ||||
|  | ||||
|         // Add a group to clip the contents of the magnified view. | ||||
|         let mainGroup = new Clutter.Actor({ clip_to_allocation: true }); | ||||
|         // Append a Clutter.Group to clip the contents of the magnified view. | ||||
|         let mainGroup = new Clutter.Group({ clip_to_allocation: true }); | ||||
|         this._magView.set_child(mainGroup); | ||||
|  | ||||
|         // Add a background for when the magnified uiGroup is scrolled | ||||
|         // out of view (don't want to see desktop showing through). | ||||
|         this._background = new Clutter.Actor({ background_color: Main.DEFAULT_BACKGROUND_COLOR, | ||||
|                                                width: global.screen_width, | ||||
|                                                height: global.screen_height }); | ||||
|         this._background = new Clutter.Rectangle({ color: Main.DEFAULT_BACKGROUND_COLOR }); | ||||
|         mainGroup.add_actor(this._background); | ||||
|  | ||||
|         // Clone the group that contains all of UI on the screen.  This is the | ||||
|         // chrome, the windows, etc. | ||||
|         this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup }); | ||||
|         mainGroup.add_actor(this._uiGroupClone); | ||||
|         Main.uiGroup.set_size(global.screen_width, global.screen_height); | ||||
|         this._background.set_size(global.screen_width, global.screen_height); | ||||
|  | ||||
|         // Add either the given mouseSourceActor to the ZoomRegion, or a clone of | ||||
|         // it. | ||||
| @@ -1325,7 +1329,7 @@ const ZoomRegion = new Lang.Class({ | ||||
|         this._mouseActor.set_scale(this._xMagFactor, this._yMagFactor); | ||||
|  | ||||
|         let [x, y] = this._screenToViewPort(0, 0); | ||||
|         this._uiGroupClone.set_position(Math.round(x), Math.round(y)); | ||||
|         this._uiGroupClone.set_position(x, y); | ||||
|  | ||||
|         this._updateMousePosition(); | ||||
|     }, | ||||
| @@ -1353,6 +1357,7 @@ const ZoomRegion = new Lang.Class({ | ||||
|         if (!this.isActive()) | ||||
|             return; | ||||
|  | ||||
|         Main.uiGroup.set_size(global.screen_width, global.screen_height); | ||||
|         this._background.set_size(global.screen_width, global.screen_height); | ||||
|  | ||||
|         if (this._screenPosition == GDesktopEnums.MagnifierScreenPosition.NONE) | ||||
| @@ -1376,15 +1381,15 @@ const Crosshairs = new Lang.Class({ | ||||
|         let groupWidth = global.screen_width * 3; | ||||
|         let groupHeight = global.screen_height * 3; | ||||
|  | ||||
|         this._actor = new Clutter.Actor({ | ||||
|         this._actor = new Clutter.Group({ | ||||
|             clip_to_allocation: false, | ||||
|             width: groupWidth, | ||||
|             height: groupHeight | ||||
|         }); | ||||
|         this._horizLeftHair = new Clutter.Actor(); | ||||
|         this._horizRightHair = new Clutter.Actor(); | ||||
|         this._vertTopHair = new Clutter.Actor(); | ||||
|         this._vertBottomHair = new Clutter.Actor(); | ||||
|         this._horizLeftHair = new Clutter.Rectangle(); | ||||
|         this._horizRightHair = new Clutter.Rectangle(); | ||||
|         this._vertTopHair = new Clutter.Rectangle(); | ||||
|         this._vertBottomHair = new Clutter.Rectangle(); | ||||
|         this._actor.add_actor(this._horizLeftHair); | ||||
|         this._actor.add_actor(this._horizRightHair); | ||||
|         this._actor.add_actor(this._vertTopHair); | ||||
| @@ -1455,10 +1460,10 @@ const Crosshairs = new Lang.Class({ | ||||
|      * @clutterColor:   The color as a Clutter.Color. | ||||
|      */ | ||||
|     setColor: function(clutterColor) { | ||||
|         this._horizLeftHair.background_color = clutterColor; | ||||
|         this._horizRightHair.background_color = clutterColor; | ||||
|         this._vertTopHair.background_color = clutterColor; | ||||
|         this._vertBottomHair.background_color = clutterColor; | ||||
|         this._horizLeftHair.set_color(clutterColor); | ||||
|         this._horizRightHair.set_color(clutterColor); | ||||
|         this._vertTopHair.set_color(clutterColor); | ||||
|         this._vertBottomHair.set_color(clutterColor); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
| @@ -1467,7 +1472,9 @@ const Crosshairs = new Lang.Class({ | ||||
|      * @color:  The color as a Clutter.Color. | ||||
|      */ | ||||
|     getColor: function() { | ||||
|         return this._horizLeftHair.get_color(); | ||||
|         let clutterColor = new Clutter.Color(); | ||||
|         this._horizLeftHair.get_color(clutterColor); | ||||
|         return clutterColor; | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|   | ||||
							
								
								
									
										234
									
								
								js/ui/main.js
									
									
									
									
									
								
							
							
						
						
									
										234
									
								
								js/ui/main.js
									
									
									
									
									
								
							| @@ -10,9 +10,12 @@ const Meta = imports.gi.Meta; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const Components = imports.ui.components; | ||||
| const AutomountManager = imports.ui.automountManager; | ||||
| const AutorunManager = imports.ui.autorunManager; | ||||
| const CtrlAltTab = imports.ui.ctrlAltTab; | ||||
| const EndSessionDialog = imports.ui.endSessionDialog; | ||||
| const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent; | ||||
| const KeyringPrompt = imports.ui.keyringPrompt; | ||||
| const Environment = imports.ui.environment; | ||||
| const ExtensionSystem = imports.ui.extensionSystem; | ||||
| const ExtensionDownloader = imports.ui.extensionDownloader; | ||||
| @@ -20,9 +23,11 @@ const Keyboard = imports.ui.keyboard; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const Overview = imports.ui.overview; | ||||
| const Panel = imports.ui.panel; | ||||
| const PlaceDisplay = imports.ui.placeDisplay; | ||||
| const RunDialog = imports.ui.runDialog; | ||||
| const Layout = imports.ui.layout; | ||||
| const LookingGlass = imports.ui.lookingGlass; | ||||
| const NetworkAgent = imports.ui.networkAgent; | ||||
| const NotificationDaemon = imports.ui.notificationDaemon; | ||||
| const WindowAttentionHandler = imports.ui.windowAttentionHandler; | ||||
| const ScreenShield = imports.ui.screenShield; | ||||
| @@ -30,6 +35,7 @@ const Scripting = imports.ui.scripting; | ||||
| const SessionMode = imports.ui.sessionMode; | ||||
| const ShellDBus = imports.ui.shellDBus; | ||||
| const ShellMountOperation = imports.ui.shellMountOperation; | ||||
| const TelepathyClient = imports.ui.telepathyClient; | ||||
| const UnlockDialog = imports.ui.unlockDialog; | ||||
| const WindowManager = imports.ui.windowManager; | ||||
| const Magnifier = imports.ui.magnifier; | ||||
| @@ -37,10 +43,13 @@ const XdndHandler = imports.ui.xdndHandler; | ||||
| const Util = imports.misc.util; | ||||
|  | ||||
| const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides'; | ||||
| const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff); | ||||
| const DEFAULT_BACKGROUND_COLOR = new Clutter.Color(); | ||||
| DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff); | ||||
|  | ||||
| let componentManager = null; | ||||
| let automountManager = null; | ||||
| let autorunManager = null; | ||||
| let panel = null; | ||||
| let hotCorners = []; | ||||
| let overview = null; | ||||
| let runDialog = null; | ||||
| let lookingGlass = null; | ||||
| @@ -49,7 +58,9 @@ let messageTray = null; | ||||
| let screenShield = null; | ||||
| let notificationDaemon = null; | ||||
| let windowAttentionHandler = null; | ||||
| let telepathyClient = null; | ||||
| let ctrlAltTabManager = null; | ||||
| let recorder = null; | ||||
| let sessionMode = null; | ||||
| let shellDBusService = null; | ||||
| let shellMountOpDBusService = null; | ||||
| @@ -61,6 +72,7 @@ let magnifier = null; | ||||
| let xdndHandler = null; | ||||
| let keyboard = null; | ||||
| let layoutManager = null; | ||||
| let networkAgent = null; | ||||
| let _startDate; | ||||
| let _defaultCssStylesheet = null; | ||||
| let _cssStylesheet = null; | ||||
| @@ -68,10 +80,73 @@ let _overridesSettings = null; | ||||
|  | ||||
| let background = null; | ||||
|  | ||||
| function _sessionUpdated() { | ||||
|     Meta.keybindings_set_custom_handler('internal-keybinding-panel-run-dialog', sessionMode.hasRunDialog ? Util.wrapKeybinding(openRunDialog, true) : null); | ||||
|     if (sessionMode.isGreeter) | ||||
|         screenShield.showDialog(); | ||||
| function createUserSession() { | ||||
|     // Load the calendar server. Note that we are careful about | ||||
|     // not loading any events until the user presses the clock | ||||
|     global.launch_calendar_server(); | ||||
|  | ||||
|     telepathyClient = new TelepathyClient.Client(); | ||||
|     automountManager = new AutomountManager.AutomountManager(); | ||||
|     autorunManager = new AutorunManager.AutorunManager(); | ||||
|     networkAgent = new NetworkAgent.NetworkAgent(); | ||||
|  | ||||
|     _initRecorder(); | ||||
| } | ||||
|  | ||||
| function createGDMSession() { | ||||
|     screenShield.showDialog(); | ||||
| } | ||||
|  | ||||
| function createGDMLoginDialog(parentActor) { | ||||
|     // We do this this here instead of at the top to prevent GDM | ||||
|     // related code from getting loaded in normal user sessions | ||||
|     const LoginDialog = imports.gdm.loginDialog; | ||||
|  | ||||
|     let loginDialog = new LoginDialog.LoginDialog(parentActor); | ||||
|     return [loginDialog, true]; | ||||
| } | ||||
|  | ||||
| function createSessionUnlockDialog(parentActor) { | ||||
|     let dialog = new UnlockDialog.UnlockDialog(parentActor); | ||||
|     return [dialog, false]; | ||||
| } | ||||
|  | ||||
| function createInitialSetupSession() { | ||||
|     networkAgent = new NetworkAgent.NetworkAgent(); | ||||
| } | ||||
|  | ||||
| function _initRecorder() { | ||||
|     let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' }); | ||||
|     let desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' }); | ||||
|     let bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' }); | ||||
|  | ||||
|     global.display.add_keybinding('toggle-recording', | ||||
|                                   bindingSettings, | ||||
|                                   Meta.KeyBindingFlags.NONE, function() { | ||||
|         if (recorder == null) { | ||||
|             recorder = new Shell.Recorder({ stage: global.stage }); | ||||
|         } | ||||
|  | ||||
|         if (recorder.is_recording()) { | ||||
|             recorder.close(); | ||||
|             Meta.enable_unredirect_for_screen(global.screen); | ||||
|         } else if (!desktopLockdownSettings.get_boolean('disable-save-to-disk')) { | ||||
|             // read the parameters from GSettings always in case they have changed | ||||
|             recorder.set_framerate(recorderSettings.get_int('framerate')); | ||||
|             /* Translators: this is a filename used for screencast recording */ | ||||
|             // xgettext:no-c-format | ||||
|             recorder.set_filename(_("Screencast from %d %t") + '.' + recorderSettings.get_string('file-extension')); | ||||
|             let pipeline = recorderSettings.get_string('pipeline'); | ||||
|  | ||||
|             if (!pipeline.match(/^\s*$/)) | ||||
|                 recorder.set_pipeline(pipeline); | ||||
|             else | ||||
|                 recorder.set_pipeline(null); | ||||
|  | ||||
|             Meta.disable_unredirect_for_screen(global.screen); | ||||
|             recorder.record(); | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function start() { | ||||
| @@ -137,42 +212,61 @@ function start() { | ||||
|     ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager(); | ||||
|     overview = new Overview.Overview(); | ||||
|     magnifier = new Magnifier.Magnifier(); | ||||
|     if (UnlockDialog.isSupported()) | ||||
|         screenShield = new ScreenShield.ScreenShield(); | ||||
|     else | ||||
|         screenShield = new ScreenShield.ScreenShieldFallback(); | ||||
|     screenShield = new ScreenShield.ScreenShield(); | ||||
|     screenSaverDBus = new ShellDBus.ScreenSaverDBus(); | ||||
|     panel = new Panel.Panel(); | ||||
|     wm = new WindowManager.WindowManager(); | ||||
|     messageTray = new MessageTray.MessageTray(); | ||||
|     keyboard = new Keyboard.Keyboard(); | ||||
|     notificationDaemon = new NotificationDaemon.NotificationDaemon(); | ||||
|     windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler(); | ||||
|     componentManager = new Components.ComponentManager(); | ||||
|  | ||||
|     sessionMode.createSession(); | ||||
|  | ||||
|     panel.startStatusArea(); | ||||
|  | ||||
|     layoutManager.init(); | ||||
|     keyboard.init(); | ||||
|     overview.init(); | ||||
|  | ||||
|     global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, | ||||
|                                             false, -1, 1); | ||||
|     Meta.keybindings_set_custom_handler('internal-keybinding-panel-main-menu', Util.wrapKeybinding(Lang.bind(overview, function() { | ||||
|         this.toggle(); | ||||
|         return true; | ||||
|     }), true)); | ||||
|     global.display.connect('overlay-key', Util.wrapKeybinding(Lang.bind(overview, function() { | ||||
|         this.toggle(); | ||||
|         return true; | ||||
|     }), true)); | ||||
|     if (sessionMode.hasWorkspaces) | ||||
|         global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, | ||||
|                                                 false, -1, 1); | ||||
|  | ||||
|     sessionMode.connect('update', _sessionUpdated); | ||||
|     _sessionUpdated(); | ||||
|     if (sessionMode.allowExtensions) { | ||||
|         ExtensionDownloader.init(); | ||||
|         ExtensionSystem.loadExtensions(); | ||||
|     } | ||||
|  | ||||
|     if (sessionMode.hasRunDialog) { | ||||
|         Meta.keybindings_set_custom_handler('panel-run-dialog', function() { | ||||
|            getRunDialog().open(); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     if (sessionMode.hasOverview) { | ||||
|         Meta.keybindings_set_custom_handler('panel-main-menu', function () { | ||||
|             overview.toggle(); | ||||
|         }); | ||||
|  | ||||
|         global.display.connect('overlay-key', | ||||
|                                Lang.bind(overview, overview.toggle)); | ||||
|     } | ||||
|  | ||||
|     // Provide the bus object for gnome-session to | ||||
|     // initiate logouts. | ||||
|     EndSessionDialog.init(); | ||||
|  | ||||
|     // Attempt to become a PolicyKit authentication agent | ||||
|     PolkitAuthenticationAgent.init() | ||||
|  | ||||
|     // Become a prompter for gnome keyring | ||||
|     KeyringPrompt.init(); | ||||
|  | ||||
|     _startDate = new Date(); | ||||
|  | ||||
|     global.stage.connect('captured-event', _globalKeyPressHandler); | ||||
|  | ||||
|     log('GNOME Shell started at ' + _startDate); | ||||
|  | ||||
|     let perfModuleName = GLib.getenv("SHELL_PERF_MODULE"); | ||||
| @@ -192,9 +286,6 @@ function start() { | ||||
|     global.screen.connect('restacked', _windowsRestacked); | ||||
|  | ||||
|     _nWorkspacesChanged(); | ||||
|  | ||||
|     ExtensionDownloader.init(); | ||||
|     ExtensionSystem.init(); | ||||
| } | ||||
|  | ||||
| let _workspaces = []; | ||||
| @@ -418,6 +509,9 @@ function loadTheme() { | ||||
|  | ||||
|     let theme = new St.Theme ({ application_stylesheet: cssStylesheet }); | ||||
|  | ||||
|     if (sessionMode.extraStylesheet) | ||||
|         theme.load_stylesheet(sessionMode.extraStylesheet); | ||||
|  | ||||
|     if (previousTheme) { | ||||
|         let customStylesheets = previousTheme.get_custom_stylesheets(); | ||||
|  | ||||
| @@ -438,6 +532,7 @@ function notify(msg, details) { | ||||
|     messageTray.add(source); | ||||
|     let notification = new MessageTray.Notification(source, msg, details); | ||||
|     notification.setTransient(true); | ||||
|     notification.setShowWhenLocked(true); | ||||
|     source.notify(notification); | ||||
| } | ||||
|  | ||||
| @@ -469,6 +564,85 @@ function getWindowActorsForWorkspace(workspaceIndex) { | ||||
|     }); | ||||
| } | ||||
|  | ||||
| // This function encapsulates hacks to make certain global keybindings | ||||
| // work even when we are in one of our modes where global keybindings | ||||
| // are disabled with a global grab. (When there is a global grab, then | ||||
| // all key events will be delivered to the stage, so ::captured-event | ||||
| // on the stage can be used for global keybindings.) | ||||
| function _globalKeyPressHandler(actor, event) { | ||||
|     if (modalCount == 0) | ||||
|         return false; | ||||
|     if (event.type() != Clutter.EventType.KEY_PRESS) | ||||
|         return false; | ||||
|  | ||||
|     if (!sessionMode.allowKeybindingsWhenModal) { | ||||
|         if (modalCount > (overview.visible ? 1 : 0)) | ||||
|             return false; | ||||
|     } | ||||
|  | ||||
|     let symbol = event.get_key_symbol(); | ||||
|     let keyCode = event.get_key_code(); | ||||
|     let ignoredModifiers = global.display.get_ignored_modifier_mask(); | ||||
|     let modifierState = event.get_state() & ~ignoredModifiers; | ||||
|  | ||||
|     // This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType | ||||
|     let action = global.display.get_keybinding_action(keyCode, modifierState); | ||||
|  | ||||
|     // This isn't a Meta.KeyBindingAction yet | ||||
|     if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) { | ||||
|         overview.hide(); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     if (action == Meta.KeyBindingAction.SWITCH_PANELS) { | ||||
|         ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK, | ||||
|                                 modifierState); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     switch (action) { | ||||
|         // left/right would effectively act as synonyms for up/down if we enabled them; | ||||
|         // but that could be considered confusing; we also disable them in the main view. | ||||
|         // | ||||
|         // case Meta.KeyBindingAction.WORKSPACE_LEFT: | ||||
|         //  if (!sessionMode.hasWorkspaces) | ||||
|         //      return false; | ||||
|         // | ||||
|         //     wm.actionMoveWorkspaceLeft(); | ||||
|         //     return true; | ||||
|         // case Meta.KeyBindingAction.WORKSPACE_RIGHT: | ||||
|         //  if (!sessionMode.hasWorkspaces) | ||||
|         //      return false; | ||||
|         // | ||||
|         //     wm.actionMoveWorkspaceRight(); | ||||
|         //     return true; | ||||
|         case Meta.KeyBindingAction.WORKSPACE_UP: | ||||
|             if (!sessionMode.hasWorkspaces) | ||||
|                 return false; | ||||
|  | ||||
|             wm.actionMoveWorkspace(Meta.MotionDirection.UP); | ||||
|             return true; | ||||
|         case Meta.KeyBindingAction.WORKSPACE_DOWN: | ||||
|             if (!sessionMode.hasWorkspaces) | ||||
|                 return false; | ||||
|  | ||||
|             wm.actionMoveWorkspace(Meta.MotionDirection.DOWN); | ||||
|             return true; | ||||
|         case Meta.KeyBindingAction.PANEL_RUN_DIALOG: | ||||
|         case Meta.KeyBindingAction.COMMAND_2: | ||||
|             if (!sessionMode.hasRunDialog) | ||||
|                 return false; | ||||
|  | ||||
|             getRunDialog().open(); | ||||
|             return true; | ||||
|         case Meta.KeyBindingAction.PANEL_MAIN_MENU: | ||||
|             overview.hide(); | ||||
|             return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| function _findModal(actor) { | ||||
|     for (let i = 0; i < modalActorFocusStack.length; i++) { | ||||
|         if (modalActorFocusStack[i].actor == actor) | ||||
| @@ -604,13 +778,11 @@ function createLookingGlass() { | ||||
|     return lookingGlass; | ||||
| } | ||||
|  | ||||
| function openRunDialog() { | ||||
| function getRunDialog() { | ||||
|     if (runDialog == null) { | ||||
|         runDialog = new RunDialog.RunDialog(); | ||||
|     } | ||||
|     runDialog.open(); | ||||
|  | ||||
|     return true; | ||||
|     return runDialog; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   | ||||
							
								
								
									
										1381
									
								
								js/ui/messageTray.js
									
									
									
									
									
								
							
							
						
						
									
										1381
									
								
								js/ui/messageTray.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -14,12 +14,12 @@ const Atk = imports.gi.Atk; | ||||
|  | ||||
| const Params = imports.misc.params; | ||||
|  | ||||
| const Layout = imports.ui.layout; | ||||
| const Lightbox = imports.ui.lightbox; | ||||
| const Main = imports.ui.main; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const OPEN_AND_CLOSE_TIME = 0.1; | ||||
| const FADE_IN_BUTTONS_TIME = 0.33; | ||||
| const FADE_OUT_DIALOG_TIME = 1.0; | ||||
|  | ||||
| const State = { | ||||
| @@ -36,13 +36,12 @@ const ModalDialog = new Lang.Class({ | ||||
|     _init: function(params) { | ||||
|         params = Params.parse(params, { shellReactive: false, | ||||
|                                         styleClass: null, | ||||
|                                         parentActor: Main.uiGroup, | ||||
|                                         shouldFadeIn: true }); | ||||
|                                         parentActor: Main.uiGroup | ||||
|                                       }); | ||||
|  | ||||
|         this.state = State.CLOSED; | ||||
|         this._hasModal = false; | ||||
|         this._shellReactive = params.shellReactive; | ||||
|         this._shouldFadeIn = params.shouldFadeIn; | ||||
|  | ||||
|         this._group = new St.Widget({ visible: false, | ||||
|                                       x: 0, | ||||
| @@ -60,14 +59,12 @@ const ModalDialog = new Lang.Class({ | ||||
|         this._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent)); | ||||
|  | ||||
|         this._backgroundBin = new St.Bin(); | ||||
|         this._monitorConstraint = new Layout.MonitorConstraint(); | ||||
|         this._backgroundBin.add_constraint(this._monitorConstraint); | ||||
|         this._group.add_actor(this._backgroundBin); | ||||
|  | ||||
|         this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog', | ||||
|                                                vertical:    true }); | ||||
|         this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog', | ||||
|                                                 vertical:    true }); | ||||
|         if (params.styleClass != null) { | ||||
|             this.dialogLayout.add_style_class_name(params.styleClass); | ||||
|             this._dialogLayout.add_style_class_name(params.styleClass); | ||||
|         } | ||||
|  | ||||
|         if (!this._shellReactive) { | ||||
| @@ -80,29 +77,29 @@ const ModalDialog = new Lang.Class({ | ||||
|  | ||||
|             this._eventBlocker = new Clutter.Group({ reactive: true }); | ||||
|             stack.add_actor(this._eventBlocker); | ||||
|             stack.add_actor(this.dialogLayout); | ||||
|             stack.add_actor(this._dialogLayout); | ||||
|         } else { | ||||
|             this._backgroundBin.child = this.dialogLayout; | ||||
|             this._backgroundBin.child = this._dialogLayout; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         this.contentLayout = new St.BoxLayout({ vertical: true }); | ||||
|         this.dialogLayout.add(this.contentLayout, | ||||
|                               { x_fill:  true, | ||||
|                                 y_fill:  true, | ||||
|                                 x_align: St.Align.MIDDLE, | ||||
|                                 y_align: St.Align.START }); | ||||
|         this._dialogLayout.add(this.contentLayout, | ||||
|                                { x_fill:  true, | ||||
|                                  y_fill:  true, | ||||
|                                  x_align: St.Align.MIDDLE, | ||||
|                                  y_align: St.Align.START }); | ||||
|  | ||||
|         this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box', | ||||
|                                                 visible:     false, | ||||
|                                                 vertical:    false }); | ||||
|         this.dialogLayout.add(this._buttonLayout, | ||||
|                               { expand:  true, | ||||
|                                 x_align: St.Align.MIDDLE, | ||||
|                                 y_align: St.Align.END }); | ||||
|         this._dialogLayout.add(this._buttonLayout, | ||||
|                                { expand:  true, | ||||
|                                  x_align: St.Align.MIDDLE, | ||||
|                                  y_align: St.Align.END }); | ||||
|  | ||||
|         global.focus_manager.add_group(this.dialogLayout); | ||||
|         this._initialKeyFocus = this.dialogLayout; | ||||
|         global.focus_manager.add_group(this._dialogLayout); | ||||
|         this._initialKeyFocus = this._dialogLayout; | ||||
|         this._initialKeyFocusDestroyId = 0; | ||||
|         this._savedKeyFocus = null; | ||||
|     }, | ||||
| @@ -116,6 +113,8 @@ const ModalDialog = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     setButtons: function(buttons) { | ||||
|         let hadChildren = this._buttonLayout.get_children() > 0; | ||||
|  | ||||
|         this._buttonLayout.destroy_all_children(); | ||||
|         this._actionKeys = {}; | ||||
|  | ||||
| @@ -128,11 +127,8 @@ const ModalDialog = new Lang.Class({ | ||||
|             let key = buttonInfo['key']; | ||||
|             let isDefault = buttonInfo['default']; | ||||
|  | ||||
|             if (isDefault && !key) { | ||||
|                 this._actionKeys[Clutter.KEY_KP_Enter] = action; | ||||
|                 this._actionKeys[Clutter.KEY_ISO_Enter] = action; | ||||
|             if (isDefault && !key) | ||||
|                 key = Clutter.KEY_Return; | ||||
|             } | ||||
|  | ||||
|             buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button', | ||||
|                                                 reactive:    true, | ||||
| @@ -166,7 +162,21 @@ const ModalDialog = new Lang.Class({ | ||||
|                 this._actionKeys[key] = action; | ||||
|         } | ||||
|  | ||||
|         this.emit('buttons-set'); | ||||
|         // Fade in buttons if there weren't any before | ||||
|         if (!hadChildren && buttons.length > 0) { | ||||
|             this._buttonLayout.opacity = 0; | ||||
|             Tweener.addTween(this._buttonLayout, | ||||
|                              { opacity: 255, | ||||
|                                time: FADE_IN_BUTTONS_TIME, | ||||
|                                transition: 'easeOutQuad', | ||||
|                                onComplete: Lang.bind(this, function() { | ||||
|                                    this.emit('buttons-set'); | ||||
|                                }) | ||||
|                              }); | ||||
|         } else { | ||||
|             this.emit('buttons-set'); | ||||
|         } | ||||
|  | ||||
|     }, | ||||
|  | ||||
|     _onKeyReleaseEvent: function(object, event) { | ||||
| @@ -185,22 +195,22 @@ const ModalDialog = new Lang.Class({ | ||||
|         this.emit('destroy'); | ||||
|     }, | ||||
|  | ||||
|     _fadeOpen: function(onPrimary) { | ||||
|         if (onPrimary) | ||||
|             this._monitorConstraint.primary = true; | ||||
|         else | ||||
|             this._monitorConstraint.index = global.screen.get_current_monitor(); | ||||
|     _fadeOpen: function() { | ||||
|         let monitor = Main.layoutManager.currentMonitor; | ||||
|  | ||||
|         this._backgroundBin.set_position(monitor.x, monitor.y); | ||||
|         this._backgroundBin.set_size(monitor.width, monitor.height); | ||||
|  | ||||
|         this.state = State.OPENING; | ||||
|  | ||||
|         this.dialogLayout.opacity = 255; | ||||
|         this._dialogLayout.opacity = 255; | ||||
|         if (this._lightbox) | ||||
|             this._lightbox.show(); | ||||
|         this._group.opacity = 0; | ||||
|         this._group.show(); | ||||
|         Tweener.addTween(this._group, | ||||
|                          { opacity: 255, | ||||
|                            time: this._shouldFadeIn ? OPEN_AND_CLOSE_TIME : 0, | ||||
|                            time: OPEN_AND_CLOSE_TIME, | ||||
|                            transition: 'easeOutQuad', | ||||
|                            onComplete: Lang.bind(this, | ||||
|                                function() { | ||||
| @@ -217,19 +227,19 @@ const ModalDialog = new Lang.Class({ | ||||
|         this._initialKeyFocus = actor; | ||||
|  | ||||
|         this._initialKeyFocusDestroyId = actor.connect('destroy', Lang.bind(this, function() { | ||||
|             this._initialKeyFocus = this.dialogLayout; | ||||
|             this._initialKeyFocus = this._dialogLayout; | ||||
|             this._initialKeyFocusDestroyId = 0; | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     open: function(timestamp, onPrimary) { | ||||
|     open: function(timestamp) { | ||||
|         if (this.state == State.OPENED || this.state == State.OPENING) | ||||
|             return true; | ||||
|  | ||||
|         if (!this.pushModal(timestamp)) | ||||
|             return false; | ||||
|  | ||||
|         this._fadeOpen(onPrimary); | ||||
|         this._fadeOpen(); | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
| @@ -310,7 +320,7 @@ const ModalDialog = new Lang.Class({ | ||||
|             return; | ||||
|  | ||||
|         this.popModal(timestamp); | ||||
|         Tweener.addTween(this.dialogLayout, | ||||
|         Tweener.addTween(this._dialogLayout, | ||||
|                          { opacity: 0, | ||||
|                            time:    FADE_OUT_DIALOG_TIME, | ||||
|                            transition: 'easeOutQuad', | ||||
|   | ||||
| @@ -1,4 +1,23 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | ||||
| /* | ||||
|  * Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com> | ||||
|  * | ||||
|  * 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, 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., 59 Temple Place - Suite 330, Boston, MA | ||||
|  * 02111-1307, USA. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gio = imports.gi.Gio; | ||||
| @@ -116,10 +135,7 @@ const NetworkSecretDialog = new Lang.Class({ | ||||
|             } else | ||||
|                 secret.valid = true; | ||||
| 
 | ||||
|             secretTable.add(label, { row: pos, col: 0, | ||||
|                                      x_expand: false, x_fill: true, | ||||
|                                      x_align: St.Align.START, | ||||
|                                      y_fill: false, y_align: St.Align.MIDDLE }); | ||||
|             secretTable.add(label, { row: pos, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START, y_align: St.Align.START }); | ||||
|             secretTable.add(secret.entry, { row: pos, col: 1, x_expand: true, x_fill: true, y_align: St.Align.END }); | ||||
|             pos++; | ||||
| 
 | ||||
| @@ -149,7 +165,6 @@ const NetworkSecretDialog = new Lang.Class({ | ||||
|         } | ||||
| 
 | ||||
|         this._okButton.button.reactive = valid; | ||||
|         this._okButton.button.can_focus = valid; | ||||
|     }, | ||||
| 
 | ||||
|     _onOk: function() { | ||||
| @@ -408,10 +423,7 @@ const VPNRequestHandler = new Lang.Class({ | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     cancel: function(respond) { | ||||
|         if (respond) | ||||
|             this._agent.respond(this._requestId, Shell.NetworkAgentResponse.USER_CANCELED); | ||||
| 
 | ||||
|     cancel: function() { | ||||
|         if (this._newStylePlugin && this._shellDialog) { | ||||
|             this._shellDialog.close(global.get_current_time()); | ||||
|             this._shellDialog.destroy(); | ||||
| @@ -587,7 +599,7 @@ const NetworkAgent = new Lang.Class({ | ||||
|     Name: 'NetworkAgent', | ||||
| 
 | ||||
|     _init: function() { | ||||
|         this._native = new Shell.NetworkAgent({ auto_register: false, | ||||
|         this._native = new Shell.NetworkAgent({ auto_register: true, | ||||
|                                                 identifier: 'org.gnome.Shell.NetworkAgent' }); | ||||
| 
 | ||||
|         this._dialogs = { }; | ||||
| @@ -597,24 +609,6 @@ const NetworkAgent = new Lang.Class({ | ||||
|         this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest)); | ||||
|     }, | ||||
| 
 | ||||
|     enable: function() { | ||||
|         this._native.register(); | ||||
|     }, | ||||
| 
 | ||||
|     disable: function() { | ||||
|         let requestId; | ||||
| 
 | ||||
|         for (requestId in this._dialogs) | ||||
|             this._dialogs[requestId].cancel(); | ||||
|         this._dialogs = { }; | ||||
| 
 | ||||
|         for (requestId in this._vpnRequests) | ||||
|             this._vpnRequests[requestId].cancel(true); | ||||
|         this._vpnRequests = { }; | ||||
| 
 | ||||
|         this._native.unregister(); | ||||
|     }, | ||||
| 
 | ||||
|     _newRequest:  function(agent, requestId, connection, settingName, hints, flags) { | ||||
|         if (settingName == 'vpn') { | ||||
|             this._vpnRequest(requestId, connection, hints, flags); | ||||
| @@ -635,7 +629,7 @@ const NetworkAgent = new Lang.Class({ | ||||
|             this._dialogs[requestId].destroy(); | ||||
|             delete this._dialogs[requestId]; | ||||
|         } else if (this._vpnRequests[requestId]) { | ||||
|             this._vpnRequests[requestId].cancel(false); | ||||
|             this._vpnRequests[requestId].cancel(); | ||||
|             delete this._vpnRequests[requestId]; | ||||
|         } | ||||
|     }, | ||||
| @@ -704,4 +698,3 @@ const NetworkAgent = new Lang.Class({ | ||||
|         } | ||||
|     } | ||||
| }); | ||||
| const Component = NetworkAgent; | ||||
| @@ -116,8 +116,8 @@ const NotificationDaemon = new Lang.Class({ | ||||
|         this._busProxy = new Bus(); | ||||
|  | ||||
|         this._trayManager = new Shell.TrayManager(); | ||||
|         this._trayIconAddedId = this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded)); | ||||
|         this._trayIconRemovedId = this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved)); | ||||
|         this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded)); | ||||
|         this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved)); | ||||
|  | ||||
|         Shell.WindowTracker.get_default().connect('notify::focus-app', | ||||
|             Lang.bind(this, this._onFocusAppChanged)); | ||||
| @@ -256,7 +256,6 @@ const NotificationDaemon = new Lang.Class({ | ||||
|             Mainloop.idle_add(Lang.bind(this, | ||||
|                                         function () { | ||||
|                                             this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED); | ||||
|                                             return false; | ||||
|                                         })); | ||||
|             return invocation.return_value(GLib.Variant.new('(u)', [id])); | ||||
|         } | ||||
| @@ -356,10 +355,13 @@ const NotificationDaemon = new Lang.Class({ | ||||
|              ndata.actions, ndata.hints, ndata.notification]; | ||||
|  | ||||
|         let gicon = this._iconForNotificationData(icon, hints); | ||||
|         let iconActor = new St.Icon({ gicon: gicon, | ||||
|                                       icon_type: St.IconType.FULLCOLOR, | ||||
|                                       icon_size: source.ICON_SIZE }); | ||||
|  | ||||
|         if (notification == null) { | ||||
|             notification = new MessageTray.Notification(source, summary, body, | ||||
|                                                         { gicon: gicon, | ||||
|                                                         { icon: iconActor, | ||||
|                                                           bannerMarkup: true }); | ||||
|             ndata.notification = notification; | ||||
|             notification.connect('destroy', Lang.bind(this, | ||||
| @@ -384,7 +386,7 @@ const NotificationDaemon = new Lang.Class({ | ||||
|                     this._emitActionInvoked(ndata.id, actionId); | ||||
|                 })); | ||||
|         } else { | ||||
|             notification.update(summary, body, { gicon: gicon, | ||||
|             notification.update(summary, body, { icon: iconActor, | ||||
|                                                  bannerMarkup: true, | ||||
|                                                  clear: true }); | ||||
|         } | ||||
| @@ -497,7 +499,7 @@ const NotificationDaemon = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onTrayIconAdded: function(o, icon) { | ||||
|         let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : ''; | ||||
|         let wmClass = icon.wm_class.toLowerCase(); | ||||
|         if (STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass] !== undefined) | ||||
|             return; | ||||
|  | ||||
| @@ -581,12 +583,18 @@ const Source = new Lang.Class({ | ||||
|             this.notifications.length > 0) | ||||
|             return false; | ||||
|  | ||||
|         let id = global.connect('notify::stage-input-mode', Lang.bind(this, function () { | ||||
|             global.disconnect(id); | ||||
|         if (Main.overview.visible) { | ||||
|             // We can't just connect to Main.overview's 'hidden' signal, | ||||
|             // because it's emitted *before* it calls popModal()... | ||||
|             let id = global.connect('notify::stage-input-mode', Lang.bind(this, | ||||
|                 function () { | ||||
|                     global.disconnect(id); | ||||
|                     this.trayIcon.click(event); | ||||
|                 })); | ||||
|             Main.overview.hide(); | ||||
|         } else { | ||||
|             this.trayIcon.click(event); | ||||
|         })); | ||||
|  | ||||
|         Main.overview.hide(); | ||||
|         } | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
| @@ -622,16 +630,6 @@ const Source = new Lang.Class({ | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     setTitle: function(title) { | ||||
|         // Do nothing if .app is set, we don't want to override the | ||||
|         // app name with whatever is provided through libnotify (usually | ||||
|         // garbage) | ||||
|         if (this.app) | ||||
|             return; | ||||
|  | ||||
|         this.parent(title); | ||||
|     }, | ||||
|  | ||||
|     open: function(notification) { | ||||
|         this.destroyNonResidentNotifications(); | ||||
|         this.openApp(); | ||||
|   | ||||
| @@ -10,42 +10,29 @@ const St = imports.gi.St; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Gdk = imports.gi.Gdk; | ||||
|  | ||||
| const AppDisplay = imports.ui.appDisplay; | ||||
| const Dash = imports.ui.dash; | ||||
| const DND = imports.ui.dnd; | ||||
| const Main = imports.ui.main; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const Panel = imports.ui.panel; | ||||
| const Params = imports.misc.params; | ||||
| const PlaceDisplay = imports.ui.placeDisplay; | ||||
| const RemoteSearch = imports.ui.remoteSearch; | ||||
| const Tweener = imports.ui.tweener; | ||||
| const ViewSelector = imports.ui.viewSelector; | ||||
| const Wanda = imports.ui.wanda; | ||||
| const WorkspacesView = imports.ui.workspacesView; | ||||
| const WorkspaceThumbnail = imports.ui.workspaceThumbnail; | ||||
|  | ||||
| // Time for initial animation going into Overview mode | ||||
| const ANIMATION_TIME = 0.25; | ||||
|  | ||||
| // XXX -- grab this automatically. Hard to do. | ||||
| const DASH_MAX_WIDTH = 96; | ||||
| // We split the screen vertically between the dash and the view selector. | ||||
| const DASH_SPLIT_FRACTION = 0.1; | ||||
|  | ||||
| const DND_WINDOW_SWITCH_TIMEOUT = 1250; | ||||
|  | ||||
| const GLSL_DIM_EFFECT_DECLARATIONS = ''; | ||||
| const GLSL_DIM_EFFECT_CODE = '\ | ||||
|    vec2 dist = cogl_tex_coord_in[0].xy - vec2(0.5, 0.5); \ | ||||
|    float elipse_radius = 0.5; \ | ||||
|    /* from https://bugzilla.gnome.org/show_bug.cgi?id=669798: \ | ||||
|       the alpha on the gradient goes from 165 at its darkest to 98 at its most transparent. */ \ | ||||
|    float y = 165.0 / 255.0; \ | ||||
|    float x = 98.0 / 255.0; \ | ||||
|    /* interpolate darkening value, based on distance from screen center */ \ | ||||
|    float val = min(length(dist), elipse_radius); \ | ||||
|    float a = mix(x, y, val / elipse_radius); \ | ||||
|    /* dim_factor varies from [1.0 -> 0.5] when overview is showing \ | ||||
|       We use it to smooth value, then we clamp it to valid color interval */ \ | ||||
|    a = clamp(a - cogl_color_in.r + 0.5, 0.0, 1.0); \ | ||||
|    /* We\'re blending between: color and black color (obviously omitted in the equation) */ \ | ||||
|    cogl_color_out.xyz = cogl_color_out.xyz * (1.0 - a); \ | ||||
|    cogl_color_out.a = 1.0;'; | ||||
|  | ||||
| const SwipeScrollDirection = { | ||||
|     NONE: 0, | ||||
|     HORIZONTAL: 1, | ||||
| @@ -75,14 +62,7 @@ const ShellInfo = new Lang.Class({ | ||||
|             this._source.destroy(); | ||||
|     }, | ||||
|  | ||||
|     setMessage: function(text, options) { | ||||
|         options = Params.parse(options, { undoCallback: null, | ||||
|                                           forFeedback: false | ||||
|                                         }); | ||||
|  | ||||
|         let undoCallback = options.undoCallback; | ||||
|         let forFeedback = options.forFeedback; | ||||
|  | ||||
|     setMessage: function(text, undoCallback, undoLabel) { | ||||
|         if (this._source == null) { | ||||
|             this._source = new MessageTray.SystemNotificationSource(); | ||||
|             this._source.connect('destroy', Lang.bind(this, | ||||
| @@ -95,17 +75,20 @@ const ShellInfo = new Lang.Class({ | ||||
|         let notification = null; | ||||
|         if (this._source.notifications.length == 0) { | ||||
|             notification = new MessageTray.Notification(this._source, text, null); | ||||
|             notification.setTransient(true); | ||||
|             notification.setForFeedback(forFeedback); | ||||
|             notification.setShowWhenLocked(true); | ||||
|         } else { | ||||
|             notification = this._source.notifications[0]; | ||||
|             notification.update(text, null, { clear: true }); | ||||
|         } | ||||
|  | ||||
|         notification.setTransient(true); | ||||
|  | ||||
|         this._undoCallback = undoCallback; | ||||
|         if (undoCallback) { | ||||
|             notification.addButton('system-undo', _("Undo")); | ||||
|             notification.connect('action-invoked', Lang.bind(this, this._onUndoClicked)); | ||||
|             notification.addButton('system-undo', | ||||
|                                    undoLabel ? undoLabel : _("Undo")); | ||||
|             notification.connect('action-invoked', | ||||
|                                  Lang.bind(this, this._onUndoClicked)); | ||||
|         } | ||||
|  | ||||
|         this._source.notify(notification); | ||||
| @@ -115,21 +98,16 @@ const ShellInfo = new Lang.Class({ | ||||
| const Overview = new Lang.Class({ | ||||
|     Name: 'Overview', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._overviewCreated = false; | ||||
|     _init : function() { | ||||
|         this.isDummy = !Main.sessionMode.hasOverview; | ||||
|  | ||||
|         Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); | ||||
|         this._sessionUpdated(); | ||||
|     }, | ||||
|  | ||||
|     _createOverview: function() { | ||||
|         if (this._overviewCreated) | ||||
|         // We only have an overview in user sessions, so | ||||
|         // create a dummy overview in other cases | ||||
|         if (this.isDummy) { | ||||
|             this.animationInProgress = false; | ||||
|             this.visible = false; | ||||
|             return; | ||||
|  | ||||
|         if (this.isDummy) | ||||
|             return; | ||||
|  | ||||
|         this._overviewCreated = true; | ||||
|         } | ||||
|  | ||||
|         // The main BackgroundActor is inside global.window_group which is | ||||
|         // hidden when displaying the overview, so we create a new | ||||
| @@ -137,10 +115,6 @@ const Overview = new Lang.Class({ | ||||
|         // scenes which allows us to show the background with different | ||||
|         // rendering options without duplicating the texture data. | ||||
|         this._background = Meta.BackgroundActor.new_for_screen(global.screen); | ||||
|         this._background.add_glsl_snippet(Meta.SnippetHook.FRAGMENT, | ||||
|                                           GLSL_DIM_EFFECT_DECLARATIONS, | ||||
|                                           GLSL_DIM_EFFECT_CODE, | ||||
|                                           false); | ||||
|         this._background.hide(); | ||||
|         global.overlay_group.add_actor(this._background); | ||||
|  | ||||
| @@ -170,6 +144,8 @@ const Overview = new Lang.Class({ | ||||
|         this._capturedEventId = 0; | ||||
|         this._buttonPressId = 0; | ||||
|  | ||||
|         this._workspacesDisplay = null; | ||||
|  | ||||
|         this.visible = false;           // animating to overview, in overview, animating out | ||||
|         this._shown = false;            // show() and not hide() | ||||
|         this._shownTemporarily = false; // showTemporarily() and not hideTemporarily() | ||||
| @@ -206,11 +182,6 @@ const Overview = new Lang.Class({ | ||||
|         this._needsFakePointerEvent = false; | ||||
|     }, | ||||
|  | ||||
|     _sessionUpdated: function() { | ||||
|         this.isDummy = !Main.sessionMode.hasOverview; | ||||
|         this._createOverview(); | ||||
|     }, | ||||
|  | ||||
|     // The members we construct that are implemented in JS might | ||||
|     // want to access the overview as Main.overview to connect | ||||
|     // signal handlers and so forth. So we create them after | ||||
| @@ -221,23 +192,29 @@ const Overview = new Lang.Class({ | ||||
|  | ||||
|         this._shellInfo = new ShellInfo(); | ||||
|  | ||||
|         this._searchEntry = new St.Entry({ name: 'searchEntry', | ||||
|                                            /* Translators: this is the text displayed | ||||
|                                               in the search entry when no search is | ||||
|                                               active; it should not exceed ~30 | ||||
|                                               characters. */ | ||||
|                                            hint_text: _("Type to search..."), | ||||
|                                            track_hover: true, | ||||
|                                            can_focus: true }); | ||||
|         this._group.add_actor(this._searchEntry); | ||||
|  | ||||
|         this._dash = new Dash.Dash(); | ||||
|         this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry, | ||||
|                                                            this._dash.showAppsButton); | ||||
|         this._viewSelector = new ViewSelector.ViewSelector(); | ||||
|         this._group.add_actor(this._viewSelector.actor); | ||||
|         this._group.add_actor(this._dash.actor); | ||||
|  | ||||
|         this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay(); | ||||
|         this._viewSelector.addViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic'); | ||||
|  | ||||
|         let appView = new AppDisplay.AllAppDisplay(); | ||||
|         this._viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run'); | ||||
|  | ||||
|         // Default search providers | ||||
|         // Wanda comes obviously first | ||||
|         this.addSearchProvider(new Wanda.WandaSearchProvider()); | ||||
|         this.addSearchProvider(new AppDisplay.AppSearchProvider()); | ||||
|         this.addSearchProvider(new AppDisplay.SettingsSearchProvider()); | ||||
|         this.addSearchProvider(new PlaceDisplay.PlaceSearchProvider()); | ||||
|  | ||||
|         // Load remote search providers provided by applications | ||||
|         RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider)); | ||||
|  | ||||
|         // TODO - recalculate everything when desktop size changes | ||||
|         this._dash = new Dash.Dash(); | ||||
|         this._group.add_actor(this._dash.actor); | ||||
|         this._dash.actor.add_constraint(this._viewSelector.constrainY); | ||||
|         this._dash.actor.add_constraint(this._viewSelector.constrainHeight); | ||||
|         this.dashIconSize = this._dash.iconSize; | ||||
|         this._dash.connect('icon-size-changed', | ||||
| @@ -247,7 +224,7 @@ const Overview = new Lang.Class({ | ||||
|  | ||||
|         // Translators: this is the name of the dock/favorites area on | ||||
|         // the left of the overview | ||||
|         Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks-symbolic'); | ||||
|         Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks'); | ||||
|  | ||||
|         Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout)); | ||||
|         this._relayout(); | ||||
| @@ -261,16 +238,11 @@ const Overview = new Lang.Class({ | ||||
|         this._viewSelector.removeSearchProvider(provider); | ||||
|     }, | ||||
|  | ||||
|     // | ||||
|     // options: | ||||
|     //  - undoCallback (function): the callback to be called if undo support is needed | ||||
|     //  - forFeedback (boolean): whether the message is for direct feedback of a user action | ||||
|     // | ||||
|     setMessage: function(text, options) { | ||||
|     setMessage: function(text, undoCallback, undoLabel) { | ||||
|         if (this.isDummy) | ||||
|             return; | ||||
|  | ||||
|         this._shellInfo.setMessage(text, options); | ||||
|         this._shellInfo.setMessage(text, undoCallback, undoLabel); | ||||
|     }, | ||||
|  | ||||
|     _onDragBegin: function() { | ||||
| @@ -503,9 +475,7 @@ const Overview = new Lang.Class({ | ||||
|         if (windows.length == 0) | ||||
|             return null; | ||||
|  | ||||
|         let window = windows[0]; | ||||
|         let clone = new Clutter.Clone({ source: window.get_texture(), | ||||
|                                         x: window.x, y: window.y }); | ||||
|         let clone = new Clutter.Clone({ source: windows[0].get_texture() }); | ||||
|         clone.source.connect('destroy', Lang.bind(this, function() { | ||||
|             clone.destroy(); | ||||
|         })); | ||||
| @@ -530,13 +500,13 @@ const Overview = new Lang.Class({ | ||||
|         this._coverPane.set_position(0, contentY); | ||||
|         this._coverPane.set_size(primary.width, contentHeight); | ||||
|  | ||||
|         let searchWidth = this._searchEntry.get_width(); | ||||
|         let searchHeight = this._searchEntry.get_height(); | ||||
|         let searchX = (primary.width - searchWidth) / 2; | ||||
|         let searchY = contentY + this._spacing; | ||||
|         let dashWidth = Math.round(DASH_SPLIT_FRACTION * primary.width); | ||||
|         let viewWidth = primary.width - dashWidth - this._spacing; | ||||
|         let viewHeight = contentHeight - 2 * this._spacing; | ||||
|         let viewY = contentY + this._spacing; | ||||
|         let viewX = rtl ? 0 : dashWidth + this._spacing; | ||||
|  | ||||
|         let dashWidth = DASH_MAX_WIDTH; | ||||
|         let dashY = searchY + searchHeight + this._spacing; | ||||
|         // Set the dash's x position - y is handled by a constraint | ||||
|         let dashX; | ||||
|         if (rtl) { | ||||
|             this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST); | ||||
| @@ -544,14 +514,8 @@ const Overview = new Lang.Class({ | ||||
|         } else { | ||||
|             dashX = 0; | ||||
|         } | ||||
|         this._dash.actor.set_x(dashX); | ||||
|  | ||||
|         let viewX = rtl ? 0 : dashWidth + this._spacing; | ||||
|         let viewY = searchY + searchHeight + this._spacing; | ||||
|         let viewWidth = primary.width - dashWidth - this._spacing; | ||||
|         let viewHeight = contentHeight - this._spacing - viewY; | ||||
|  | ||||
|         this._searchEntry.set_position(searchX, searchY); | ||||
|         this._dash.actor.set_position(dashX, dashY); | ||||
|         this._viewSelector.actor.set_position(viewX, viewY); | ||||
|         this._viewSelector.actor.set_size(viewWidth, viewHeight); | ||||
|     }, | ||||
| @@ -601,28 +565,6 @@ const Overview = new Lang.Class({ | ||||
|             Lang.bind(this, this._onButtonPress)); | ||||
|     }, | ||||
|  | ||||
|     fadeInDesktop: function() { | ||||
|             this._desktopFade.opacity = 0; | ||||
|             this._desktopFade.show(); | ||||
|             Tweener.addTween(this._desktopFade, | ||||
|                              { opacity: 255, | ||||
|                                time: ANIMATION_TIME, | ||||
|                                transition: 'easeOutQuad' }); | ||||
|     }, | ||||
|  | ||||
|     fadeOutDesktop: function() { | ||||
|         if (!this._desktopFade.child) | ||||
|             this._desktopFade.child = this._getDesktopClone(); | ||||
|  | ||||
|         this._desktopFade.opacity = 255; | ||||
|         this._desktopFade.show(); | ||||
|         Tweener.addTween(this._desktopFade, | ||||
|                          { opacity: 0, | ||||
|                            time: ANIMATION_TIME, | ||||
|                            transition: 'easeOutQuad' | ||||
|                          }); | ||||
|     }, | ||||
|  | ||||
|     _animateVisible: function() { | ||||
|         if (this.visible || this.animationInProgress) | ||||
|             return; | ||||
| @@ -643,7 +585,21 @@ const Overview = new Lang.Class({ | ||||
|         global.window_group.hide(); | ||||
|         this._group.show(); | ||||
|         this._background.show(); | ||||
|         this._viewSelector.show(); | ||||
|  | ||||
|         this._workspacesDisplay.show(); | ||||
|  | ||||
|         if (!this._desktopFade.child) | ||||
|             this._desktopFade.child = this._getDesktopClone(); | ||||
|  | ||||
|         if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) { | ||||
|             this._desktopFade.opacity = 255; | ||||
|             this._desktopFade.show(); | ||||
|             Tweener.addTween(this._desktopFade, | ||||
|                              { opacity: 0, | ||||
|                                time: ANIMATION_TIME, | ||||
|                                transition: 'easeOutQuad' | ||||
|                              }); | ||||
|         } | ||||
|  | ||||
|         this._group.opacity = 0; | ||||
|         Tweener.addTween(this._group, | ||||
| @@ -655,7 +611,7 @@ const Overview = new Lang.Class({ | ||||
|                          }); | ||||
|  | ||||
|         Tweener.addTween(this._background, | ||||
|                          { dim_factor: 0.8, | ||||
|                          { dim_factor: 0.4, | ||||
|                            time: ANIMATION_TIME, | ||||
|                            transition: 'easeOutQuad' | ||||
|                          }); | ||||
| @@ -770,7 +726,16 @@ const Overview = new Lang.Class({ | ||||
|         this.animationInProgress = true; | ||||
|         this._hideInProgress = true; | ||||
|  | ||||
|         this._viewSelector.zoomFromOverview(); | ||||
|         if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) { | ||||
|             this._desktopFade.opacity = 0; | ||||
|             this._desktopFade.show(); | ||||
|             Tweener.addTween(this._desktopFade, | ||||
|                              { opacity: 255, | ||||
|                                time: ANIMATION_TIME, | ||||
|                                transition: 'easeOutQuad' }); | ||||
|         } | ||||
|  | ||||
|         this._workspacesDisplay.zoomFromOverview(); | ||||
|  | ||||
|         // Make other elements fade out. | ||||
|         Tweener.addTween(this._group, | ||||
| @@ -812,7 +777,8 @@ const Overview = new Lang.Class({ | ||||
|  | ||||
|         global.window_group.show(); | ||||
|  | ||||
|         this._viewSelector.hide(); | ||||
|         this._workspacesDisplay.hide(); | ||||
|  | ||||
|         this._desktopFade.hide(); | ||||
|         this._background.hide(); | ||||
|         this._group.hide(); | ||||
|   | ||||
							
								
								
									
										262
									
								
								js/ui/panel.js
									
									
									
									
									
								
							
							
						
						
									
										262
									
								
								js/ui/panel.js
									
									
									
									
									
								
							| @@ -14,14 +14,13 @@ const St = imports.gi.St; | ||||
| const Signals = imports.signals; | ||||
| const Atk = imports.gi.Atk; | ||||
|  | ||||
|  | ||||
| const Config = imports.misc.config; | ||||
| const CtrlAltTab = imports.ui.ctrlAltTab; | ||||
| const DND = imports.ui.dnd; | ||||
| const Layout = imports.ui.layout; | ||||
| const Overview = imports.ui.overview; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| const PanelMenu = imports.ui.panelMenu; | ||||
| const DateMenu = imports.ui.dateMenu; | ||||
| const Main = imports.ui.main; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| @@ -222,14 +221,14 @@ const AppMenuButton = new Lang.Class({ | ||||
|     Name: 'AppMenuButton', | ||||
|     Extends: PanelMenu.Button, | ||||
|  | ||||
|     _init: function(panel) { | ||||
|     _init: function(menuManager) { | ||||
|         this.parent(0.0, null, true); | ||||
|  | ||||
|         this.actor.accessible_role = Atk.Role.MENU; | ||||
|  | ||||
|         this._startingApps = []; | ||||
|  | ||||
|         this._menuManager = panel.menuManager; | ||||
|         this._menuManager = menuManager; | ||||
|         this._targetApp = null; | ||||
|         this._appMenuNotifyId = 0; | ||||
|         this._actionGroupNotifyId = 0; | ||||
| @@ -247,10 +246,6 @@ const AppMenuButton = new Lang.Class({ | ||||
|         this._container.connect('get-preferred-height', Lang.bind(this, this._getContentPreferredHeight)); | ||||
|         this._container.connect('allocate', Lang.bind(this, this._contentAllocate)); | ||||
|  | ||||
|         let textureCache = St.TextureCache.get_default(); | ||||
|         textureCache.connect('icon-theme-changed', | ||||
|                              Lang.bind(this, this._onIconThemeChanged)); | ||||
|  | ||||
|         this._iconBox = new Shell.Slicer({ name: 'appMenuIcon' }); | ||||
|         this._iconBox.connect('style-changed', | ||||
|                               Lang.bind(this, this._onIconBoxStyleChanged)); | ||||
| @@ -290,7 +285,7 @@ const AppMenuButton = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     show: function() { | ||||
|         if (this._visible || Main.screenShield.locked) | ||||
|         if (this._visible) | ||||
|             return; | ||||
|  | ||||
|         this._visible = true; | ||||
| @@ -336,15 +331,6 @@ const AppMenuButton = new Lang.Class({ | ||||
|         this._updateIconBoxClip(); | ||||
|     }, | ||||
|  | ||||
|     _onIconThemeChanged: function() { | ||||
|         if (this._iconBox.child == null) | ||||
|             return; | ||||
|  | ||||
|         this._iconBox.child.destroy(); | ||||
|         let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE); | ||||
|         this._iconBox.set_child(icon); | ||||
|     }, | ||||
|  | ||||
|     _updateIconBoxClip: function() { | ||||
|         let allocation = this._iconBox.allocation; | ||||
|         if (this._iconBottomClip > 0) | ||||
| @@ -481,6 +467,13 @@ const AppMenuButton = new Lang.Class({ | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     setLockedState: function(locked) { | ||||
|         if (locked) | ||||
|             this.hide(); | ||||
|         else | ||||
|             this._sync(); | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         let tracker = Shell.WindowTracker.get_default(); | ||||
|         let focusedApp = tracker.focus_app; | ||||
| @@ -614,8 +607,8 @@ const ActivitiesButton = new Lang.Class({ | ||||
|  | ||||
|         this.actor.label_actor = this._label; | ||||
|  | ||||
|         this.hotCorner = new Layout.HotCorner(); | ||||
|         container.add_actor(this.hotCorner.actor); | ||||
|         this._hotCorner = new Layout.HotCorner(); | ||||
|         container.add_actor(this._hotCorner.actor); | ||||
|  | ||||
|         // Hack up our menu... | ||||
|         this.menu.open = Lang.bind(this, this._onMenuOpenRequest); | ||||
| @@ -665,10 +658,10 @@ const ActivitiesButton = new Lang.Class({ | ||||
|         } | ||||
|  | ||||
|         hotBox.x1 = Math.round(x); | ||||
|         hotBox.x2 = hotBox.x1 + this.hotCorner.actor.width; | ||||
|         hotBox.x2 = hotBox.x1 + this._hotCorner.actor.width; | ||||
|         hotBox.y1 = Math.round(y); | ||||
|         hotBox.y2 = hotBox.y1 + this.hotCorner.actor.height; | ||||
|         this.hotCorner.actor.allocate(hotBox, flags); | ||||
|         hotBox.y2 = hotBox.y1 + this._hotCorner.actor.height; | ||||
|         this._hotCorner.actor.allocate(hotBox, flags); | ||||
|     }, | ||||
|  | ||||
|     handleDragOver: function(source, actor, x, y, time) { | ||||
| @@ -690,7 +683,7 @@ const ActivitiesButton = new Lang.Class({ | ||||
|  | ||||
|     _onCapturedEvent: function(actor, event) { | ||||
|         if (event.type() == Clutter.EventType.BUTTON_PRESS) { | ||||
|             if (!this.hotCorner.shouldToggleOverviewOnClick()) | ||||
|             if (!this._hotCorner.shouldToggleOverviewOnClick()) | ||||
|                 return true; | ||||
|         } | ||||
|         return false; | ||||
| @@ -906,30 +899,6 @@ const PanelCorner = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const PANEL_ITEM_IMPLEMENTATIONS = { | ||||
|     'activities': ActivitiesButton, | ||||
|     'appMenu': AppMenuButton, | ||||
|     'dateMenu': imports.ui.dateMenu.DateMenuButton, | ||||
|     'a11y': imports.ui.status.accessibility.ATIndicator, | ||||
|     'volume': imports.ui.status.volume.Indicator, | ||||
|     'battery': imports.ui.status.power.Indicator, | ||||
|     'lockScreen': imports.ui.status.lockScreenMenu.Indicator, | ||||
|     'logo': imports.gdm.loginDialog.LogoMenuButton, | ||||
|     'keyboard': imports.ui.status.keyboard.InputSourceIndicator, | ||||
|     'powerMenu': imports.gdm.powerMenu.PowerMenuButton, | ||||
|     'userMenu': imports.ui.userMenu.UserMenuButton | ||||
| }; | ||||
|  | ||||
| if (Config.HAVE_BLUETOOTH) | ||||
|     PANEL_ITEM_IMPLEMENTATIONS['bluetooth'] = | ||||
|         imports.ui.status.bluetooth.Indicator; | ||||
|  | ||||
| try { | ||||
|     PANEL_ITEM_IMPLEMENTATIONS['network'] = | ||||
|         imports.ui.status.network.NMApplet; | ||||
| } catch(e) { | ||||
|     log('NMApplet is not supported. It is possible that your NetworkManager version is too old'); | ||||
| } | ||||
|  | ||||
| const Panel = new Lang.Class({ | ||||
|     Name: 'Panel', | ||||
| @@ -939,7 +908,7 @@ const Panel = new Lang.Class({ | ||||
|                                                   reactive: true }); | ||||
|         this.actor._delegate = this; | ||||
|  | ||||
|         this.statusArea = {}; | ||||
|         this._statusArea = {}; | ||||
|  | ||||
|         Main.overview.connect('shown', Lang.bind(this, function () { | ||||
|             this.actor.add_style_class_name('in-overview'); | ||||
| @@ -948,7 +917,9 @@ const Panel = new Lang.Class({ | ||||
|             this.actor.remove_style_class_name('in-overview'); | ||||
|         })); | ||||
|  | ||||
|         this.menuManager = new PopupMenu.PopupMenuManager(this); | ||||
|         Main.screenShield.connect('lock-status-changed', Lang.bind(this, this._onLockStateChanged)); | ||||
|  | ||||
|         this._menus = new PopupMenu.PopupMenuManager(this); | ||||
|  | ||||
|         this._leftBox = new St.BoxLayout({ name: 'panelLeft' }); | ||||
|         this.actor.add_actor(this._leftBox); | ||||
| @@ -975,12 +946,30 @@ const Panel = new Lang.Class({ | ||||
|         this.actor.connect('allocate', Lang.bind(this, this._allocate)); | ||||
|         this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress)); | ||||
|  | ||||
|         Main.layoutManager.panelBox.add(this.actor); | ||||
|         Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here-symbolic', | ||||
|                                         { sortGroup: CtrlAltTab.SortGroup.TOP }); | ||||
|         /* Button on the left side of the panel. */ | ||||
|         if (Main.sessionMode.hasOverview) { | ||||
|             this._activitiesButton = new ActivitiesButton(); | ||||
|             this._activities = this._activitiesButton.actor; | ||||
|             this._leftBox.add(this._activities); | ||||
|  | ||||
|         Main.sessionMode.connect('updated', Lang.bind(this, this._updatePanel)); | ||||
|         this._updatePanel(); | ||||
|             // The activities button has a pretend menu, so as to integrate | ||||
|             // more cleanly with the rest of the panel | ||||
|             this._menus.addMenu(this._activitiesButton.menu); | ||||
|         } | ||||
|  | ||||
|         if (Main.sessionMode.hasAppMenu) { | ||||
|             this._appMenu = new AppMenuButton(this._menus); | ||||
|             this._leftBox.add(this._appMenu.actor); | ||||
|         } | ||||
|  | ||||
|         /* center */ | ||||
|         this._dateMenu = new DateMenu.DateMenuButton(); | ||||
|         this._centerBox.add(this._dateMenu.actor, { y_fill: true }); | ||||
|         this._menus.addMenu(this._dateMenu.menu); | ||||
|  | ||||
|         Main.layoutManager.panelBox.add(this.actor); | ||||
|         Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here', | ||||
|                                         { sortGroup: CtrlAltTab.SortGroup.TOP }); | ||||
|     }, | ||||
|  | ||||
|     _getPreferredWidth: function(actor, forHeight, alloc) { | ||||
| @@ -1040,19 +1029,16 @@ const Panel = new Lang.Class({ | ||||
|         } | ||||
|         this._rightBox.allocate(childBox, flags); | ||||
|  | ||||
|         let cornerMinWidth, cornerMinHeight; | ||||
|         let cornerWidth, cornerHeight; | ||||
|  | ||||
|         [cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1); | ||||
|         [cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_height(-1); | ||||
|         let [cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1); | ||||
|         let [cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_width(-1); | ||||
|         childBox.x1 = 0; | ||||
|         childBox.x2 = cornerWidth; | ||||
|         childBox.y1 = allocHeight; | ||||
|         childBox.y2 = allocHeight + cornerHeight; | ||||
|         this._leftCorner.actor.allocate(childBox, flags); | ||||
|  | ||||
|         [cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1); | ||||
|         [cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_height(-1); | ||||
|         let [cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1); | ||||
|         let [cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_width(-1); | ||||
|         childBox.x1 = allocWidth - cornerWidth; | ||||
|         childBox.x2 = allocWidth; | ||||
|         childBox.y1 = allocHeight; | ||||
| @@ -1100,126 +1086,76 @@ const Panel = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     openAppMenu: function() { | ||||
|         let indicator = this.statusArea.appMenu; | ||||
|         if (!indicator) // appMenu not supported by current session mode | ||||
|             return; | ||||
|  | ||||
|         let menu = indicator.menu; | ||||
|         if (!indicator.actor.reactive || menu.isOpen) | ||||
|         let menu = this._appMenu.menu; | ||||
|         if (!this._appMenu.actor.reactive || menu.isOpen) | ||||
|             return; | ||||
|  | ||||
|         menu.open(); | ||||
|         menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); | ||||
|     }, | ||||
|  | ||||
|     set boxOpacity(value) { | ||||
|         let isReactive = value > 0; | ||||
|  | ||||
|         this._leftBox.opacity = value; | ||||
|         this._leftBox.reactive = isReactive; | ||||
|         this._centerBox.opacity = value; | ||||
|         this._centerBox.reactive = isReactive; | ||||
|         this._rightBox.opacity = value; | ||||
|         this._rightBox.reactive = isReactive; | ||||
|     }, | ||||
|  | ||||
|     get boxOpacity() { | ||||
|         return this._leftBox.opacity; | ||||
|     }, | ||||
|  | ||||
|     _updatePanel: function() { | ||||
|         let panel = Main.sessionMode.panel; | ||||
|         this._hideIndicators(); | ||||
|         this._updateBox(panel.left, this._leftBox); | ||||
|         this._updateBox(panel.center, this._centerBox); | ||||
|         this._updateBox(panel.right, this._rightBox); | ||||
|     }, | ||||
|  | ||||
|     _initBox: function(elements, box) { | ||||
|         for (let i = 0; i < elements.length; i++) { | ||||
|             let role = elements[i]; | ||||
|             let constructor = PANEL_ITEM_IMPLEMENTATIONS[role]; | ||||
|             if (!constructor) { | ||||
|                 // panel icon is not supported (can happen for | ||||
|                 // bluetooth or network) | ||||
|                 continue; | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _hideIndicators: function() { | ||||
|         for (let role in PANEL_ITEM_IMPLEMENTATIONS) { | ||||
|             let indicator = this.statusArea[role]; | ||||
|             if (!indicator) | ||||
|                 continue; | ||||
|             if (indicator.menu) | ||||
|                 indicator.menu.close(); | ||||
|             indicator.container.hide(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _ensureIndicator: function(role) { | ||||
|         let indicator = this.statusArea[role]; | ||||
|         if (!indicator) { | ||||
|             let constructor = PANEL_ITEM_IMPLEMENTATIONS[role]; | ||||
|     startStatusArea: function() { | ||||
|         for (let i = 0; i < Main.sessionMode.statusArea.order.length; i++) { | ||||
|             let role = Main.sessionMode.statusArea.order[i]; | ||||
|             let constructor = Main.sessionMode.statusArea.implementation[role]; | ||||
|             if (!constructor) { | ||||
|                 // This icon is not implemented (this is a bug) | ||||
|                 return null; | ||||
|             } | ||||
|             indicator = new constructor(this); | ||||
|             this.statusArea[role] = indicator; | ||||
|         } | ||||
|         return indicator; | ||||
|     }, | ||||
|  | ||||
|     _updateBox: function(elements, box) { | ||||
|         let nChildren = box.get_n_children(); | ||||
|  | ||||
|         for (let i = 0; i < elements.length; i++) { | ||||
|             let role = elements[i]; | ||||
|             let indicator = this._ensureIndicator(role); | ||||
|             if (indicator == null) | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             this._addToPanelBox(role, indicator, i + nChildren, box); | ||||
|             let indicator = new constructor(); | ||||
|             this.addToStatusArea(role, indicator, i); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _addToPanelBox: function(role, indicator, position, box) { | ||||
|         let container = indicator.container; | ||||
|         container.show(); | ||||
|  | ||||
|         let parent = container.get_parent(); | ||||
|         if (parent) | ||||
|             parent.remove_actor(container); | ||||
|  | ||||
|         box.insert_child_at_index(container, position); | ||||
|         if (indicator.menu) | ||||
|             this.menuManager.addMenu(indicator.menu); | ||||
|         this.statusArea[role] = indicator; | ||||
|         let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) { | ||||
|             delete this.statusArea[role]; | ||||
|             emitter.disconnect(destroyId); | ||||
|             container.destroy(); | ||||
|         })); | ||||
|     _insertStatusItem: function(actor, position) { | ||||
|         let children = this._rightBox.get_children(); | ||||
|         let i; | ||||
|         for (i = children.length - 1; i >= 0; i--) { | ||||
|             let rolePosition = children[i]._rolePosition; | ||||
|             if (position > rolePosition) { | ||||
|                 this._rightBox.insert_child_at_index(actor, i + 1); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         if (i == -1) { | ||||
|             // If we didn't find a position, we must be first | ||||
|             this._rightBox.insert_child_at_index(actor, 0); | ||||
|         } | ||||
|         actor._rolePosition = position; | ||||
|     }, | ||||
|  | ||||
|     addToStatusArea: function(role, indicator, position, box) { | ||||
|         if (this.statusArea[role]) | ||||
|     addToStatusArea: function(role, indicator, position) { | ||||
|         if (this._statusArea[role]) | ||||
|             throw new Error('Extension point conflict: there is already a status indicator for role ' + role); | ||||
|  | ||||
|         if (!(indicator instanceof PanelMenu.Button)) | ||||
|             throw new TypeError('Status indicator must be an instance of PanelMenu.Button'); | ||||
|  | ||||
|         position = position || 0; | ||||
|         let boxes = { | ||||
|             left: this._leftBox, | ||||
|             center: this._centerBox, | ||||
|             right: this._rightBox | ||||
|         }; | ||||
|         let boxContainer = boxes[box] || this._rightBox; | ||||
|         this.statusArea[role] = indicator; | ||||
|         this._addToPanelBox(role, indicator, position, boxContainer); | ||||
|         if (!position) | ||||
|             position = 0; | ||||
|         this._insertStatusItem(indicator.actor, position); | ||||
|         if (indicator.menu) | ||||
|             this._menus.addMenu(indicator.menu); | ||||
|  | ||||
|         this._statusArea[role] = indicator; | ||||
|         let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) { | ||||
|             delete this._statusArea[role]; | ||||
|             emitter.disconnect(destroyId); | ||||
|         })); | ||||
|  | ||||
|         return indicator; | ||||
|     } | ||||
|     }, | ||||
|  | ||||
|     _onLockStateChanged: function(shield, locked) { | ||||
|         if (this._activitiesButton) | ||||
|             this._activitiesButton.setLockedState(locked); | ||||
|         if (this._appMenu) | ||||
|             this._appMenu.setLockedState(locked); | ||||
|         if (this._dateMenu) | ||||
|             this._dateMenu.setLockedState(locked); | ||||
|  | ||||
|         for (let id in this._statusArea) | ||||
|             this._statusArea[id].setLockedState(locked); | ||||
|     }, | ||||
| }); | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gio = imports.gi.Gio; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Lang = imports.lang; | ||||
| const Shell = imports.gi.Shell; | ||||
| @@ -21,10 +20,6 @@ const ButtonBox = new Lang.Class({ | ||||
|         this.actor = new Shell.GenericContainer(params); | ||||
|         this.actor._delegate = this; | ||||
|  | ||||
|         this.container = new St.Bin({ y_fill: true, | ||||
|                                       x_fill: true, | ||||
|                                       child: this.actor }); | ||||
|  | ||||
|         this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); | ||||
|         this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); | ||||
|         this.actor.connect('allocate', Lang.bind(this, this._allocate)); | ||||
| @@ -119,12 +114,6 @@ const Button = new Lang.Class({ | ||||
|         this.setName(nameText); | ||||
|     }, | ||||
|  | ||||
|     setSensitive: function(sensitive) { | ||||
|         this.actor.reactive = sensitive; | ||||
|         this.actor.can_focus = sensitive; | ||||
|         this.actor.track_hover = sensitive; | ||||
|     }, | ||||
|  | ||||
|     setName: function(text) { | ||||
|         if (text != null) { | ||||
|             // This is the easiest way to provide a accessible name to | ||||
| @@ -156,6 +145,13 @@ const Button = new Lang.Class({ | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     setLockedState: function(locked) { | ||||
|         // default behaviour is to hide completely | ||||
|         if (locked) | ||||
|             this.menu.close(); | ||||
|         this.actor.visible = !locked; | ||||
|     }, | ||||
|  | ||||
|     _onButtonPress: function(actor, event) { | ||||
|         if (!this.menu) | ||||
|             return; | ||||
| @@ -186,7 +182,8 @@ const Button = new Lang.Class({ | ||||
|     _onMenuKeyPress: function(actor, event) { | ||||
|         let symbol = event.get_key_symbol(); | ||||
|         if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) { | ||||
|             let group = global.focus_manager.get_group(this.actor); | ||||
|             let focusManager = St.FocusManager.get_for_stage(global.stage); | ||||
|             let group = focusManager.get_group(this.actor); | ||||
|             if (group) { | ||||
|                 let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT; | ||||
|                 group.navigate_focus(this.actor, direction, false); | ||||
| @@ -214,8 +211,7 @@ const Button = new Lang.Class({ | ||||
|     destroy: function() { | ||||
|         this.actor._delegate = null; | ||||
|  | ||||
|         if (this.menu) | ||||
|             this.menu.destroy(); | ||||
|         this.menu.destroy(); | ||||
|         this.actor.destroy(); | ||||
|  | ||||
|         this.emit('destroy'); | ||||
| @@ -235,36 +231,19 @@ const SystemStatusButton = new Lang.Class({ | ||||
|  | ||||
|     _init: function(iconName, nameText) { | ||||
|         this.parent(0.0, nameText); | ||||
|  | ||||
|         this._iconActor = new St.Icon({ icon_name: iconName, | ||||
|                                         icon_type: St.IconType.SYMBOLIC, | ||||
|                                         style_class: 'system-status-icon' }); | ||||
|         this.actor.add_actor(this._iconActor); | ||||
|         this.actor.add_style_class_name('panel-status-button'); | ||||
|  | ||||
|         this._box = new St.BoxLayout({ style_class: 'panel-status-button-box' }); | ||||
|         this.actor.add_actor(this._box); | ||||
|  | ||||
|         if (iconName) | ||||
|             this.setIcon(iconName); | ||||
|     }, | ||||
|  | ||||
|     addIcon: function(gicon) { | ||||
|         let icon = new St.Icon({ gicon: gicon, | ||||
|                                  style_class: 'system-status-icon' }); | ||||
|         this._box.add_actor(icon); | ||||
|  | ||||
|         return icon; | ||||
|     }, | ||||
|  | ||||
|     setIcon: function(iconName) { | ||||
|         // Need to first add a NULL GIcon and then set icon_name, to ensure | ||||
|         // compatibility with -symbolic fallbacks | ||||
|  | ||||
|         if (!this.mainIcon) | ||||
|             this.mainIcon = this.addIcon(null); | ||||
|         this.mainIcon.icon_name = iconName; | ||||
|         this._iconActor.icon_name = iconName; | ||||
|     }, | ||||
|  | ||||
|     setGIcon: function(gicon) { | ||||
|         if (this.mainIcon) | ||||
|             this.mainIcon.gicon = gicon; | ||||
|         else | ||||
|             this.mainIcon = this.addIcon(gicon); | ||||
|         this._iconActor.gicon = gicon; | ||||
|     } | ||||
| }); | ||||
|   | ||||
							
								
								
									
										430
									
								
								js/ui/placeDisplay.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										430
									
								
								js/ui/placeDisplay.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,430 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gio = imports.gi.Gio; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Signals = imports.signals; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const DND = imports.ui.dnd; | ||||
| const Main = imports.ui.main; | ||||
| const Params = imports.misc.params; | ||||
| const Search = imports.ui.search; | ||||
| const Util = imports.misc.util; | ||||
|  | ||||
| /** | ||||
|  * Represents a place object, which is most normally a bookmark entry, | ||||
|  * a mount/volume, or a special place like the Home Folder, Computer, and Network. | ||||
|  * | ||||
|  * @name: String title | ||||
|  * @iconFactory: A JavaScript callback which will create an icon texture given a size parameter | ||||
|  * @launch: A JavaScript callback to launch the entry | ||||
|  */ | ||||
| const PlaceInfo = new Lang.Class({ | ||||
|     Name: 'PlaceInfo', | ||||
|  | ||||
|     _init: function(id, name, iconFactory, launch) { | ||||
|         this.id = id; | ||||
|         this.name = name; | ||||
|         this._lowerName = name.toLowerCase(); | ||||
|         this.iconFactory = iconFactory; | ||||
|         this.launch = launch; | ||||
|     }, | ||||
|  | ||||
|     matchTerms: function(terms) { | ||||
|         let mtype = Search.MatchType.NONE; | ||||
|         for (let i = 0; i < terms.length; i++) { | ||||
|             let term = terms[i]; | ||||
|             let idx = this._lowerName.indexOf(term); | ||||
|             if (idx == 0) { | ||||
|                 mtype = Search.MatchType.PREFIX; | ||||
|             } else if (idx > 0) { | ||||
|                 if (mtype == Search.MatchType.NONE) | ||||
|                     mtype = Search.MatchType.SUBSTRING; | ||||
|             } else { | ||||
|                 return Search.MatchType.NONE; | ||||
|             } | ||||
|         } | ||||
|         return mtype; | ||||
|     }, | ||||
|  | ||||
|     isRemovable: function() { | ||||
|         return false; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| // Helper function to translate launch parameters into a GAppLaunchContext | ||||
| function _makeLaunchContext(params) | ||||
| { | ||||
|     params = Params.parse(params, { workspace: -1, | ||||
|                                     timestamp: 0 }); | ||||
|  | ||||
|     let launchContext = global.create_app_launch_context(); | ||||
|     if (params.workspace != -1) | ||||
|         launchContext.set_desktop(params.workspace); | ||||
|     if (params.timestamp != 0) | ||||
|         launchContext.set_timestamp(params.timestamp); | ||||
|  | ||||
|     return launchContext; | ||||
| } | ||||
|  | ||||
| const PlaceDeviceInfo = new Lang.Class({ | ||||
|     Name: 'PlaceDeviceInfo', | ||||
|     Extends: PlaceInfo, | ||||
|  | ||||
|     _init: function(mount) { | ||||
|         this._mount = mount; | ||||
|         this.name = mount.get_name(); | ||||
|         this._lowerName = this.name.toLowerCase(); | ||||
|         this.id = 'mount:' + mount.get_root().get_uri(); | ||||
|     }, | ||||
|  | ||||
|     iconFactory: function(size) { | ||||
|         let icon = this._mount.get_icon(); | ||||
|         return St.TextureCache.get_default().load_gicon(null, icon, size); | ||||
|     }, | ||||
|  | ||||
|     launch: function(params) { | ||||
|         Gio.app_info_launch_default_for_uri(this._mount.get_root().get_uri(), | ||||
|                                             _makeLaunchContext(params)); | ||||
|     }, | ||||
|  | ||||
|     isRemovable: function() { | ||||
|         return this._mount.can_unmount(); | ||||
|     }, | ||||
|  | ||||
|     remove: function() { | ||||
|         if (!this.isRemovable()) | ||||
|             return; | ||||
|  | ||||
|         if (this._mount.can_eject()) | ||||
|             this._mount.eject(0, null, Lang.bind(this, this._removeFinish)); | ||||
|         else | ||||
|             this._mount.unmount(0, null, Lang.bind(this, this._removeFinish)); | ||||
|     }, | ||||
|  | ||||
|     _removeFinish: function(o, res, data) { | ||||
|         try { | ||||
|             if (this._mount.can_eject()) | ||||
|                 this._mount.eject_finish(res); | ||||
|             else | ||||
|                 this._mount.unmount_finish(res); | ||||
|         } catch (e) { | ||||
|             let message = _("Failed to unmount '%s'").format(o.get_name()); | ||||
|             Main.overview.setMessage(message, | ||||
|                                      Lang.bind(this, this.remove), | ||||
|                                      _("Retry")); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const PlacesManager = new Lang.Class({ | ||||
|     Name: 'PlacesManager', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._defaultPlaces = []; | ||||
|         this._mounts = []; | ||||
|         this._bookmarks = []; | ||||
|  | ||||
|         let homeFile = Gio.file_new_for_path (GLib.get_home_dir()); | ||||
|         let homeUri = homeFile.get_uri(); | ||||
|         let homeLabel = Shell.util_get_label_for_uri (homeUri); | ||||
|         let homeIcon = Shell.util_get_icon_for_uri (homeUri); | ||||
|         this._home = new PlaceInfo('special:home', homeLabel, | ||||
|             function(size) { | ||||
|                 return St.TextureCache.get_default().load_gicon(null, homeIcon, size); | ||||
|             }, | ||||
|             function(params) { | ||||
|                 Gio.app_info_launch_default_for_uri(homeUri, _makeLaunchContext(params)); | ||||
|             }); | ||||
|  | ||||
|         let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP); | ||||
|         let desktopFile = Gio.file_new_for_path (desktopPath); | ||||
|         let desktopUri = desktopFile.get_uri(); | ||||
|         let desktopLabel = Shell.util_get_label_for_uri (desktopUri); | ||||
|         let desktopIcon = Shell.util_get_icon_for_uri (desktopUri); | ||||
|         this._desktopMenu = new PlaceInfo('special:desktop', desktopLabel, | ||||
|             function(size) { | ||||
|                 return St.TextureCache.get_default().load_gicon(null, desktopIcon, size); | ||||
|             }, | ||||
|             function(params) { | ||||
|                 Gio.app_info_launch_default_for_uri(desktopUri, _makeLaunchContext(params)); | ||||
|             }); | ||||
|  | ||||
|         this._connect = new PlaceInfo('special:connect', _("Connect to..."), | ||||
|             function (size) { | ||||
|                 // do NOT use St.Icon here, it crashes the shell | ||||
|                 // see wanda.js for details | ||||
|                 return St.TextureCache.get_default().load_icon_name(null, | ||||
|                                                                     'applications-internet', | ||||
|                                                                     St.IconType.FULLCOLOR, | ||||
|                                                                     size); | ||||
|             }, | ||||
|             function (params) { | ||||
|                 // BUG: nautilus-connect-server doesn't have a desktop file, so we can't | ||||
|                 // launch it with the workspace from params. It's probably pretty rare | ||||
|                 // and odd to drag this place onto a workspace in any case | ||||
|  | ||||
|                 Util.spawn(['nautilus-connect-server']); | ||||
|             }); | ||||
|  | ||||
|         this._defaultPlaces.push(this._home); | ||||
|         this._defaultPlaces.push(this._desktopMenu); | ||||
|         this._defaultPlaces.push(this._connect); | ||||
|  | ||||
|         /* | ||||
|         * Show devices, code more or less ported from nautilus-places-sidebar.c | ||||
|         */ | ||||
|         this._volumeMonitor = Gio.VolumeMonitor.get(); | ||||
|         this._volumeMonitor.connect('volume-added', Lang.bind(this, this._updateDevices)); | ||||
|         this._volumeMonitor.connect('volume-removed',Lang.bind(this, this._updateDevices)); | ||||
|         this._volumeMonitor.connect('volume-changed', Lang.bind(this, this._updateDevices)); | ||||
|         this._volumeMonitor.connect('mount-added', Lang.bind(this, this._updateDevices)); | ||||
|         this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._updateDevices)); | ||||
|         this._volumeMonitor.connect('mount-changed', Lang.bind(this, this._updateDevices)); | ||||
|         this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._updateDevices)); | ||||
|         this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._updateDevices)); | ||||
|         this._volumeMonitor.connect('drive-changed', Lang.bind(this, this._updateDevices)); | ||||
|         this._updateDevices(); | ||||
|  | ||||
|         this._bookmarksPath = GLib.build_filenamev([GLib.get_user_config_dir(), 'gtk-3.0', 'bookmarks']); | ||||
|         this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath); | ||||
|         this._monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null); | ||||
|         this._bookmarkTimeoutId = 0; | ||||
|         this._monitor.connect('changed', Lang.bind(this, function () { | ||||
|             if (this._bookmarkTimeoutId > 0) | ||||
|                 return; | ||||
|             /* Defensive event compression */ | ||||
|             this._bookmarkTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function () { | ||||
|                 this._bookmarkTimeoutId = 0; | ||||
|                 this._reloadBookmarks(); | ||||
|                 return false; | ||||
|             })); | ||||
|         })); | ||||
|  | ||||
|         this._reloadBookmarks(); | ||||
|     }, | ||||
|  | ||||
|     _updateDevices: function() { | ||||
|         this._mounts = []; | ||||
|  | ||||
|         /* first go through all connected drives */ | ||||
|         let drives = this._volumeMonitor.get_connected_drives(); | ||||
|         for (let i = 0; i < drives.length; i++) { | ||||
|             let volumes = drives[i].get_volumes(); | ||||
|             for(let j = 0; j < volumes.length; j++) { | ||||
|                 let mount = volumes[j].get_mount(); | ||||
|                 if(mount != null) { | ||||
|                     this._addMount(mount); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /* add all volumes that is not associated with a drive */ | ||||
|         let volumes = this._volumeMonitor.get_volumes(); | ||||
|         for(let i = 0; i < volumes.length; i++) { | ||||
|             if(volumes[i].get_drive() != null) | ||||
|                 continue; | ||||
|  | ||||
|             let mount = volumes[i].get_mount(); | ||||
|             if(mount != null) { | ||||
|                 this._addMount(mount); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /* add mounts that have no volume (/etc/mtab mounts, ftp, sftp,...) */ | ||||
|         let mounts = this._volumeMonitor.get_mounts(); | ||||
|         for(let i = 0; i < mounts.length; i++) { | ||||
|             if(mounts[i].is_shadowed()) | ||||
|                 continue; | ||||
|  | ||||
|             if(mounts[i].get_volume()) | ||||
|                 continue; | ||||
|  | ||||
|             this._addMount(mounts[i]); | ||||
|         } | ||||
|  | ||||
|         /* We emit two signals, one for a generic 'all places' update | ||||
|          * and the other for one specific to mounts. We do this because | ||||
|          * clients like PlaceDisplay may only care about places in general | ||||
|          * being updated while clients like DashPlaceDisplay care which | ||||
|          * specific type of place got updated. | ||||
|          */ | ||||
|         this.emit('mounts-updated'); | ||||
|         this.emit('places-updated'); | ||||
|  | ||||
|     }, | ||||
|  | ||||
|     _reloadBookmarks: function() { | ||||
|  | ||||
|         this._bookmarks = []; | ||||
|  | ||||
|         if (!GLib.file_test(this._bookmarksPath, GLib.FileTest.EXISTS)) | ||||
|             return; | ||||
|  | ||||
|         let bookmarksContent = Shell.get_file_contents_utf8_sync(this._bookmarksPath); | ||||
|  | ||||
|         let bookmarks = bookmarksContent.split('\n'); | ||||
|  | ||||
|         let bookmarksToLabel = {}; | ||||
|         let bookmarksOrder = []; | ||||
|         for (let i = 0; i < bookmarks.length; i++) { | ||||
|             let bookmarkLine = bookmarks[i]; | ||||
|             let components = bookmarkLine.split(' '); | ||||
|             let bookmark = components[0]; | ||||
|             if (bookmark in bookmarksToLabel) | ||||
|                 continue; | ||||
|             let label = null; | ||||
|             if (components.length > 1) | ||||
|                 label = components.slice(1).join(' '); | ||||
|             bookmarksToLabel[bookmark] = label; | ||||
|             bookmarksOrder.push(bookmark); | ||||
|         } | ||||
|  | ||||
|         for (let i = 0; i < bookmarksOrder.length; i++) { | ||||
|             let bookmark = bookmarksOrder[i]; | ||||
|             let label = bookmarksToLabel[bookmark]; | ||||
|             let file = Gio.file_new_for_uri(bookmark); | ||||
|             if (!file.query_exists(null)) | ||||
|                 continue; | ||||
|             if (label == null) | ||||
|                 label = Shell.util_get_label_for_uri(bookmark); | ||||
|             if (label == null) | ||||
|                 continue; | ||||
|             let icon = Shell.util_get_icon_for_uri(bookmark); | ||||
|  | ||||
|             let item = new PlaceInfo('bookmark:' + bookmark, label, | ||||
|                 function(size) { | ||||
|                     return St.TextureCache.get_default().load_gicon(null, icon, size); | ||||
|                 }, | ||||
|                 function(params) { | ||||
|                     Gio.app_info_launch_default_for_uri(bookmark, _makeLaunchContext(params)); | ||||
|                 }); | ||||
|             this._bookmarks.push(item); | ||||
|         } | ||||
|  | ||||
|         /* See comment in _updateDevices for explanation why there are two signals. */ | ||||
|         this.emit('bookmarks-updated'); | ||||
|         this.emit('places-updated'); | ||||
|     }, | ||||
|  | ||||
|     _addMount: function(mount) { | ||||
|         let devItem = new PlaceDeviceInfo(mount); | ||||
|         this._mounts.push(devItem); | ||||
|     }, | ||||
|  | ||||
|     getAllPlaces: function () { | ||||
|         return this.getDefaultPlaces().concat(this.getBookmarks(), this.getMounts()); | ||||
|     }, | ||||
|  | ||||
|     getDefaultPlaces: function () { | ||||
|         return this._defaultPlaces; | ||||
|     }, | ||||
|  | ||||
|     getBookmarks: function () { | ||||
|         return this._bookmarks; | ||||
|     }, | ||||
|  | ||||
|     getMounts: function () { | ||||
|         return this._mounts; | ||||
|     }, | ||||
|  | ||||
|     _lookupIndexById: function(sourceArray, id) { | ||||
|         for (let i = 0; i < sourceArray.length; i++) { | ||||
|             let place = sourceArray[i]; | ||||
|             if (place.id == id) | ||||
|                 return i; | ||||
|         } | ||||
|         return -1; | ||||
|     }, | ||||
|  | ||||
|     lookupPlaceById: function(id) { | ||||
|         let colonIdx = id.indexOf(':'); | ||||
|         let type = id.substring(0, colonIdx); | ||||
|         let sourceArray = null; | ||||
|         if (type == 'special') | ||||
|             sourceArray = this._defaultPlaces; | ||||
|         else if (type == 'mount') | ||||
|             sourceArray = this._mounts; | ||||
|         else if (type == 'bookmark') | ||||
|             sourceArray = this._bookmarks; | ||||
|         return sourceArray[this._lookupIndexById(sourceArray, id)]; | ||||
|     }, | ||||
|  | ||||
|     _removeById: function(sourceArray, id) { | ||||
|         sourceArray.splice(this._lookupIndexById(sourceArray, id), 1); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(PlacesManager.prototype); | ||||
|  | ||||
| const PlaceSearchProvider = new Lang.Class({ | ||||
|     Name: 'PlaceSearchProvider', | ||||
|     Extends: Search.SearchProvider, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent(_("PLACES & DEVICES")); | ||||
|         this.placesManager = new PlacesManager(); | ||||
|     }, | ||||
|  | ||||
|     getResultMetas: function(resultIds, callback) { | ||||
|         let metas = []; | ||||
|         for (let i = 0; i < resultIds.length; i++) { | ||||
|             let placeInfo = this.placesManager.lookupPlaceById(resultIds[i]); | ||||
|             if (!placeInfo) | ||||
|                 metas.push(null); | ||||
|             else | ||||
|                 metas.push({ 'id': resultIds[i], | ||||
|                              'name': placeInfo.name, | ||||
|                              'createIcon': function(size) { | ||||
|                                  return placeInfo.iconFactory(size); | ||||
|                              } | ||||
|                            }); | ||||
|         } | ||||
|         callback(metas); | ||||
|     }, | ||||
|  | ||||
|     activateResult: function(id, params) { | ||||
|         let placeInfo = this.placesManager.lookupPlaceById(id); | ||||
|         placeInfo.launch(params); | ||||
|     }, | ||||
|  | ||||
|     _compareResultMeta: function (idA, idB) { | ||||
|         let infoA = this.placesManager.lookupPlaceById(idA); | ||||
|         let infoB = this.placesManager.lookupPlaceById(idB); | ||||
|         return infoA.name.localeCompare(infoB.name); | ||||
|     }, | ||||
|  | ||||
|     _searchPlaces: function(places, terms) { | ||||
|         let prefixResults = []; | ||||
|         let substringResults = []; | ||||
|  | ||||
|         terms = terms.map(String.toLowerCase); | ||||
|  | ||||
|         for (let i = 0; i < places.length; i++) { | ||||
|             let place = places[i]; | ||||
|             let mtype = place.matchTerms(terms); | ||||
|             if (mtype == Search.MatchType.PREFIX) | ||||
|                 prefixResults.push(place.id); | ||||
|             else if (mtype == Search.MatchType.SUBSTRING) | ||||
|                 substringResults.push(place.id); | ||||
|         } | ||||
|         prefixResults.sort(Lang.bind(this, this._compareResultMeta)); | ||||
|         substringResults.sort(Lang.bind(this, this._compareResultMeta)); | ||||
|  | ||||
|         this.searchSystem.pushResults(this, prefixResults.concat(substringResults)); | ||||
|     }, | ||||
|  | ||||
|     getInitialResultSet: function(terms) { | ||||
|         let places = this.placesManager.getAllPlaces(); | ||||
|         this._searchPlaces(places, terms); | ||||
|     }, | ||||
|  | ||||
|     getSubsearchResultSet: function(previousResults, terms) { | ||||
|         let places = previousResults.map(Lang.bind(this, function(id) { | ||||
|             return this.placesManager.lookupPlaceById(id); | ||||
|         })); | ||||
|         this._searchPlaces(places, terms); | ||||
|     } | ||||
| }); | ||||
| @@ -1,130 +0,0 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const GnomeDesktop = imports.gi.GnomeDesktop; | ||||
| const Shell = imports.gi.Shell; | ||||
|  | ||||
| // We stop polling if the user is idle for more than this amount of time | ||||
| const IDLE_TIME = 1000; | ||||
|  | ||||
| // This file implements a reasonably efficient system for tracking the position | ||||
| // of the mouse pointer. We simply query the pointer from the X server in a loop, | ||||
| // but we turn off the polling when the user is idle. | ||||
|  | ||||
| let _pointerWatcher = null; | ||||
| function getPointerWatcher() { | ||||
|     if (_pointerWatcher == null) | ||||
|         _pointerWatcher = new PointerWatcher(); | ||||
|  | ||||
|     return _pointerWatcher; | ||||
| } | ||||
|  | ||||
| const PointerWatch = new Lang.Class({ | ||||
|     Name: 'PointerWatch', | ||||
|  | ||||
|     _init: function(watcher, interval, callback) { | ||||
|         this.watcher = watcher; | ||||
|         this.interval = interval; | ||||
|         this.callback = callback; | ||||
|     }, | ||||
|  | ||||
|     // remove: | ||||
|     // remove this watch. This function may safely be called | ||||
|     // while the callback is executing. | ||||
|     remove: function() { | ||||
|         this.watcher._removeWatch(this); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const PointerWatcher = new Lang.Class({ | ||||
|     Name: 'PointerWatcher', | ||||
|  | ||||
|     _init: function() { | ||||
|         let idleMonitor = new GnomeDesktop.IdleMonitor(); | ||||
|         idleMonitor.connect('became-active', Lang.bind(this, this._onIdleMonitorBecameActive)); | ||||
|         idleMonitor.add_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle)); | ||||
|         this._idle = idleMonitor.get_idletime() > IDLE_TIME; | ||||
|         this._watches = []; | ||||
|         this.pointerX = null; | ||||
|         this.pointerY = null; | ||||
|     }, | ||||
|  | ||||
|     // addWatch: | ||||
|     // @interval: hint as to the time resolution needed. When the user is | ||||
|     //   not idle, the position of the pointer will be queried at least | ||||
|     //   once every this many milliseconds. | ||||
|     // @callback: function to call when the pointer position changes - takes | ||||
|     //   two arguments, X and Y. | ||||
|     // | ||||
|     // Set up a watch on the position of the mouse pointer. Returns a | ||||
|     // PointerWatch object which has a remove() method to remove the watch. | ||||
|     addWatch: function(interval, callback) { | ||||
|         // Avoid unreliably calling the watch for the current position | ||||
|         this._updatePointer(); | ||||
|  | ||||
|         let watch = new PointerWatch(this, interval, callback); | ||||
|         this._watches.push(watch); | ||||
|         this._updateTimeout(); | ||||
|         return watch; | ||||
|     }, | ||||
|  | ||||
|     _removeWatch: function(watch) { | ||||
|         for (let i = 0; i < this._watches.length; i++) { | ||||
|             if (this._watches[i] == watch) { | ||||
|                 this._watches.splice(i, 1); | ||||
|                 this._updateTimeout(); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onIdleMonitorBecameActive: function(monitor) { | ||||
|         this._idle = false; | ||||
|         this._updatePointer(); | ||||
|         this._updateTimeout(); | ||||
|     }, | ||||
|  | ||||
|     _onIdleMonitorBecameIdle: function(monitor) { | ||||
|         this._idle = true; | ||||
|         this._updateTimeout(); | ||||
|     }, | ||||
|  | ||||
|     _updateTimeout: function() { | ||||
|         if (this._timeoutId) { | ||||
|             Mainloop.source_remove(this._timeoutId); | ||||
|             this._timeoutId = 0; | ||||
|         } | ||||
|  | ||||
|         if (this._idle || this._watches.length == 0) | ||||
|             return; | ||||
|  | ||||
|         let minInterval = this._watches[0].interval; | ||||
|         for (let i = 1; i < this._watches.length; i++) | ||||
|             minInterval = Math.min(this._watches[i].interval, minInterval); | ||||
|  | ||||
|         this._timeoutId = Mainloop.timeout_add(minInterval, | ||||
|                                                Lang.bind(this, this._onTimeout)); | ||||
|     }, | ||||
|  | ||||
|     _onTimeout: function() { | ||||
|         this._updatePointer(); | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _updatePointer: function() { | ||||
|         let [x, y, mods] = global.get_pointer(); | ||||
|         if (this.pointerX == x && this.pointerY == y) | ||||
|             return; | ||||
|  | ||||
|         this.pointerX = x; | ||||
|         this.pointerY = y; | ||||
|  | ||||
|         for (let i = 0; i < this._watches.length;) { | ||||
|             let watch = this._watches[i]; | ||||
|             watch.callback(x, y); | ||||
|             if (watch == this._watches[i]) // guard against self-removal | ||||
|                 i++; | ||||
|         } | ||||
|     } | ||||
| }); | ||||
| @@ -1,4 +1,24 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | ||||
| /* | ||||
|  * Copyright 2010 Red Hat, Inc | ||||
|  * | ||||
|  * 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, 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., 59 Temple Place - Suite 330, Boston, MA | ||||
|  * 02111-1307, USA. | ||||
| * | ||||
|  * Author: David Zeuthen <davidz@redhat.com> | ||||
|  */ | ||||
| 
 | ||||
| const Lang = imports.lang; | ||||
| const Signals = imports.signals; | ||||
| @@ -13,12 +33,8 @@ const Mainloop = imports.mainloop; | ||||
| const Polkit = imports.gi.Polkit; | ||||
| const PolkitAgent = imports.gi.PolkitAgent; | ||||
| 
 | ||||
| const Components = imports.ui.components; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
| const ShellEntry = imports.ui.shellEntry; | ||||
| const UserMenu = imports.ui.userMenu; | ||||
| 
 | ||||
| const DIALOG_ICON_SIZE = 48; | ||||
| 
 | ||||
| const AuthenticationDialog = new Lang.Class({ | ||||
|     Name: 'AuthenticationDialog', | ||||
| @@ -101,11 +117,9 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|             let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout', | ||||
|                                              vertical: false }); | ||||
|             messageBox.add(userBox); | ||||
|             this._userAvatar = new UserMenu.UserAvatarWidget(this._user, | ||||
|                                                              { iconSize: DIALOG_ICON_SIZE, | ||||
|                                                                styleClass: 'polkit-dialog-user-icon' }); | ||||
|             this._userAvatar.actor.hide(); | ||||
|             userBox.add(this._userAvatar.actor, | ||||
|             this._userIcon = new St.Icon(); | ||||
|             this._userIcon.hide(); | ||||
|             userBox.add(this._userIcon, | ||||
|                         { x_fill:  true, | ||||
|                           y_fill:  false, | ||||
|                           x_align: St.Align.END, | ||||
| @@ -299,9 +313,19 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|     }, | ||||
| 
 | ||||
|     _onUserChanged: function() { | ||||
|         if (this._user.is_loaded && this._userAvatar) { | ||||
|             this._userAvatar.update(); | ||||
|             this._userAvatar.actor.show(); | ||||
|         if (this._user.is_loaded) { | ||||
|             if (this._userIcon) { | ||||
|                 let iconFileName = this._user.get_icon_file(); | ||||
|                 let iconFile = Gio.file_new_for_path(iconFileName); | ||||
|                 let icon; | ||||
|                 if (iconFile.query_exists(null)) { | ||||
|                     icon = new Gio.FileIcon({file: iconFile}); | ||||
|                 } else { | ||||
|                     icon = new Gio.ThemedIcon({name: 'avatar-default'}); | ||||
|                 } | ||||
|                 this._userIcon.set_gicon (icon); | ||||
|                 this._userIcon.show(); | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
| @@ -317,20 +341,11 @@ const AuthenticationAgent = new Lang.Class({ | ||||
|     Name: 'AuthenticationAgent', | ||||
| 
 | ||||
|     _init: function() { | ||||
|         this._currentDialog = null; | ||||
|         this._isCompleting = false; | ||||
|         this._handle = null; | ||||
|         this._native = new Shell.PolkitAuthenticationAgent(); | ||||
|         this._native.connect('initiate', Lang.bind(this, this._onInitiate)); | ||||
|         this._native.connect('cancel', Lang.bind(this, this._onCancel)); | ||||
|     }, | ||||
| 
 | ||||
|     enable: function() { | ||||
|         this._native.register(); | ||||
|     }, | ||||
| 
 | ||||
|     disable: function() { | ||||
|         this._native.unregister(); | ||||
|         this._currentDialog = null; | ||||
|         this._isCompleting = false; | ||||
|     }, | ||||
| 
 | ||||
|     _onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) { | ||||
| @@ -388,4 +403,6 @@ const AuthenticationAgent = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
| 
 | ||||
| const Component = AuthenticationAgent; | ||||
| function init() { | ||||
|     let agent = new AuthenticationAgent(); | ||||
| } | ||||
| @@ -37,13 +37,12 @@ const PopupBaseMenuItem = new Lang.Class({ | ||||
|                                          activate: true, | ||||
|                                          hover: true, | ||||
|                                          sensitive: true, | ||||
|                                          style_class: null, | ||||
|                                          can_focus: true | ||||
|                                          style_class: null | ||||
|                                        }); | ||||
|         this.actor = new Shell.GenericContainer({ style_class: 'popup-menu-item', | ||||
|                                                   reactive: params.reactive, | ||||
|                                                   track_hover: params.reactive, | ||||
|                                                   can_focus: params.can_focus, | ||||
|                                                   can_focus: params.reactive, | ||||
|                                                   accessible_role: Atk.Role.MENU_ITEM}); | ||||
|         this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); | ||||
|         this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); | ||||
| @@ -61,9 +60,6 @@ const PopupBaseMenuItem = new Lang.Class({ | ||||
|  | ||||
|         this.setSensitive(this.sensitive); | ||||
|  | ||||
|         if (!this._activatable) | ||||
|             this.actor.add_style_class_name('popup-inactive-menu-item'); | ||||
|  | ||||
|         if (params.style_class) | ||||
|             this.actor.add_style_class_name(params.style_class); | ||||
|  | ||||
| @@ -73,9 +69,10 @@ const PopupBaseMenuItem = new Lang.Class({ | ||||
|         } | ||||
|         if (params.reactive && params.hover) | ||||
|             this.actor.connect('notify::hover', Lang.bind(this, this._onHoverChanged)); | ||||
|  | ||||
|         this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn)); | ||||
|         this.actor.connect('key-focus-out', Lang.bind(this, this._onKeyFocusOut)); | ||||
|         if (params.reactive) { | ||||
|             this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn)); | ||||
|             this.actor.connect('key-focus-out', Lang.bind(this, this._onKeyFocusOut)); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onStyleChanged: function (actor) { | ||||
| @@ -137,7 +134,6 @@ const PopupBaseMenuItem = new Lang.Class({ | ||||
|  | ||||
|         this.sensitive = sensitive; | ||||
|         this.actor.reactive = sensitive; | ||||
|         this.actor.can_focus = sensitive; | ||||
|  | ||||
|         this.emit('sensitive-changed', sensitive); | ||||
|     }, | ||||
| @@ -183,14 +179,12 @@ const PopupBaseMenuItem = new Lang.Class({ | ||||
|             this._dot = new St.DrawingArea({ style_class: 'popup-menu-item-dot' }); | ||||
|             this._dot.connect('repaint', Lang.bind(this, this._onRepaintDot)); | ||||
|             this.actor.add_actor(this._dot); | ||||
|             this.actor.add_accessible_state (Atk.StateType.CHECKED); | ||||
|         } else { | ||||
|             if (!this._dot) | ||||
|                 return; | ||||
|  | ||||
|             this._dot.destroy(); | ||||
|             this._dot = null; | ||||
|             this.actor.remove_accessible_state (Atk.StateType.CHECKED); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -401,8 +395,7 @@ const PopupSeparatorMenuItem = new Lang.Class({ | ||||
|     Extends: PopupBaseMenuItem, | ||||
|  | ||||
|     _init: function () { | ||||
|         this.parent({ reactive: false, | ||||
|                       can_focus: false}); | ||||
|         this.parent({ reactive: false }); | ||||
|  | ||||
|         this._drawingArea = new St.DrawingArea({ style_class: 'popup-separator-menu-item' }); | ||||
|         this.addActor(this._drawingArea, { span: -1, expand: true }); | ||||
| @@ -721,8 +714,7 @@ const Switch = new Lang.Class({ | ||||
|  | ||||
|     _init: function(state) { | ||||
|         this.actor = new St.Bin({ style_class: 'toggle-switch', | ||||
|                                   accessible_role: Atk.Role.CHECK_BOX, | ||||
|                                   can_focus: true }); | ||||
|                                   accessible_role: Atk.Role.CHECK_BOX}); | ||||
|         // Translators: this MUST be either "toggle-switch-us" | ||||
|         // (for toggle switches containing the English words | ||||
|         // "ON" and "OFF") or "toggle-switch-intl" (for toggle | ||||
| @@ -766,7 +758,7 @@ const PopupSwitchMenuItem = new Lang.Class({ | ||||
|                       { expand: true, span: -1, align: St.Align.END }); | ||||
|  | ||||
|         this._statusLabel = new St.Label({ text: '', | ||||
|                                            style_class: 'popup-status-menu-item' | ||||
|                                            style_class: 'popup-inactive-menu-item' | ||||
|                                          }); | ||||
|         this._statusBin.child = this._switch.actor; | ||||
|     }, | ||||
| @@ -870,15 +862,13 @@ const PopupMenuBase = new Lang.Class({ | ||||
|         // for the menu which causes its prelight state to freeze | ||||
|         this.blockSourceEvents = false; | ||||
|  | ||||
|         // Can be set while a menu is up to let all events through without special | ||||
|         // menu handling useful for scrollbars in menus, and probably not otherwise. | ||||
|         this.passEvents = false; | ||||
|  | ||||
|         this._activeMenuItem = null; | ||||
|         this._childMenus = []; | ||||
|         this._settingsActions = { }; | ||||
|  | ||||
|         this._sessionUpdatedId = Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); | ||||
|     }, | ||||
|  | ||||
|     _sessionUpdated: function() { | ||||
|         this._setSettingsVisibility(Main.sessionMode.allowSettings); | ||||
|     }, | ||||
|  | ||||
|     addAction: function(title, callback) { | ||||
| @@ -892,6 +882,9 @@ const PopupMenuBase = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     addSettingsAction: function(title, desktopFile) { | ||||
|         if (!Main.sessionMode.allowSettings) | ||||
|             return null; | ||||
|  | ||||
|         let menuItem = this.addAction(title, function() { | ||||
|                            let app = Shell.AppSystem.get_default().lookup_setting(desktopFile); | ||||
|  | ||||
| @@ -904,13 +897,12 @@ const PopupMenuBase = new Lang.Class({ | ||||
|                            app.activate(); | ||||
|                        }); | ||||
|  | ||||
|         menuItem.actor.visible = Main.sessionMode.allowSettings; | ||||
|         this._settingsActions[desktopFile] = menuItem; | ||||
|  | ||||
|         return menuItem; | ||||
|     }, | ||||
|  | ||||
|     _setSettingsVisibility: function(visible) { | ||||
|     setSettingsVisibility: function(visible) { | ||||
|         for (let id in this._settingsActions) { | ||||
|             let item = this._settingsActions[id]; | ||||
|             item.actor.visible = visible; | ||||
| @@ -1059,17 +1051,14 @@ const PopupMenuBase = new Lang.Class({ | ||||
|  | ||||
|         if (menuItem instanceof PopupMenuSection) { | ||||
|             this._connectSubMenuSignals(menuItem, menuItem); | ||||
|             menuItem._parentOpenStateChangedId = this.connect('open-state-changed', | ||||
|             menuItem._closingId = this.connect('open-state-changed', | ||||
|                 function(self, open) { | ||||
|                     if (open) | ||||
|                         menuItem.open(); | ||||
|                     else | ||||
|                         menuItem.close(); | ||||
|                     if (!open) | ||||
|                         menuItem.close(BoxPointer.PopupAnimation.FADE); | ||||
|                 }); | ||||
|             menuItem.connect('destroy', Lang.bind(this, function() { | ||||
|                 menuItem.disconnect(menuItem._subMenuActivateId); | ||||
|                 menuItem.disconnect(menuItem._subMenuActiveChangeId); | ||||
|                 this.disconnect(menuItem._parentOpenStateChangedId); | ||||
|  | ||||
|                 this.length--; | ||||
|             })); | ||||
| @@ -1104,8 +1093,7 @@ const PopupMenuBase = new Lang.Class({ | ||||
|         let columnWidths = []; | ||||
|         let items = this.box.get_children(); | ||||
|         for (let i = 0; i < items.length; i++) { | ||||
|             if (!items[i].visible && | ||||
|                 !(items[i]._delegate instanceof PopupSubMenu && items[i-1].visible)) | ||||
|             if (!items[i].visible) | ||||
|                 continue; | ||||
|             if (items[i]._delegate instanceof PopupBaseMenuItem || items[i]._delegate instanceof PopupMenuBase) { | ||||
|                 let itemColumnWidths = items[i]._delegate.getColumnWidths(); | ||||
| @@ -1180,9 +1168,6 @@ const PopupMenuBase = new Lang.Class({ | ||||
|         this.actor.destroy(); | ||||
|  | ||||
|         this.emit('destroy'); | ||||
|  | ||||
|         Main.sessionMode.disconnect(this._sessionUpdatedId); | ||||
|         this._sessionUpdatedId = 0; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(PopupMenuBase.prototype); | ||||
| @@ -1269,14 +1254,13 @@ const PopupMenu = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     close: function(animate) { | ||||
|         if (!this.isOpen) | ||||
|             return; | ||||
|  | ||||
|         if (this._activeMenuItem) | ||||
|             this._activeMenuItem.setActive(false); | ||||
|  | ||||
|         if (this._boxPointer.actor.visible) | ||||
|             this._boxPointer.hide(animate); | ||||
|  | ||||
|         if (!this.isOpen) | ||||
|             return; | ||||
|         this._boxPointer.hide(animate); | ||||
|  | ||||
|         this.isOpen = false; | ||||
|         this.emit('open-state-changed', false); | ||||
| @@ -1300,6 +1284,24 @@ const PopupSubMenu = new Lang.Class({ | ||||
|                                          hscrollbar_policy: Gtk.PolicyType.NEVER, | ||||
|                                          vscrollbar_policy: Gtk.PolicyType.NEVER }); | ||||
|  | ||||
|         // StScrollbar plays dirty tricks with events, calling | ||||
|         // clutter_set_motion_events_enabled (FALSE) during the scroll; this | ||||
|         // confuses our event tracking, so we just turn it off during the | ||||
|         // scroll. | ||||
|         let vscroll = this.actor.get_vscroll_bar(); | ||||
|         vscroll.connect('scroll-start', | ||||
|                         Lang.bind(this, function() { | ||||
|                                       let topMenu = this._getTopMenu(); | ||||
|                                       if (topMenu) | ||||
|                                           topMenu.passEvents = true; | ||||
|                                   })); | ||||
|         vscroll.connect('scroll-stop', | ||||
|                         Lang.bind(this, function() { | ||||
|                                       let topMenu = this._getTopMenu(); | ||||
|                                       if (topMenu) | ||||
|                                           topMenu.passEvents = false; | ||||
|                                   })); | ||||
|  | ||||
|         this.actor.add_actor(this.box); | ||||
|         this.actor._delegate = this; | ||||
|         this.actor.clip_to_allocation = true; | ||||
| @@ -1349,11 +1351,6 @@ const PopupSubMenu = new Lang.Class({ | ||||
|         this.actor.vscrollbar_policy = | ||||
|             needsScrollbar ? Gtk.PolicyType.AUTOMATIC : Gtk.PolicyType.NEVER; | ||||
|  | ||||
|         if (needsScrollbar) | ||||
|             this.actor.add_style_pseudo_class('scrolled'); | ||||
|         else | ||||
|             this.actor.remove_style_pseudo_class('scrolled'); | ||||
|  | ||||
|         // It looks funny if we animate with a scrollbar (at what point is | ||||
|         // the scrollbar added?) so just skip that case | ||||
|         if (animate && needsScrollbar) | ||||
| @@ -1460,7 +1457,7 @@ const PopupMenuSection = new Lang.Class({ | ||||
|  | ||||
|     // deliberately ignore any attempt to open() or close(), but emit the | ||||
|     // corresponding signal so children can still pick it up | ||||
|     open: function() { this.emit('open-state-changed', true); }, | ||||
|     open: function(animate) { this.emit('open-state-changed', true); }, | ||||
|     close: function() { this.emit('open-state-changed', false); }, | ||||
|  | ||||
|     destroy: function() { | ||||
| @@ -1898,7 +1895,7 @@ const RemoteMenu = new Lang.Class({ | ||||
|             })); | ||||
|         } | ||||
|  | ||||
|         item.actor.reactive = item.actor.can_focus = action.enabled; | ||||
|         item.actor.reactive = action.enabled; | ||||
|  | ||||
|         destroyId = item.connect('destroy', Lang.bind(this, function() { | ||||
|             item.disconnect(destroyId); | ||||
| @@ -2030,7 +2027,7 @@ const RemoteMenu = new Lang.Class({ | ||||
|         if (action.items.length) { | ||||
|             for (let i = 0; i < action.items.length; i++) { | ||||
|                 let item = action.items[i]; | ||||
|                 item.actor.reactive = item.actor.can_focus = action.enabled; | ||||
|                 item.actor.reactive = action.enabled; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -2058,9 +2055,6 @@ const PopupMenuManager = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     addMenu: function(menu, position) { | ||||
|         if (this._findMenu(menu) > -1) | ||||
|             return; | ||||
|  | ||||
|         let menudata = { | ||||
|             menu:              menu, | ||||
|             openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)), | ||||
| @@ -2284,6 +2278,9 @@ const PopupMenuManager = new Lang.Class({ | ||||
|             this._owner.menuEventFilter(event)) | ||||
|             return true; | ||||
|  | ||||
|         if (this._activeMenu != null && this._activeMenu.passEvents) | ||||
|             return false; | ||||
|  | ||||
|         if (this._didPop) { | ||||
|             this._didPop = false; | ||||
|             return true; | ||||
|   | ||||
| @@ -34,30 +34,16 @@ var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface); | ||||
|  | ||||
| function loadRemoteSearchProviders(addProviderCallback) { | ||||
|     let dataDirs = GLib.get_system_data_dirs(); | ||||
|     let loadedProviders = {}; | ||||
|     for (let i = 0; i < dataDirs.length; i++) { | ||||
|         let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', 'search-providers']); | ||||
|         let dir = Gio.file_new_for_path(path); | ||||
|  | ||||
|         dir.query_info_async('standard:type', Gio.FileQueryInfoFlags.NONE, | ||||
|             GLib.PRIORITY_DEFAULT, null, | ||||
|                 function(object, res) { | ||||
|                     let exists = false; | ||||
|                     try { | ||||
|                         object.query_info_finish(res); | ||||
|                         exists = true; | ||||
|                     } catch (e) { | ||||
|                     } | ||||
|  | ||||
|                     if (!exists) | ||||
|                         return; | ||||
|  | ||||
|                     loadRemoteSearchProvidersFromDir(dir, loadedProviders, addProviderCallback); | ||||
|                 }); | ||||
|         if (!dir.query_exists(null)) | ||||
|             continue; | ||||
|         loadRemoteSearchProvidersFromDir(dir, addProviderCallback); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| function loadRemoteSearchProvidersFromDir(dir, loadedProviders, addProviderCallback) { | ||||
| function loadRemoteSearchProvidersFromDir(dir, addProviderCallback) { | ||||
|     let dirPath = dir.get_path(); | ||||
|     FileUtils.listDirAsync(dir, Lang.bind(this, function(files) { | ||||
|         for (let i = 0; i < files.length; i++) { | ||||
| @@ -73,31 +59,35 @@ function loadRemoteSearchProvidersFromDir(dir, loadedProviders, addProviderCallb | ||||
|             if (!keyfile.has_group(KEY_FILE_GROUP)) | ||||
|                 continue; | ||||
|  | ||||
|             let remoteProvider; | ||||
|             let remoteProvider, title; | ||||
|             try { | ||||
|                 let group = KEY_FILE_GROUP; | ||||
|                 let busName = keyfile.get_string(group, 'BusName'); | ||||
|                 let objectPath = keyfile.get_string(group, 'ObjectPath'); | ||||
|  | ||||
|                 if (loadedProviders[objectPath]) | ||||
|                     continue; | ||||
|  | ||||
|                 let appInfo = null; | ||||
|                 try { | ||||
|                     let desktopId = keyfile.get_string(group, 'DesktopId'); | ||||
|                     appInfo = Gio.DesktopAppInfo.new(desktopId); | ||||
|                 } catch (e) { | ||||
|                     log('Ignoring search provider ' + path + ': missing DesktopId'); | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 remoteProvider = new RemoteSearchProvider(appInfo.get_name(), | ||||
|                                                           appInfo.get_icon(), | ||||
|                 let icon; | ||||
|                 if (appInfo) { | ||||
|                     icon = appInfo.get_icon(); | ||||
|                     title = appInfo.get_name(); | ||||
|                 } else { | ||||
|                     let iconName = keyfile.get_string(group, 'Icon'); | ||||
|                     icon = new Gio.ThemedIcon({ name: iconName }); | ||||
|                     title = keyfile.get_locale_string(group, 'Title', null); | ||||
|                 } | ||||
|  | ||||
|                 remoteProvider = new RemoteSearchProvider(title, | ||||
|                                                           icon, | ||||
|                                                           busName, | ||||
|                                                           objectPath); | ||||
|                 loadedProviders[objectPath] = remoteProvider; | ||||
|             } catch(e) { | ||||
|                 log('Failed to add search provider %s: %s'.format(path, e.toString())); | ||||
|                 log('Failed to add search provider "%s": %s'.format(title, e.toString())); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
| @@ -113,20 +103,17 @@ const RemoteSearchProvider = new Lang.Class({ | ||||
|  | ||||
|     _init: function(title, icon, dbusName, dbusPath) { | ||||
|         this._proxy = new SearchProviderProxy(Gio.DBus.session, | ||||
|             dbusName, dbusPath, Lang.bind(this, this._onProxyConstructed)); | ||||
|                                               dbusName, dbusPath); | ||||
|  | ||||
|         this.parent(title.toUpperCase()); | ||||
|         this._cancellable = new Gio.Cancellable(); | ||||
|     }, | ||||
|  | ||||
|     _onProxyConstructed: function(proxy) { | ||||
|         // Do nothing | ||||
|     }, | ||||
|  | ||||
|     createIcon: function(size, meta) { | ||||
|         if (meta['gicon']) { | ||||
|             return new St.Icon({ gicon: Gio.icon_new_for_string(meta['gicon']), | ||||
|                                  icon_size: size }); | ||||
|                                  icon_size: size, | ||||
|                                  icon_type: St.IconType.FULLCOLOR }); | ||||
|         } else if (meta['icon-data']) { | ||||
|             let [width, height, rowStride, hasAlpha, | ||||
|                  bitsPerSample, nChannels, data] = meta['icon-data']; | ||||
| @@ -137,7 +124,8 @@ const RemoteSearchProvider = new Lang.Class({ | ||||
|  | ||||
|         // Ugh, but we want to fall back to something ... | ||||
|         return new St.Icon({ icon_name: 'text-x-generic', | ||||
|                              icon_size: size }); | ||||
|                              icon_size: size, | ||||
|                              icon_type: St.IconType.FULLCOLOR }); | ||||
|     }, | ||||
|  | ||||
|     _getResultsFinished: function(results, error) { | ||||
|   | ||||
| @@ -202,12 +202,11 @@ const RunDialog = new Lang.Class({ | ||||
|  | ||||
|  | ||||
|         let label = new St.Label({ style_class: 'run-dialog-label', | ||||
|                                    text: _("Enter a Command") }); | ||||
|                                    text: _("Please enter a command:") }); | ||||
|  | ||||
|         this.contentLayout.add(label, { y_align: St.Align.START }); | ||||
|  | ||||
|         let entry = new St.Entry({ style_class: 'run-dialog-entry', | ||||
|                                    can_focus: true }); | ||||
|         let entry = new St.Entry({ style_class: 'run-dialog-entry' }); | ||||
|         ShellEntry.addContextMenu(entry); | ||||
|  | ||||
|         entry.label_actor = label; | ||||
| @@ -220,9 +219,7 @@ const RunDialog = new Lang.Class({ | ||||
|  | ||||
|         this.contentLayout.add(this._errorBox, { expand: true }); | ||||
|  | ||||
|         let errorIcon = new St.Icon({ icon_name: 'dialog-error-symbolic', | ||||
|                                       icon_size: 24, | ||||
|                                       style_class: 'run-dialog-error-icon' }); | ||||
|         let errorIcon = new St.Icon({ icon_name: 'dialog-error', icon_size: 24, style_class: 'run-dialog-error-icon' }); | ||||
|  | ||||
|         this._errorBox.add(errorIcon, { y_align: St.Align.MIDDLE }); | ||||
|  | ||||
| @@ -237,10 +234,6 @@ const RunDialog = new Lang.Class({ | ||||
|  | ||||
|         this._errorBox.hide(); | ||||
|  | ||||
|         this.setButtons([{ action: Lang.bind(this, this.close), | ||||
|                            label: _("Close"), | ||||
|                            key: Clutter.Escape }]); | ||||
|  | ||||
|         this._pathCompleter = new Gio.FilenameCompleter(); | ||||
|         this._commandCompleter = new CommandCompleter(); | ||||
|         this._group.connect('notify::visible', Lang.bind(this._commandCompleter, this._commandCompleter.update)); | ||||
| @@ -251,12 +244,20 @@ const RunDialog = new Lang.Class({ | ||||
|             let symbol = e.get_key_symbol(); | ||||
|             if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) { | ||||
|                 this.popModal(); | ||||
|                 this._run(o.get_text(), | ||||
|                           e.get_state() & Clutter.ModifierType.CONTROL_MASK); | ||||
|                 if (!this._commandError || | ||||
|                     !this.pushModal()) | ||||
|                 if (e.get_state() & Clutter.ModifierType.CONTROL_MASK) | ||||
|                     this._run(o.get_text(), true); | ||||
|                 else | ||||
|                     this._run(o.get_text(), false); | ||||
|                 if (!this._commandError) | ||||
|                     this.close(); | ||||
|  | ||||
|                 else { | ||||
|                     if (!this.pushModal()) | ||||
|                         this.close(); | ||||
|                 } | ||||
|                 return true; | ||||
|             } | ||||
|             if (symbol == Clutter.Escape) { | ||||
|                 this.close(); | ||||
|                 return true; | ||||
|             } | ||||
|             if (symbol == Clutter.slash) { | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -10,6 +10,8 @@ const Util = imports.misc.util; | ||||
| const FileUtils = imports.misc.fileUtils; | ||||
| const Main = imports.ui.main; | ||||
|  | ||||
| const DISABLED_OPEN_SEARCH_PROVIDERS_KEY = 'disabled-open-search-providers'; | ||||
|  | ||||
| // Not currently referenced by the search API, but | ||||
| // this enumeration can be useful for provider | ||||
| // implementations. | ||||
| @@ -167,6 +169,99 @@ const SearchProvider = new Lang.Class({ | ||||
| }); | ||||
| Signals.addSignalMethods(SearchProvider.prototype); | ||||
|  | ||||
| const OpenSearchSystem = new Lang.Class({ | ||||
|     Name: 'OpenSearchSystem', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._providers = []; | ||||
|         global.settings.connect('changed::' + DISABLED_OPEN_SEARCH_PROVIDERS_KEY, Lang.bind(this, this._refresh)); | ||||
|         this._refresh(); | ||||
|     }, | ||||
|  | ||||
|     getProviders: function() { | ||||
|         let res = []; | ||||
|         for (let i = 0; i < this._providers.length; i++) | ||||
|             res.push({ id: i, name: this._providers[i].name }); | ||||
|  | ||||
|         return res; | ||||
|     }, | ||||
|  | ||||
|     setSearchTerms: function(terms) { | ||||
|         this._terms = terms; | ||||
|     }, | ||||
|  | ||||
|     _checkSupportedProviderLanguage: function(provider) { | ||||
|         if (provider.url.search(/{language}/) == -1) | ||||
|             return true; | ||||
|  | ||||
|         let langs = GLib.get_language_names(); | ||||
|  | ||||
|         langs.push('en'); | ||||
|         let lang = null; | ||||
|         for (let i = 0; i < langs.length; i++) { | ||||
|             for (let k = 0; k < provider.langs.length; k++) { | ||||
|                 if (langs[i] == provider.langs[k]) | ||||
|                     lang = langs[i]; | ||||
|             } | ||||
|             if (lang) | ||||
|                 break; | ||||
|         } | ||||
|         provider.lang = lang; | ||||
|         return lang != null; | ||||
|     }, | ||||
|  | ||||
|     activateResult: function(id, params) { | ||||
|         let searchTerms = this._terms.join(' '); | ||||
|  | ||||
|         let url = this._providers[id].url.replace('{searchTerms}', encodeURIComponent(searchTerms)); | ||||
|         if (url.match('{language}')) | ||||
|             url = url.replace('{language}', this._providers[id].lang); | ||||
|  | ||||
|         try { | ||||
|             Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context()); | ||||
|         } catch (e) { | ||||
|             // TODO: remove this after glib will be removed from moduleset | ||||
|             // In the default jhbuild, gio is in our prefix but gvfs is not | ||||
|             Util.spawn(['gvfs-open', url]) | ||||
|         } | ||||
|  | ||||
|         Main.overview.hide(); | ||||
|     }, | ||||
|  | ||||
|     _addProvider: function(fileName) { | ||||
|         let path = global.datadir + '/open-search-providers/' + fileName; | ||||
|         let source = Shell.get_file_contents_utf8_sync(path); | ||||
|         let [success, name, url, langs, icon_uri] = Shell.parse_search_provider(source); | ||||
|         let provider ={ name: name, | ||||
|                         url: url, | ||||
|                         id: this._providers.length, | ||||
|                         icon_uri: icon_uri, | ||||
|                         langs: langs }; | ||||
|         if (this._checkSupportedProviderLanguage(provider)) { | ||||
|             this._providers.push(provider); | ||||
|             this.emit('changed'); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _refresh: function() { | ||||
|         this._providers = []; | ||||
|         let names = global.settings.get_strv(DISABLED_OPEN_SEARCH_PROVIDERS_KEY); | ||||
|         let file = Gio.file_new_for_path(global.datadir + '/open-search-providers'); | ||||
|         FileUtils.listDirAsync(file, Lang.bind(this, function(files) { | ||||
|             for (let i = 0; i < files.length; i++) { | ||||
|                 let enabled = true; | ||||
|                 let name = files[i].get_name(); | ||||
|                 for (let k = 0; k < names.length; k++) | ||||
|                     if (names[k] == name) | ||||
|                         enabled = false; | ||||
|                 if (enabled) | ||||
|                     this._addProvider(name); | ||||
|             } | ||||
|         })); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(OpenSearchSystem.prototype); | ||||
|  | ||||
| const SearchSystem = new Lang.Class({ | ||||
|     Name: 'SearchSystem', | ||||
|  | ||||
|   | ||||
| @@ -173,9 +173,10 @@ const GridSearchResults = new Lang.Class({ | ||||
| const SearchResults = new Lang.Class({ | ||||
|     Name: 'SearchResults', | ||||
|  | ||||
|     _init: function(searchSystem) { | ||||
|     _init: function(searchSystem, openSearchSystem) { | ||||
|         this._searchSystem = searchSystem; | ||||
|         this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults)); | ||||
|         this._openSearchSystem = openSearchSystem; | ||||
|  | ||||
|         this.actor = new St.BoxLayout({ name: 'searchResults', | ||||
|                                         vertical: true }); | ||||
| @@ -190,7 +191,7 @@ const SearchResults = new Lang.Class({ | ||||
|         scrollView.add_actor(this._content); | ||||
|  | ||||
|         this.actor.add(scrollView, { x_fill: true, | ||||
|                                      y_fill: true, | ||||
|                                      y_fill: false, | ||||
|                                      expand: true, | ||||
|                                      x_align: St.Align.START, | ||||
|                                      y_align: St.Align.START }); | ||||
| @@ -205,26 +206,70 @@ const SearchResults = new Lang.Class({ | ||||
|             })); | ||||
|  | ||||
|         this._statusText = new St.Label({ style_class: 'search-statustext' }); | ||||
|         this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE, | ||||
|                                        y_align: St.Align.MIDDLE }); | ||||
|         this._content.add(this._statusBin, { expand: true }); | ||||
|         this._statusBin.add_actor(this._statusText); | ||||
|         this._content.add(this._statusText); | ||||
|         this._providers = this._searchSystem.getProviders(); | ||||
|         this._providerMeta = []; | ||||
|         for (let i = 0; i < this._providers.length; i++) { | ||||
|             this.createProviderMeta(this._providers[i]); | ||||
|         } | ||||
|         this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' }); | ||||
|         this.actor.add(this._searchProvidersBox); | ||||
|  | ||||
|         this._openSearchProviders = []; | ||||
|         this._openSearchSystem.connect('changed', Lang.bind(this, this._updateOpenSearchProviderButtons)); | ||||
|         this._updateOpenSearchProviderButtons(); | ||||
|  | ||||
|         this._highlightDefault = false; | ||||
|         this._defaultResult = null; | ||||
|     }, | ||||
|  | ||||
|     _updateOpenSearchProviderButtons: function() { | ||||
|         for (let i = 0; i < this._openSearchProviders.length; i++) | ||||
|             this._openSearchProviders[i].actor.destroy(); | ||||
|         this._openSearchProviders = this._openSearchSystem.getProviders(); | ||||
|         for (let i = 0; i < this._openSearchProviders.length; i++) | ||||
|             this._createOpenSearchProviderButton(this._openSearchProviders[i]); | ||||
|     }, | ||||
|  | ||||
|     _createOpenSearchProviderButton: function(provider) { | ||||
|         let button = new St.Button({ style_class: 'dash-search-button', | ||||
|                                      reactive: true, | ||||
|                                      can_focus: true, | ||||
|                                      x_fill: true, | ||||
|                                      y_align: St.Align.MIDDLE }); | ||||
|         let bin = new St.Bin({ x_fill: false, | ||||
|                                x_align:St.Align.MIDDLE }); | ||||
|         button.connect('clicked', Lang.bind(this, function() { | ||||
|             this._openSearchSystem.activateResult(provider.id); | ||||
|         })); | ||||
|         let title = new St.Label({ text: provider.name, | ||||
|                                    style_class: 'dash-search-button-label' }); | ||||
|  | ||||
|         button.label_actor = title; | ||||
|         bin.set_child(title); | ||||
|         button.set_child(bin); | ||||
|         provider.actor = button; | ||||
|  | ||||
|         button.setSelected = function(selected) { | ||||
|             if (selected) | ||||
|                 button.add_style_pseudo_class('selected'); | ||||
|             else | ||||
|                 button.remove_style_pseudo_class('selected'); | ||||
|         }; | ||||
|         button.activate = Lang.bind(this, function() { | ||||
|             this._openSearchSystem.activateResult(provider.id); | ||||
|         }); | ||||
|         button.actor = button; | ||||
|  | ||||
|         this._searchProvidersBox.add(button); | ||||
|     }, | ||||
|  | ||||
|     createProviderMeta: function(provider) { | ||||
|         let providerBox = new St.BoxLayout({ style_class: 'search-section', | ||||
|                                              vertical: true }); | ||||
|         let title = new St.Label({ style_class: 'search-section-header', | ||||
|                                    text: provider.title }); | ||||
|         providerBox.add(title, { x_fill: false, x_align: St.Align.START }); | ||||
|         providerBox.add(title); | ||||
|  | ||||
|         let resultDisplayBin = new St.Bin({ style_class: 'search-section-results', | ||||
|                                             x_fill: true, | ||||
| @@ -266,18 +311,20 @@ const SearchResults = new Lang.Class({ | ||||
|  | ||||
|     reset: function() { | ||||
|         this._searchSystem.reset(); | ||||
|         this._statusBin.hide(); | ||||
|         this._statusText.hide(); | ||||
|         this._clearDisplay(); | ||||
|     }, | ||||
|  | ||||
|     startingSearch: function() { | ||||
|         this.reset(); | ||||
|         this._statusText.set_text(_("Searching...")); | ||||
|         this._statusBin.show(); | ||||
|         this._statusText.show(); | ||||
|     }, | ||||
|  | ||||
|     doSearch: function (searchString) { | ||||
|         this._searchSystem.updateSearch(searchString); | ||||
|         let terms = this._searchSystem.getTerms(); | ||||
|         this._openSearchSystem.setSearchTerms(terms); | ||||
|     }, | ||||
|  | ||||
|     _metaForProvider: function(provider) { | ||||
| @@ -300,6 +347,9 @@ const SearchResults = new Lang.Class({ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (!newDefaultResult) | ||||
|             newDefaultResult = this._searchProvidersBox.get_first_child(); | ||||
|  | ||||
|         if (newDefaultResult != this._defaultResult) { | ||||
|             if (this._defaultResult) | ||||
|                 this._defaultResult.setSelected(false); | ||||
| @@ -320,10 +370,10 @@ const SearchResults = new Lang.Class({ | ||||
|             } | ||||
|  | ||||
|         if (!haveResults) { | ||||
|             this._statusText.set_text(_("No results.")); | ||||
|             this._statusBin.show(); | ||||
|             this._statusText.set_text(_("No matching results.")); | ||||
|             this._statusText.show(); | ||||
|         } else { | ||||
|             this._statusBin.hide(); | ||||
|             this._statusText.hide(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|   | ||||
| @@ -1,161 +1,135 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Lang = imports.lang; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const Config = imports.misc.config; | ||||
| const Main = imports.ui.main; | ||||
| const Params = imports.misc.params; | ||||
|  | ||||
| const DEFAULT_MODE = 'restrictive'; | ||||
|  | ||||
| const STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION = { | ||||
|     'a11y': imports.ui.status.accessibility.ATIndicator, | ||||
|     'volume': imports.ui.status.volume.Indicator, | ||||
|     'battery': imports.ui.status.power.Indicator, | ||||
|     'keyboard': imports.ui.status.keyboard.InputSourceIndicator, | ||||
|     'userMenu': imports.ui.userMenu.UserMenuButton | ||||
| }; | ||||
|  | ||||
| if (Config.HAVE_BLUETOOTH) | ||||
|     STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['bluetooth'] = | ||||
|         imports.ui.status.bluetooth.Indicator; | ||||
|  | ||||
| try { | ||||
|     STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['network'] = | ||||
|         imports.ui.status.network.NMApplet; | ||||
| } catch(e) { | ||||
|     log('NMApplet is not supported. It is possible that your NetworkManager version is too old'); | ||||
| } | ||||
|  | ||||
|  | ||||
| const DEFAULT_MODE = 'user'; | ||||
|  | ||||
| const _modes = { | ||||
|     'restrictive': { | ||||
|         hasOverview: false, | ||||
|         showCalendarEvents: false, | ||||
|         allowSettings: false, | ||||
|         allowExtensions: false, | ||||
|         allowKeybindingsWhenModal: false, | ||||
|         hasRunDialog: false, | ||||
|         hasWorkspaces: false, | ||||
|         hasWindows: false, | ||||
|         hasNotifications: false, | ||||
|         isLocked: false, | ||||
|         isGreeter: false, | ||||
|         isPrimary: false, | ||||
|         unlockDialog: null, | ||||
|         components: [], | ||||
|         panel: { | ||||
|             left: [], | ||||
|             center: [], | ||||
|             right: [] | ||||
|         }, | ||||
|     }, | ||||
|     'gdm': { hasOverview: false, | ||||
|              hasAppMenu: false, | ||||
|              showCalendarEvents: false, | ||||
|              allowSettings: false, | ||||
|              allowExtensions: false, | ||||
|              allowKeybindingsWhenModal: true, | ||||
|              hasRunDialog: false, | ||||
|              hasWorkspaces: false, | ||||
|              createSession: Main.createGDMSession, | ||||
|              createUnlockDialog: Main.createGDMLoginDialog, | ||||
|              extraStylesheet: null, | ||||
|              statusArea: { | ||||
|                  order: [ | ||||
|                      'a11y', 'display', 'keyboard', | ||||
|                      'volume', 'battery', 'powerMenu' | ||||
|                  ], | ||||
|                  implementation: { | ||||
|                      'a11y': imports.ui.status.accessibility.ATIndicator, | ||||
|                      'volume': imports.ui.status.volume.Indicator, | ||||
|                      'battery': imports.ui.status.power.Indicator, | ||||
|                      'keyboard': imports.ui.status.keyboard.InputSourceIndicator, | ||||
|                      'powerMenu': imports.gdm.powerMenu.PowerMenuButton | ||||
|                  } | ||||
|              } | ||||
|            }, | ||||
|  | ||||
|     'gdm': { | ||||
|         allowKeybindingsWhenModal: true, | ||||
|         hasNotifications: true, | ||||
|         isGreeter: true, | ||||
|         isPrimary: true, | ||||
|         unlockDialog: imports.gdm.loginDialog.LoginDialog, | ||||
|         components: ['polkitAgent', 'mediaKeysManager'], | ||||
|         panel: { | ||||
|             left: ['logo'], | ||||
|             center: ['dateMenu'], | ||||
|             right: ['a11y', 'display', 'keyboard', | ||||
|                     'volume', 'battery', 'powerMenu'] | ||||
|         } | ||||
|     }, | ||||
|     'initial-setup': { hasOverview: false, | ||||
|                        hasAppMenu: false, | ||||
|                        showCalendarEvents: false, | ||||
|                        allowSettings: false, | ||||
|                        allowExtensions: false, | ||||
|                        allowKeybindingsWhenModal: false, | ||||
|                        hasRunDialog: false, | ||||
|                        hasWorkspaces: false, | ||||
|                        createSession: Main.createInitialSetupSession, | ||||
|                        extraStylesheet: null, | ||||
|                        statusArea: { | ||||
|                            order: [ | ||||
|                                'a11y', 'keyboard', 'volume' | ||||
|                            ], | ||||
|                            implementation: { | ||||
|                                'a11y': imports.ui.status.accessibility.ATIndicator, | ||||
|                                'keyboard': imports.ui.status.keyboard.XKBIndicator, | ||||
|                                'volume': imports.ui.status.volume.Indicator | ||||
|                         } | ||||
|                 } | ||||
|            }, | ||||
|  | ||||
|     'lock-screen': { | ||||
|         isLocked: true, | ||||
|         isGreeter: undefined, | ||||
|         unlockDialog: undefined, | ||||
|         components: ['polkitAgent', 'telepathyClient', 'mediaKeysManager'], | ||||
|         panel: { | ||||
|             left: ['userMenu'], | ||||
|             center: [], | ||||
|             right: ['lockScreen'] | ||||
|         }, | ||||
|     }, | ||||
|  | ||||
|     'unlock-dialog': { | ||||
|         isLocked: true, | ||||
|         unlockDialog: undefined, | ||||
|         components: ['polkitAgent', 'telepathyClient', 'mediaKeysManager'], | ||||
|         panel: { | ||||
|             left: ['userMenu'], | ||||
|             center: [], | ||||
|             right: ['a11y', 'keyboard', 'lockScreen'] | ||||
|         }, | ||||
|     }, | ||||
|  | ||||
|     'initial-setup': { | ||||
|         isPrimary: true, | ||||
|         components: ['keyring', 'mediaKeysManager'], | ||||
|         panel: { | ||||
|             left: [], | ||||
|             center: ['dateMenu'], | ||||
|             right: ['a11y', 'keyboard', 'volume'] | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     'user': { | ||||
|         hasOverview: true, | ||||
|         showCalendarEvents: true, | ||||
|         allowSettings: true, | ||||
|         allowExtensions: true, | ||||
|         hasRunDialog: true, | ||||
|         hasWorkspaces: true, | ||||
|         hasWindows: true, | ||||
|         hasNotifications: true, | ||||
|         isLocked: false, | ||||
|         isPrimary: true, | ||||
|         unlockDialog: imports.ui.unlockDialog.UnlockDialog, | ||||
|         components: ['networkAgent', 'polkitAgent', 'telepathyClient', 'keyring', | ||||
|                      'recorder', 'autorunManager', 'automountManager', | ||||
|                      'mediaKeysManager'], | ||||
|         panel: { | ||||
|             left: ['activities', 'appMenu'], | ||||
|             center: ['dateMenu'], | ||||
|             right: ['a11y', 'keyboard', 'volume', 'bluetooth', | ||||
|                     'network', 'battery', 'userMenu'] | ||||
|         } | ||||
|     } | ||||
|     'user': { hasOverview: true, | ||||
|               hasAppMenu: true, | ||||
|               showCalendarEvents: true, | ||||
|               allowSettings: true, | ||||
|               allowExtensions: true, | ||||
|               allowKeybindingsWhenModal: false, | ||||
|               hasRunDialog: true, | ||||
|               hasWorkspaces: true, | ||||
|               createSession: Main.createUserSession, | ||||
|               createUnlockDialog: Main.createSessionUnlockDialog, | ||||
|               extraStylesheet: null, | ||||
|               statusArea: { | ||||
|                   order: [ | ||||
|                       'input-method', 'a11y', 'keyboard', 'volume', 'bluetooth', | ||||
|                       'network', 'battery', 'userMenu' | ||||
|                   ], | ||||
|                   implementation: STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION | ||||
|               } | ||||
|             } | ||||
| }; | ||||
|  | ||||
| function listModes() { | ||||
|     let modes = Object.getOwnPropertyNames(_modes); | ||||
|     for (let i = 0; i < modes.length; i++) | ||||
|         if (_modes[modes[i]].isPrimary) | ||||
|             print(modes[i]); | ||||
|         print(modes[i]); | ||||
| } | ||||
|  | ||||
| const SessionMode = new Lang.Class({ | ||||
|     Name: 'SessionMode', | ||||
|  | ||||
|     _init: function() { | ||||
|         global.connect('notify::session-mode', Lang.bind(this, this._sync)); | ||||
|         let mode = _modes[global.session_mode].isPrimary ? global.session_mode | ||||
|                                                          : 'user'; | ||||
|         this._modeStack = [mode]; | ||||
|         this._sync(); | ||||
|     }, | ||||
|         let params = _modes[global.session_mode]; | ||||
|  | ||||
|     pushMode: function(mode) { | ||||
|         this._modeStack.push(mode); | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     popMode: function(mode) { | ||||
|         if (this.currentMode != mode || this._modeStack.length === 1) | ||||
|             throw new Error("Invalid SessionMode.popMode"); | ||||
|         this._modeStack.pop(); | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     switchMode: function(to) { | ||||
|         if (this.currentMode == to) | ||||
|             return; | ||||
|         this._modeStack[this._modeStack.length - 1] = to; | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     get currentMode() { | ||||
|         return this._modeStack[this._modeStack.length - 1]; | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         let params = _modes[this.currentMode]; | ||||
|         params = Params.parse(params, _modes[DEFAULT_MODE]); | ||||
|  | ||||
|         // A simplified version of Lang.copyProperties, handles | ||||
|         // undefined as a special case for "no change / inherit from previous mode" | ||||
|         for (let prop in params) { | ||||
|             if (params[prop] !== undefined) | ||||
|                 this[prop] = params[prop]; | ||||
|         } | ||||
|         this._createSession = params.createSession; | ||||
|         delete params.createSession; | ||||
|         this._createUnlockDialog = params.createUnlockDialog; | ||||
|         delete params.createUnlockDialog; | ||||
|  | ||||
|         this.emit('updated'); | ||||
|     } | ||||
|         Lang.copyProperties(params, this); | ||||
|     }, | ||||
|  | ||||
|     createSession: function() { | ||||
|         if (this._createSession) | ||||
|             this._createSession(); | ||||
|     }, | ||||
|  | ||||
|     createUnlockDialog: function() { | ||||
|         if (this._createUnlockDialog) | ||||
|             return this._createUnlockDialog.apply(this, arguments); | ||||
|         else | ||||
|             return null; | ||||
|     }, | ||||
| }); | ||||
| Signals.addSignalMethods(SessionMode.prototype); | ||||
|   | ||||
| @@ -57,10 +57,7 @@ const ScreenSaverIface = <interface name="org.gnome.ScreenSaver"> | ||||
|     <arg name="active" direction="out" type="b" /> | ||||
| </method> | ||||
| <method name="SetActive"> | ||||
|     <arg name="value" direction="in" type="b" /> | ||||
| </method> | ||||
| <method name="GetActiveTime"> | ||||
|     <arg name="value" direction="out" type="u" /> | ||||
|     <arg name="value" direction="in" type="u" /> | ||||
| </method> | ||||
| <signal name="ActiveChanged"> | ||||
|     <arg name="new_value" type="b" /> | ||||
| @@ -321,11 +318,8 @@ const GnomeShellExtensions = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     ReloadExtension: function(uuid) { | ||||
|         let extension = ExtensionUtils.extensions[uuid]; | ||||
|         if (!extension) | ||||
|             return; | ||||
|  | ||||
|         ExtensionSystem.reloadExtension(extension); | ||||
|         ExtensionSystem.unloadExtension(uuid); | ||||
|         ExtensionSystem.loadExtension(uuid); | ||||
|     }, | ||||
|  | ||||
|     CheckForUpdates: function() { | ||||
| @@ -343,46 +337,29 @@ const GnomeShellExtensions = new Lang.Class({ | ||||
| const ScreenSaverDBus = new Lang.Class({ | ||||
|     Name: 'ScreenSaverDBus', | ||||
|  | ||||
|     _init: function(screenShield) { | ||||
|     _init: function() { | ||||
|         this.parent(); | ||||
|  | ||||
|         this._screenShield = screenShield; | ||||
|         screenShield.connect('lock-status-changed', Lang.bind(this, function(shield) { | ||||
|             this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.locked])); | ||||
|         Main.screenShield.connect('lock-status-changed', Lang.bind(this, function(shield, locked) { | ||||
|             this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [locked])); | ||||
|         })); | ||||
|  | ||||
|         this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this); | ||||
|         this._dbusImpl.export(Gio.DBus.session, '/org/gnome/ScreenSaver'); | ||||
|  | ||||
|         Gio.DBus.session.own_name('org.gnome.ScreenSaver', Gio.BusNameOwnerFlags.REPLACE, null, null); | ||||
|     }, | ||||
|  | ||||
|     LockAsync: function(parameters, invocation) { | ||||
|         let tmpId = this._screenShield.connect('lock-screen-shown', Lang.bind(this, function() { | ||||
|             this._screenShield.disconnect(tmpId); | ||||
|  | ||||
|             invocation.return_value(null); | ||||
|         })); | ||||
|  | ||||
|         this._screenShield.lock(true); | ||||
|     Lock: function() { | ||||
|         Main.screenShield.lock(true); | ||||
|     }, | ||||
|  | ||||
|     SetActive: function(active) { | ||||
|         if (active) | ||||
|             this._screenShield.lock(true); | ||||
|             Main.screenShield.lock(true); | ||||
|         else | ||||
|             this._screenShield.unlock(); | ||||
|             Main.screenShield.unlock(); | ||||
|     }, | ||||
|  | ||||
|     GetActive: function() { | ||||
|         return this._screenShield.locked; | ||||
|     }, | ||||
|  | ||||
|     GetActiveTime: function() { | ||||
|         let started = this._screenShield.activationTime; | ||||
|         if (started > 0) | ||||
|             return Math.floor((GLib.get_monotonic_time() - started) / 1000000); | ||||
|         else | ||||
|             return 0; | ||||
|     }, | ||||
|         return Main.screenShield.locked; | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -76,12 +76,6 @@ const EntryMenu = new Lang.Class({ | ||||
|             this.actor.grab_key_focus(); | ||||
|  | ||||
|         this.parent(); | ||||
|         this._entry.add_style_pseudo_class('focus'); | ||||
|     }, | ||||
|  | ||||
|     close: function() { | ||||
|         this._entry.grab_key_focus(); | ||||
|         this.parent(); | ||||
|     }, | ||||
|  | ||||
|     _updateCopyItem: function() { | ||||
| @@ -132,20 +126,34 @@ function _setMenuAlignment(entry, stageX) { | ||||
|         entry.menu.setSourceAlignment(entryX / entry.width); | ||||
| }; | ||||
|  | ||||
| function _onButtonPressEvent(actor, event, entry) { | ||||
| function _onClicked(action, actor) { | ||||
|     let entry = actor.menu ? actor : actor.get_parent(); | ||||
|  | ||||
|     if (entry.menu.isOpen) { | ||||
|         entry.menu.close(); | ||||
|         return true; | ||||
|     } else if (event.get_button() == 3) { | ||||
|         let [stageX, stageY] = event.get_coords(); | ||||
|     } else if (action.get_button() == 3) { | ||||
|         let [stageX, stageY] = action.get_coords(); | ||||
|         _setMenuAlignment(entry, stageX); | ||||
|         entry.menu.open(); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| function _onLongPress(action, actor, state) { | ||||
|     let entry = actor.menu ? actor : actor.get_parent(); | ||||
|  | ||||
|     if (state == Clutter.LongPressState.QUERY) | ||||
|         return action.get_button() == 1 && !entry.menu.isOpen; | ||||
|  | ||||
|     if (state == Clutter.LongPressState.ACTIVATE) { | ||||
|         let [stageX, stageY] = action.get_coords(); | ||||
|         _setMenuAlignment(entry, stageX); | ||||
|         entry.menu.open(); | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| }; | ||||
|  | ||||
| function _onPopup(actor, entry) { | ||||
| function _onPopup(actor) { | ||||
|     let entry = actor.menu ? actor : actor.get_parent(); | ||||
|     let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1); | ||||
|     if (success) | ||||
|         entry.menu.setSourceAlignment(textX / entry.width); | ||||
| @@ -160,11 +168,20 @@ function addContextMenu(entry, params) { | ||||
|     entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry }); | ||||
|     entry._menuManager.addMenu(entry.menu); | ||||
|  | ||||
|     // Add an event handler to both the entry and its clutter_text; the former | ||||
|     let clickAction; | ||||
|  | ||||
|     // Add a click action to both the entry and its clutter_text; the former | ||||
|     // so padding is included in the clickable area, the latter because the | ||||
|     // event processing of ClutterText prevents event-bubbling. | ||||
|     entry.clutter_text.connect('button-press-event', Lang.bind(null, _onButtonPressEvent, entry)); | ||||
|     entry.connect('button-press-event', Lang.bind(null, _onButtonPressEvent, entry)); | ||||
|     clickAction = new Clutter.ClickAction(); | ||||
|     clickAction.connect('clicked', _onClicked); | ||||
|     clickAction.connect('long-press', _onLongPress); | ||||
|     entry.clutter_text.add_action(clickAction); | ||||
|  | ||||
|     entry.connect('popup-menu', Lang.bind(null, _onPopup, entry)); | ||||
|     clickAction = new Clutter.ClickAction(); | ||||
|     clickAction.connect('clicked', _onClicked); | ||||
|     clickAction.connect('long-press', _onLongPress); | ||||
|     entry.add_action(clickAction); | ||||
|  | ||||
|     entry.connect('popup-menu', _onPopup); | ||||
| } | ||||
|   | ||||
| @@ -240,7 +240,7 @@ const ShellUnmountNotifier = new Lang.Class({ | ||||
|     Extends: MessageTray.Source, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent('', 'media-removable'); | ||||
|         this.parent('', 'media-removable', St.IconType.FULLCOLOR); | ||||
|  | ||||
|         this._notification = null; | ||||
|         Main.messageTray.add(this); | ||||
| @@ -361,12 +361,12 @@ const ShellMountPasswordDialog = new Lang.Class({ | ||||
|         if (strings[1]) | ||||
|             description.set_text(strings[1]); | ||||
|  | ||||
|         this._passwordBox = new St.BoxLayout({ vertical: false, style_class: 'prompt-dialog-password-box' }); | ||||
|         this._passwordBox = new St.BoxLayout({ vertical: false }); | ||||
|         this._messageBox.add(this._passwordBox); | ||||
|  | ||||
|         this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label', | ||||
|                                               text: _("Password") })); | ||||
|         this._passwordBox.add(this._passwordLabel, { y_fill: false, y_align: St.Align.MIDDLE }); | ||||
|                                               text: _("Passphrase") })); | ||||
|         this._passwordBox.add(this._passwordLabel); | ||||
|  | ||||
|         this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry', | ||||
|                                              text: "", | ||||
| @@ -386,7 +386,7 @@ const ShellMountPasswordDialog = new Lang.Class({ | ||||
|  | ||||
|         if (flags & Gio.AskPasswordFlags.SAVING_SUPPORTED) { | ||||
|             this._rememberChoice = new CheckBox.CheckBox(); | ||||
|             this._rememberChoice.getLabelActor().text = _("Remember Password"); | ||||
|             this._rememberChoice.getLabelActor().text = _("Remember Passphrase"); | ||||
|             this._rememberChoice.actor.checked = true; | ||||
|             this._messageBox.add(this._rememberChoice.actor); | ||||
|         } else { | ||||
|   | ||||
| @@ -36,10 +36,10 @@ const ATIndicator = new Lang.Class({ | ||||
|     Extends: PanelMenu.SystemStatusButton, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent('preferences-desktop-accessibility-symbolic', _("Accessibility")); | ||||
|         this.parent('preferences-desktop-accessibility', _("Accessibility")); | ||||
|  | ||||
|         this._highContrast = this._buildHCItem(); | ||||
|         this.menu.addMenuItem(this._highContrast); | ||||
|         let highContrast = this._buildHCItem(); | ||||
|         this.menu.addMenuItem(highContrast); | ||||
|  | ||||
|         let magnifier = this._buildItem(_("Zoom"), APPLICATIONS_SCHEMA, | ||||
|                                                    'screen-magnifier-enabled'); | ||||
| @@ -75,6 +75,10 @@ const ATIndicator = new Lang.Class({ | ||||
|         this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop'); | ||||
|     }, | ||||
|  | ||||
|     setLockedState: function(locked) { | ||||
|         this.menu.setSettingsVisibility(!locked); | ||||
|     }, | ||||
|  | ||||
|     _buildItemExtended: function(string, initial_value, writable, on_set) { | ||||
|         let widget = new PopupMenu.PopupSwitchMenuItem(string, initial_value); | ||||
|         if (!writable) | ||||
| @@ -159,9 +163,5 @@ const ATIndicator = new Lang.Class({ | ||||
|             widget.setToggleState(active); | ||||
|         }); | ||||
|         return widget; | ||||
|     }, | ||||
|  | ||||
|     toggleHighContrast: function() { | ||||
|         this._highContrast.toggle(); | ||||
|     }, | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -24,7 +24,7 @@ const Indicator = new Lang.Class({ | ||||
|     Extends: PanelMenu.SystemStatusButton, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent('bluetooth-disabled-symbolic', _("Bluetooth")); | ||||
|         this.parent('bluetooth-disabled', _("Bluetooth")); | ||||
|  | ||||
|         this._applet = new GnomeBluetoothApplet.Applet(); | ||||
|  | ||||
| @@ -88,6 +88,11 @@ const Indicator = new Lang.Class({ | ||||
|         this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest)); | ||||
|     }, | ||||
|  | ||||
|     setLockedState: function(locked) { | ||||
|         this._isLocked = locked; | ||||
|         this._updateKillswitch(); | ||||
|     }, | ||||
|  | ||||
|     _updateKillswitch: function() { | ||||
|         let current_state = this._applet.killswitch_state; | ||||
|         let on = current_state == GnomeBluetooth.KillswitchState.UNBLOCKED; | ||||
| @@ -102,14 +107,14 @@ const Indicator = new Lang.Class({ | ||||
|             /* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */ | ||||
|             this._killswitch.setStatus(_("hardware disabled")); | ||||
|  | ||||
|         this.actor.visible = has_adapter; | ||||
|         this.actor.visible = !this._isLocked && has_adapter; | ||||
|  | ||||
|         if (on) { | ||||
|             this._discoverable.actor.show(); | ||||
|             this.setIcon('bluetooth-active-symbolic'); | ||||
|             this.setIcon('bluetooth-active'); | ||||
|         } else { | ||||
|             this._discoverable.actor.hide(); | ||||
|             this.setIcon('bluetooth-disabled-symbolic'); | ||||
|             this.setIcon('bluetooth-disabled'); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -301,7 +306,7 @@ const Indicator = new Lang.Class({ | ||||
|  | ||||
|     _ensureSource: function() { | ||||
|         if (!this._source) { | ||||
|             this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active'); | ||||
|             this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active', St.IconType.SYMBOLIC); | ||||
|             Main.messageTray.add(this._source); | ||||
|         } | ||||
|     }, | ||||
| @@ -456,10 +461,7 @@ const PinNotification = new Lang.Class({ | ||||
|  | ||||
|     _canActivateOkButton: function() { | ||||
|         // PINs have a fixed length of 6 | ||||
|         if (this._numeric) | ||||
|             return this._entry.clutter_text.text.length == 6; | ||||
|         else | ||||
|             return true; | ||||
|         return this._entry.clutter_text.text.length == 6; | ||||
|     }, | ||||
|  | ||||
|     grabFocus: function(lockTray) { | ||||
|   | ||||
| @@ -5,7 +5,6 @@ const GLib = imports.gi.GLib; | ||||
| const GnomeDesktop = imports.gi.GnomeDesktop; | ||||
| const Lang = imports.lang; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Signals = imports.signals; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| try { | ||||
| @@ -46,7 +45,6 @@ const IBusManager = new Lang.Class({ | ||||
|         this._panelService = null; | ||||
|         this._engines = {}; | ||||
|         this._ready = false; | ||||
|         this._registerPropertiesId = 0; | ||||
|  | ||||
|         this._nameWatcherId = Gio.DBus.session.watch_name(IBus.SERVICE_IBUS, | ||||
|                                                           Gio.BusNameWatcherFlags.NONE, | ||||
| @@ -65,7 +63,6 @@ const IBusManager = new Lang.Class({ | ||||
|         this._candidatePopup.setPanelService(null); | ||||
|         this._engines = {}; | ||||
|         this._ready = false; | ||||
|         this._registerPropertiesId = 0; | ||||
|     }, | ||||
|  | ||||
|     _onNameAppeared: function() { | ||||
| @@ -103,11 +100,6 @@ const IBusManager = new Lang.Class({ | ||||
|             this._panelService = new IBus.PanelService({ connection: this._ibus.get_connection(), | ||||
|                                                          object_path: IBus.PATH_PANEL }); | ||||
|             this._candidatePopup.setPanelService(this._panelService); | ||||
|             // Need to set this to get 'global-engine-changed' emitions | ||||
|             this._ibus.set_watch_ibus_signal(true); | ||||
|             this._ibus.connect('global-engine-changed', Lang.bind(this, this._resetProperties)); | ||||
|             this._panelService.connect('update-property', Lang.bind(this, this._updateProperty)); | ||||
|             this._resetProperties(); | ||||
|         } else { | ||||
|             this._clear(); | ||||
|             return; | ||||
| @@ -124,39 +116,6 @@ const IBusManager = new Lang.Class({ | ||||
|             this._readyCallback(); | ||||
|     }, | ||||
|  | ||||
|     _resetProperties: function() { | ||||
|         this.emit('properties-registered', null); | ||||
|  | ||||
|         if (this._registerPropertiesId != 0) | ||||
|             return; | ||||
|  | ||||
|         this._registerPropertiesId = | ||||
|             this._panelService.connect('register-properties', Lang.bind(this, function(p, props) { | ||||
|                 if (!props.get(0)) | ||||
|                     return; | ||||
|  | ||||
|                 this._panelService.disconnect(this._registerPropertiesId); | ||||
|                 this._registerPropertiesId = 0; | ||||
|  | ||||
|                 this.emit('properties-registered', props); | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
|     _updateProperty: function(panel, prop) { | ||||
|         this.emit('property-updated', prop); | ||||
|     }, | ||||
|  | ||||
|     activateProperty: function(key, state) { | ||||
|         this._panelService.property_activate(key, state); | ||||
|     }, | ||||
|  | ||||
|     hasProperties: function(id) { | ||||
|         if (id == 'anthy') | ||||
|             return true; | ||||
|  | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     getEngineDesc: function(id) { | ||||
|         if (!IBus || !this._ready) | ||||
|             return null; | ||||
| @@ -164,7 +123,6 @@ const IBusManager = new Lang.Class({ | ||||
|         return this._engines[id]; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(IBusManager.prototype); | ||||
|  | ||||
| const LayoutMenuItem = new Lang.Class({ | ||||
|     Name: 'LayoutMenuItem', | ||||
| @@ -177,7 +135,6 @@ const LayoutMenuItem = new Lang.Class({ | ||||
|         this.indicator = new St.Label({ text: shortName }); | ||||
|         this.addActor(this.label); | ||||
|         this.addActor(this.indicator); | ||||
|         this.actor.label_actor = this.label; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -185,12 +142,6 @@ const InputSourceIndicator = new Lang.Class({ | ||||
|     Name: 'InputSourceIndicator', | ||||
|     Extends: PanelMenu.Button, | ||||
|  | ||||
|     _propertiesWhitelist: [ | ||||
|         'InputMode', | ||||
|         'TypingMode', | ||||
|         'DictMode' | ||||
|     ], | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent(0.0, _("Keyboard")); | ||||
|  | ||||
| @@ -211,73 +162,54 @@ const InputSourceIndicator = new Lang.Class({ | ||||
|         this._currentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE); | ||||
|         this._xkbInfo = new GnomeDesktop.XkbInfo(); | ||||
|  | ||||
|         this._propSeparator = new PopupMenu.PopupSeparatorMenuItem(); | ||||
|         this.menu.addMenuItem(this._propSeparator); | ||||
|         this._propSection = new PopupMenu.PopupMenuSection(); | ||||
|         this.menu.addMenuItem(this._propSection); | ||||
|         this._propSection.actor.hide(); | ||||
|  | ||||
|         this._properties = null; | ||||
|  | ||||
|         this._ibusManager = new IBusManager(Lang.bind(this, this._inputSourcesChanged)); | ||||
|         this._ibusManager.connect('properties-registered', Lang.bind(this, this._ibusPropertiesRegistered)); | ||||
|         this._ibusManager.connect('property-updated', Lang.bind(this, this._ibusPropertyUpdated)); | ||||
|  | ||||
|         this._inputSourcesChanged(); | ||||
|  | ||||
|         this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|         this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, this._showLayout)); | ||||
|  | ||||
|         Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); | ||||
|         this._sessionUpdated(); | ||||
|  | ||||
|         this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop'); | ||||
|     }, | ||||
|  | ||||
|     _sessionUpdated: function() { | ||||
|         // re-using "allowSettings" for the keyboard layout is a bit shady, | ||||
|         // but at least for now it is used as "allow popping up windows | ||||
|         // from shell menus"; we can always add a separate sessionMode | ||||
|         // option if need arises. | ||||
|         this._showLayoutItem.actor.visible = Main.sessionMode.allowSettings; | ||||
|         if (Main.sessionMode.allowSettings) { | ||||
|             this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|             this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, this._showLayout)); | ||||
|         } | ||||
|         this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop'); | ||||
|     }, | ||||
|  | ||||
|     setLockedState: function(locked) { | ||||
|         this._showLayoutItem.actor.visible = !locked; | ||||
|         this.menu.setSettingsVisibility(!locked); | ||||
|     }, | ||||
|  | ||||
|     _currentInputSourceChanged: function() { | ||||
|         let nVisibleSources = Object.keys(this._layoutItems).length; | ||||
|         if (nVisibleSources < 2) | ||||
|             return; | ||||
|  | ||||
|         let nSources = this._settings.get_value(KEY_INPUT_SOURCES).n_children(); | ||||
|         let newCurrentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE); | ||||
|         let newLayoutItem = this._layoutItems[newCurrentSourceIndex]; | ||||
|         let hasProperties; | ||||
|         if (newCurrentSourceIndex >= nSources) | ||||
|             return; | ||||
|  | ||||
|         if (newLayoutItem) | ||||
|             hasProperties = this._ibusManager.hasProperties(newLayoutItem.ibusEngineId); | ||||
|         else | ||||
|             hasProperties = false; | ||||
|  | ||||
|         if (!newLayoutItem || (nVisibleSources < 2 && !hasProperties)) { | ||||
|             // This source index might be invalid if we weren't able | ||||
|             // to build a menu item for it, so we hide ourselves since | ||||
|             // we can't fix it here. *shrug* | ||||
|  | ||||
|             // We also hide if we have only one visible source unless | ||||
|             // it's an IBus source with properties. | ||||
|         if (!this._layoutItems[newCurrentSourceIndex]) { | ||||
|             // This source index is invalid as we weren't able to | ||||
|             // build a menu item for it, so we hide ourselves since we | ||||
|             // can't fix it here. *shrug* | ||||
|             this.menu.close(); | ||||
|             this.actor.hide(); | ||||
|             return; | ||||
|         } else { | ||||
|             this.actor.show(); | ||||
|         } | ||||
|  | ||||
|         this.actor.show(); | ||||
|  | ||||
|         if (this._layoutItems[this._currentSourceIndex]) { | ||||
|             this._layoutItems[this._currentSourceIndex].setShowDot(false); | ||||
|             this._container.set_skip_paint(this._labelActors[this._currentSourceIndex], true); | ||||
|         } | ||||
|  | ||||
|         newLayoutItem.setShowDot(true); | ||||
|  | ||||
|         let newLabelActor = this._labelActors[newCurrentSourceIndex]; | ||||
|         this._container.set_skip_paint(newLabelActor, false); | ||||
|  | ||||
|         if (hasProperties) | ||||
|             newLabelActor.set_text(newLayoutItem.indicator.get_text()); | ||||
|         this._layoutItems[newCurrentSourceIndex].setShowDot(true); | ||||
|         this._container.set_skip_paint(this._labelActors[newCurrentSourceIndex], false); | ||||
|  | ||||
|         this._currentSourceIndex = newCurrentSourceIndex; | ||||
|     }, | ||||
| @@ -308,12 +240,9 @@ const InputSourceIndicator = new Lang.Class({ | ||||
|             } else if (type == INPUT_SOURCE_TYPE_IBUS) { | ||||
|                 let engineDesc = this._ibusManager.getEngineDesc(id); | ||||
|                 if (engineDesc) { | ||||
|                     let language = IBus.get_language_name(engineDesc.get_language()); | ||||
|  | ||||
|                     info.exists = true; | ||||
|                     info.displayName = language + ' (' + engineDesc.get_longname() + ')'; | ||||
|                     info.shortName = this._makeEngineShortName(engineDesc); | ||||
|                     info.ibusEngineId = id; | ||||
|                     info.displayName = engineDesc.get_longname(); | ||||
|                     info.shortName = engineDesc.get_symbol(); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @@ -328,6 +257,13 @@ const InputSourceIndicator = new Lang.Class({ | ||||
|             infos.push(info); | ||||
|         } | ||||
|  | ||||
|         if (infos.length > 1) { | ||||
|             this.actor.show(); | ||||
|         } else { | ||||
|             this.menu.close(); | ||||
|             this.actor.hide(); | ||||
|         } | ||||
|  | ||||
|         for (let i = 0; i < infos.length; i++) { | ||||
|             let info = infos[i]; | ||||
|             if (infosByShortName[info.shortName].length > 1) { | ||||
| @@ -336,7 +272,6 @@ const InputSourceIndicator = new Lang.Class({ | ||||
|             } | ||||
|  | ||||
|             let item = new LayoutMenuItem(info.displayName, info.shortName); | ||||
|             item.ibusEngineId = info.ibusEngineId; | ||||
|             this._layoutItems[info.sourceIndex] = item; | ||||
|             this.menu.addMenuItem(item, i); | ||||
|             item.connect('activate', Lang.bind(this, function() { | ||||
| @@ -382,146 +317,6 @@ const InputSourceIndicator = new Lang.Class({ | ||||
|         Util.spawn(['gkbd-keyboard-display', '-l', description]); | ||||
|     }, | ||||
|  | ||||
|     _makeEngineShortName: function(engineDesc) { | ||||
|         let symbol = engineDesc.get_symbol(); | ||||
|         if (symbol && symbol[0]) | ||||
|             return symbol; | ||||
|  | ||||
|         let langCode = engineDesc.get_language().split('_', 1)[0]; | ||||
|         if (langCode.length == 2 || langCode.length == 3) | ||||
|             return langCode.toLowerCase(); | ||||
|  | ||||
|         return String.fromCharCode(0x2328); // keyboard glyph | ||||
|     }, | ||||
|  | ||||
|     _propertyWhitelisted: function(prop) { | ||||
|         for (let i = 0; i < this._propertiesWhitelist.length; ++i) { | ||||
|             let key = prop.get_key(); | ||||
|             if (key.substr(0, this._propertiesWhitelist[i].length) == this._propertiesWhitelist[i]) | ||||
|                 return true; | ||||
|         } | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     _ibusPropertiesRegistered: function(im, props) { | ||||
|         this._properties = props; | ||||
|         this._buildPropSection(); | ||||
|     }, | ||||
|  | ||||
|     _ibusPropertyUpdated: function(im, prop) { | ||||
|         if (!this._propertyWhitelisted(prop)) | ||||
|             return; | ||||
|  | ||||
|         if (this._updateSubProperty(this._properties, prop)) | ||||
|             this._buildPropSection(); | ||||
|     }, | ||||
|  | ||||
|     _updateSubProperty: function(props, prop) { | ||||
|         if (!props) | ||||
|             return false; | ||||
|  | ||||
|         let p; | ||||
|         for (let i = 0; (p = props.get(i)) != null; ++i) { | ||||
|             if (p.get_key() == prop.get_key() && p.get_prop_type() == prop.get_prop_type()) { | ||||
|                 p.update(prop); | ||||
|                 return true; | ||||
|             } else if (p.get_prop_type() == IBus.PropType.MENU) { | ||||
|                 if (this._updateSubProperty(p.get_sub_props(), prop)) | ||||
|                     return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     _updateIndicatorLabel: function(text) { | ||||
|         let layoutItem = this._layoutItems[this._currentSourceIndex]; | ||||
|         let hasProperties; | ||||
|  | ||||
|         if (layoutItem) | ||||
|             hasProperties = this._ibusManager.hasProperties(layoutItem.ibusEngineId); | ||||
|         else | ||||
|             hasProperties = false; | ||||
|  | ||||
|         if (hasProperties) | ||||
|             this._labelActors[this._currentSourceIndex].set_text(text); | ||||
|     }, | ||||
|  | ||||
|     _buildPropSection: function() { | ||||
|         this._propSeparator.actor.hide(); | ||||
|         this._propSection.actor.hide(); | ||||
|         this._propSection.removeAll(); | ||||
|  | ||||
|         this._buildPropSubMenu(this._propSection, this._properties); | ||||
|  | ||||
|         if (!this._propSection.isEmpty()) { | ||||
|             this._propSection.actor.show(); | ||||
|             this._propSeparator.actor.show(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _buildPropSubMenu: function(menu, props) { | ||||
|         if (!props) | ||||
|             return; | ||||
|  | ||||
|         let radioGroup = []; | ||||
|         let p; | ||||
|         for (let i = 0; (p = props.get(i)) != null; ++i) { | ||||
|             let prop = p; | ||||
|  | ||||
|             if (!this._propertyWhitelisted(prop) || | ||||
|                 !prop.get_visible()) | ||||
|                 continue; | ||||
|  | ||||
|             if (prop.get_key() == 'InputMode') { | ||||
|                 let text; | ||||
|                 if (prop.get_symbol) | ||||
|                     text = prop.get_symbol().get_text(); | ||||
|                 else | ||||
|                     text = prop.get_label().get_text(); | ||||
|  | ||||
|                 if (text && text.length > 0 && text.length < 3) | ||||
|                     this._updateIndicatorLabel(text); | ||||
|             } | ||||
|  | ||||
|             let item; | ||||
|             let type = prop.get_prop_type(); | ||||
|             if (type == IBus.PropType.MENU) { | ||||
|                 item = new PopupMenu.PopupSubMenuMenuItem(prop.get_label().get_text()); | ||||
|                 this._buildPropSubMenu(item.menu, prop.get_sub_props()); | ||||
|             } else if (type == IBus.PropType.RADIO) { | ||||
|                 item = new PopupMenu.PopupMenuItem(prop.get_label().get_text()); | ||||
|                 item.prop = prop; | ||||
|                 radioGroup.push(item); | ||||
|                 item.radioGroup = radioGroup; | ||||
|                 item.setShowDot(prop.get_state() == IBus.PropState.CHECKED); | ||||
|                 item.connect('activate', Lang.bind(this, function() { | ||||
|                     if (item.prop.get_state() == IBus.PropState.CHECKED) | ||||
|                         return; | ||||
|  | ||||
|                     let group = item.radioGroup; | ||||
|                     for (let i = 0; i < group.length; ++i) { | ||||
|                         if (group[i] == item) { | ||||
|                             item.setShowDot(true); | ||||
|                             item.prop.set_state(IBus.PropState.CHECKED); | ||||
|                             this._ibusManager.activateProperty(item.prop.get_key(), | ||||
|                                                                IBus.PropState.CHECKED); | ||||
|                         } else { | ||||
|                             group[i].setShowDot(false); | ||||
|                             group[i].prop.set_state(IBus.PropState.UNCHECKED); | ||||
|                             this._ibusManager.activateProperty(group[i].prop.get_key(), | ||||
|                                                                IBus.PropState.UNCHECKED); | ||||
|                         } | ||||
|                     } | ||||
|                 })); | ||||
|             } else { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             item.setSensitive(prop.get_sensitive()); | ||||
|             menu.addMenuItem(item); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _containerGetPreferredWidth: function(container, for_height, alloc) { | ||||
|         // Here, and in _containerGetPreferredHeight, we need to query | ||||
|         // for the height of all children, but we ignore the results | ||||
|   | ||||
| @@ -1,57 +0,0 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GObject = imports.gi.GObject; | ||||
| const Lang = imports.lang; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const PanelMenu = imports.ui.panelMenu; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| const VolumeMenu = imports.ui.status.volume; | ||||
|  | ||||
| const Indicator = new Lang.Class({ | ||||
|     Name: 'LockScreenMenuIndicator', | ||||
|     Extends: PanelMenu.SystemStatusButton, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent(null, _("Volume, network, battery")); | ||||
|  | ||||
|         this._volume = Main.panel.statusArea.volume; | ||||
|         if (this._volume) { | ||||
|             this._volumeIcon = this.addIcon(null); | ||||
|             this._volume.mainIcon.bind_property('gicon', this._volumeIcon, 'gicon', | ||||
|                                                 GObject.BindingFlags.SYNC_CREATE); | ||||
|             this._volume.mainIcon.bind_property('visible', this._volumeIcon, 'visible', | ||||
|                                                 GObject.BindingFlags.SYNC_CREATE); | ||||
|  | ||||
|             this._volumeControl = VolumeMenu.getMixerControl(); | ||||
|             this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl); | ||||
|             this.menu.addMenuItem(this._volumeMenu); | ||||
|         } | ||||
|  | ||||
|         this._network = Main.panel.statusArea.network; | ||||
|         if (this._network) { | ||||
|             this._networkIcon = this.addIcon(null); | ||||
|             this._network.mainIcon.bind_property('gicon', this._networkIcon, 'gicon', | ||||
|                                                  GObject.BindingFlags.SYNC_CREATE); | ||||
|             this._network.mainIcon.bind_property('visible', this._networkIcon, 'visible', | ||||
|                                                  GObject.BindingFlags.SYNC_CREATE); | ||||
|  | ||||
|             this._networkSecondaryIcon = this.addIcon(null); | ||||
|             this._network.secondaryIcon.bind_property('gicon', this._networkSecondaryIcon, 'gicon', | ||||
|                                                       GObject.BindingFlags.SYNC_CREATE); | ||||
|             this._network.secondaryIcon.bind_property('visible', this._networkSecondaryIcon, 'visible', | ||||
|                                                       GObject.BindingFlags.SYNC_CREATE); | ||||
|         } | ||||
|  | ||||
|         this._battery = Main.panel.statusArea.battery; | ||||
|         if (this._battery) { | ||||
|             this._batteryIcon = this.addIcon(null); | ||||
|             this._battery.mainIcon.bind_property('gicon', this._batteryIcon, 'gicon', | ||||
|                                                  GObject.BindingFlags.SYNC_CREATE); | ||||
|             this._battery.mainIcon.bind_property('visible', this._batteryIcon, 'visible', | ||||
|                                                  GObject.BindingFlags.SYNC_CREATE); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -52,7 +52,7 @@ const Indicator = new Lang.Class({ | ||||
|     Extends: PanelMenu.SystemStatusButton, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent('battery-missing-symbolic', _("Battery")); | ||||
|         this.parent('battery-missing', _("Battery")); | ||||
|  | ||||
|         this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH); | ||||
|  | ||||
| @@ -76,6 +76,12 @@ const Indicator = new Lang.Class({ | ||||
|         this._devicesChanged(); | ||||
|     }, | ||||
|  | ||||
|     setLockedState: function(locked) { | ||||
|         if (locked) | ||||
|             this.menu.close(); | ||||
|         this.actor.reactive = !locked; | ||||
|     }, | ||||
|  | ||||
|     _readPrimaryDevice: function() { | ||||
|         this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) { | ||||
|             if (error) { | ||||
| @@ -96,7 +102,7 @@ const Indicator = new Lang.Class({ | ||||
|                     let minutes = time % 60; | ||||
|                     let hours = Math.floor(time / 60); | ||||
|                     let timestring; | ||||
|                     if (time >= 60) { | ||||
|                     if (time > 60) { | ||||
|                         if (minutes == 0) { | ||||
|                             timestring = ngettext("%d hour remaining", "%d hours remaining", hours).format(hours); | ||||
|                         } else { | ||||
| @@ -144,21 +150,16 @@ const Indicator = new Lang.Class({ | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     _syncIcon: function() { | ||||
|     _devicesChanged: function() { | ||||
|         let icon = this._proxy.Icon; | ||||
|         let hasIcon = false; | ||||
|  | ||||
|         if (icon) { | ||||
|             let gicon = Gio.icon_new_for_string(icon); | ||||
|             this.setGIcon(gicon); | ||||
|             hasIcon = true; | ||||
|             this.actor.show(); | ||||
|         } else { | ||||
|             this.menu.close(); | ||||
|             this.actor.hide(); | ||||
|         } | ||||
|         this.mainIcon.visible = hasIcon; | ||||
|         this.actor.visible = hasIcon; | ||||
|     }, | ||||
|  | ||||
|     _devicesChanged: function() { | ||||
|         this._syncIcon(); | ||||
|         this._readPrimaryDevice(); | ||||
|         this._readOtherDevices(); | ||||
|     } | ||||
| @@ -177,6 +178,7 @@ const DeviceItem = new Lang.Class({ | ||||
|         this._label = new St.Label({ text: this._deviceTypeToString(device_type) }); | ||||
|  | ||||
|         this._icon = new St.Icon({ gicon: Gio.icon_new_for_string(icon), | ||||
|                                    icon_type: St.IconType.SYMBOLIC, | ||||
|                                    style_class: 'popup-menu-icon' }); | ||||
|  | ||||
|         this._box.add_actor(this._icon); | ||||
| @@ -185,8 +187,6 @@ const DeviceItem = new Lang.Class({ | ||||
|  | ||||
|         let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)) }); | ||||
|         this.addActor(percentLabel, { align: St.Align.END }); | ||||
|         //FIXME: ideally we would like to expose this._label and percentLabel | ||||
|         this.actor.label_actor = percentLabel; | ||||
|     }, | ||||
|  | ||||
|     _deviceTypeToString: function(type) { | ||||
|   | ||||
| @@ -12,27 +12,14 @@ const VOLUME_ADJUSTMENT_STEP = 0.05; /* Volume adjustment step in % */ | ||||
|  | ||||
| const VOLUME_NOTIFY_ID = 1; | ||||
|  | ||||
| // Each Gvc.MixerControl is a connection to PulseAudio, | ||||
| // so it's better to make it a singleton | ||||
| let _mixerControl; | ||||
| function getMixerControl() { | ||||
|     if (_mixerControl) | ||||
|         return _mixerControl; | ||||
| const Indicator = new Lang.Class({ | ||||
|     Name: 'VolumeIndicator', | ||||
|     Extends: PanelMenu.SystemStatusButton, | ||||
|  | ||||
|     _mixerControl = new Gvc.MixerControl({ name: 'GNOME Shell Volume Control' }); | ||||
|     _mixerControl.open(); | ||||
|     _init: function() { | ||||
|         this.parent('audio-volume-muted', _("Volume")); | ||||
|  | ||||
|     return _mixerControl; | ||||
| } | ||||
|  | ||||
| const VolumeMenu = new Lang.Class({ | ||||
|     Name: 'VolumeMenu', | ||||
|     Extends: PopupMenu.PopupMenuSection, | ||||
|  | ||||
|     _init: function(control) { | ||||
|         this.parent(); | ||||
|  | ||||
|         this._control = control; | ||||
|         this._control = new Gvc.MixerControl({ name: 'GNOME Shell Volume Control' }); | ||||
|         this._control.connect('state-changed', Lang.bind(this, this._onControlStateChanged)); | ||||
|         this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput)); | ||||
|         this._control.connect('default-source-changed', Lang.bind(this, this._readInput)); | ||||
| @@ -48,10 +35,10 @@ const VolumeMenu = new Lang.Class({ | ||||
|         this._outputSlider = new PopupMenu.PopupSliderMenuItem(0); | ||||
|         this._outputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_output')); | ||||
|         this._outputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange)); | ||||
|         this.addMenuItem(this._outputTitle); | ||||
|         this.addMenuItem(this._outputSlider); | ||||
|         this.menu.addMenuItem(this._outputTitle); | ||||
|         this.menu.addMenuItem(this._outputSlider); | ||||
|  | ||||
|         this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|         this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|  | ||||
|         this._input = null; | ||||
|         this._inputVolumeId = 0; | ||||
| @@ -60,27 +47,22 @@ const VolumeMenu = new Lang.Class({ | ||||
|         this._inputSlider = new PopupMenu.PopupSliderMenuItem(0); | ||||
|         this._inputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_input')); | ||||
|         this._inputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange)); | ||||
|         this.addMenuItem(this._inputTitle); | ||||
|         this.addMenuItem(this._inputSlider); | ||||
|         this.menu.addMenuItem(this._inputTitle); | ||||
|         this.menu.addMenuItem(this._inputSlider); | ||||
|  | ||||
|         this._onControlStateChanged(); | ||||
|         this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|         this.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop'); | ||||
|  | ||||
|         this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); | ||||
|         this._control.open(); | ||||
|     }, | ||||
|  | ||||
|     toggleMute: function(quiet) { | ||||
|         let muted = this._output.is_muted; | ||||
|         this._output.change_is_muted(!muted); | ||||
|  | ||||
|         if (muted && !quiet) | ||||
|             this._notifyVolumeChange(); | ||||
|  | ||||
|         if (!muted) | ||||
|             return ['audio-volume-muted-symbolic', 0]; | ||||
|         else | ||||
|             return [this._volumeToIcon(this._output.volume), | ||||
|                     this._output.volume / this._volumeMax]; | ||||
|     setLockedState: function(locked) { | ||||
|         this.menu.setSettingsVisibility(!locked); | ||||
|     }, | ||||
|  | ||||
|     scroll: function(direction, quiet) { | ||||
|     _onScrollEvent: function(actor, event) { | ||||
|         let direction = event.get_scroll_direction(); | ||||
|         let currentVolume = this._output.volume; | ||||
|  | ||||
|         if (direction == Clutter.ScrollDirection.DOWN) { | ||||
| @@ -99,23 +81,16 @@ const VolumeMenu = new Lang.Class({ | ||||
|             this._output.push_volume(); | ||||
|         } | ||||
|  | ||||
|         if (!quiet) | ||||
|             this._notifyVolumeChange(); | ||||
|  | ||||
|         if (this._output.is_muted) | ||||
|             return ['audio-volume-muted-symbolic', 0]; | ||||
|         else | ||||
|             return [this._volumeToIcon(this._output.volume), | ||||
|                     this._output.volume / this._volumeMax]; | ||||
|         this._notifyVolumeChange(); | ||||
|     }, | ||||
|  | ||||
|     _onControlStateChanged: function() { | ||||
|         if (this._control.get_state() == Gvc.MixerControlState.READY) { | ||||
|             this._readOutput(); | ||||
|             this._readInput(); | ||||
|             this._maybeShowInput(); | ||||
|             this.actor.show(); | ||||
|         } else { | ||||
|             this.emit('icon-changed', null); | ||||
|             this.actor.hide(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -134,7 +109,7 @@ const VolumeMenu = new Lang.Class({ | ||||
|             this._volumeChanged (null, null, '_output'); | ||||
|         } else { | ||||
|             this._outputSlider.setValue(0); | ||||
|             this.emit('icon-changed', 'audio-volume-muted-symbolic'); | ||||
|             this.setIcon('audio-volume-muted-symbolic'); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -180,14 +155,14 @@ const VolumeMenu = new Lang.Class({ | ||||
|  | ||||
|     _volumeToIcon: function(volume) { | ||||
|         if (volume <= 0) { | ||||
|             return 'audio-volume-muted-symbolic'; | ||||
|             return 'audio-volume-muted'; | ||||
|         } else { | ||||
|             let n = Math.floor(3 * volume / this._volumeMax) + 1; | ||||
|             if (n < 2) | ||||
|                 return 'audio-volume-low-symbolic'; | ||||
|                 return 'audio-volume-low'; | ||||
|             if (n >= 3) | ||||
|                 return 'audio-volume-high-symbolic'; | ||||
|             return 'audio-volume-medium-symbolic'; | ||||
|                 return 'audio-volume-high'; | ||||
|             return 'audio-volume-medium'; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -221,48 +196,15 @@ const VolumeMenu = new Lang.Class({ | ||||
|         slider.setValue(muted ? 0 : (this[property].volume / this._volumeMax)); | ||||
|         if (property == '_output') { | ||||
|             if (muted) | ||||
|                 this.emit('icon-changed', 'audio-volume-muted-symbolic'); | ||||
|                 this.setIcon('audio-volume-muted'); | ||||
|             else | ||||
|                 this.emit('icon-changed', this._volumeToIcon(this._output.volume)); | ||||
|                 this.setIcon(this._volumeToIcon(this._output.volume)); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _volumeChanged: function(object, param_spec, property) { | ||||
|         this[property+'Slider'].setValue(this[property].volume / this._volumeMax); | ||||
|         if (property == '_output' && !this._output.is_muted) | ||||
|             this.emit('icon-changed', this._volumeToIcon(this._output.volume)); | ||||
|             this.setIcon(this._volumeToIcon(this._output.volume)); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const Indicator = new Lang.Class({ | ||||
|     Name: 'VolumeIndicator', | ||||
|     Extends: PanelMenu.SystemStatusButton, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent('audio-volume-muted-symbolic', _("Volume")); | ||||
|  | ||||
|         this._control = getMixerControl(); | ||||
|         this.volumeMenu = new VolumeMenu(this._control); | ||||
|         this.volumeMenu.connect('icon-changed', Lang.bind(this, function(menu, icon) { | ||||
|             this._hasPulseAudio = (icon != null); | ||||
|             this.setIcon(icon); | ||||
|             this._syncVisibility(); | ||||
|         })); | ||||
|  | ||||
|         this.menu.addMenuItem(this.volumeMenu); | ||||
|  | ||||
|         this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|         this.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop'); | ||||
|  | ||||
|         this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); | ||||
|     }, | ||||
|  | ||||
|     _syncVisibility: function() { | ||||
|         this.actor.visible = this._hasPulseAudio; | ||||
|         this.mainIcon.visible = this._hasPulseAudio; | ||||
|     }, | ||||
|  | ||||
|     _onScrollEvent: function(actor, event) { | ||||
|         this.volumeMenu.scroll(event.get_scroll_direction(), false); | ||||
|     }, | ||||
| }); | ||||
|   | ||||
| @@ -11,10 +11,10 @@ const Tpl = imports.gi.TelepathyLogger; | ||||
| const Tp = imports.gi.TelepathyGLib; | ||||
| 
 | ||||
| const History = imports.misc.history; | ||||
| const Params = imports.misc.params; | ||||
| const Main = imports.ui.main; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const Params = imports.misc.params; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| 
 | ||||
| 
 | ||||
| // See Notification.appendMessage
 | ||||
| const SCROLLBACK_IMMEDIATE_TIME = 60; // 1 minute
 | ||||
| @@ -63,10 +63,10 @@ function makeMessageFromTplEvent(event) { | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| const TelepathyClient = new Lang.Class({ | ||||
|     Name: 'TelepathyClient', | ||||
| const Client = new Lang.Class({ | ||||
|     Name: 'Client', | ||||
| 
 | ||||
|     _init: function() { | ||||
|     _init : function() { | ||||
|         // channel path -> ChatSource
 | ||||
|         this._chatSources = {}; | ||||
|         this._chatState = Tp.ChannelChatState.ACTIVE; | ||||
| @@ -89,9 +89,9 @@ const TelepathyClient = new Lang.Class({ | ||||
|         // channel matching its filters is detected.
 | ||||
|         // The second argument, recover, means _observeChannels will be run
 | ||||
|         // for any existing channel as well.
 | ||||
|         this._tpClient = new Shell.TpClient({ name: 'GnomeShell', | ||||
|                                               account_manager: this._accountManager, | ||||
|                                               uniquify_name: true }); | ||||
|         this._tpClient = new Shell.TpClient({ 'account-manager': this._accountManager, | ||||
|                                               'name': 'GnomeShell', | ||||
|                                               'uniquify-name': true }) | ||||
|         this._tpClient.set_observe_channels_func( | ||||
|             Lang.bind(this, this._observeChannels)); | ||||
|         this._tpClient.set_approve_channels_func( | ||||
| @@ -99,10 +99,6 @@ const TelepathyClient = new Lang.Class({ | ||||
|         this._tpClient.set_handle_channels_func( | ||||
|             Lang.bind(this, this._handleChannels)); | ||||
| 
 | ||||
|         // Watch subscription requests and connection errors
 | ||||
|         this._subscriptionSource = null; | ||||
|         this._accountSource = null; | ||||
| 
 | ||||
|         // Workaround for gjs not supporting GPtrArray in signals.
 | ||||
|         // See BGO bug #653941 for context.
 | ||||
|         this._tpClient.set_contact_list_changed_func( | ||||
| @@ -112,26 +108,21 @@ const TelepathyClient = new Lang.Class({ | ||||
|         // needed
 | ||||
|         this._tpClient.set_delegated_channels_callback( | ||||
|             Lang.bind(this, this._delegatedChannelsCb)); | ||||
|     }, | ||||
| 
 | ||||
|     enable: function() { | ||||
|         try { | ||||
|             this._tpClient.register(); | ||||
|         } catch (e) { | ||||
|             throw new Error('Couldn\'t register Telepathy client. Error: \n' + e); | ||||
|         } | ||||
| 
 | ||||
|         this._accountManagerValidityChangedId = this._accountManager.connect('account-validity-changed', | ||||
|                                                                              Lang.bind(this, this._accountValidityChanged)); | ||||
|         // Watch subscription requests and connection errors
 | ||||
|         this._subscriptionSource = null; | ||||
|         this._accountSource = null; | ||||
| 
 | ||||
|         if (!this._accountManager.is_prepared(Tp.AccountManager.get_feature_quark_core())) | ||||
|             this._accountManager.prepare_async(null, Lang.bind(this, this._accountManagerPrepared)); | ||||
|     }, | ||||
|         this._accountManager.connect('account-validity-changed', | ||||
|             Lang.bind(this, this._accountValidityChanged)); | ||||
| 
 | ||||
|     disable: function() { | ||||
|         this._tpClient.unregister(); | ||||
|         this._accountManager.disconnect(this._accountManagerValidityChangedId); | ||||
|         this._accountManagerValidityChangedId = 0; | ||||
|         this._accountManager.prepare_async(null, Lang.bind(this, this._accountManagerPrepared)); | ||||
|     }, | ||||
| 
 | ||||
|     _observeChannels: function(observer, account, conn, channels, | ||||
| @@ -141,7 +132,7 @@ const TelepathyClient = new Lang.Class({ | ||||
|             let channel = channels[i]; | ||||
|             let [targetHandle, targetHandleType] = channel.get_handle(); | ||||
| 
 | ||||
|             if (channel.get_invalidated()) | ||||
|             if (Shell.is_channel_invalidated(channel)) | ||||
|               continue; | ||||
| 
 | ||||
|             /* Only observe contact text channels */ | ||||
| @@ -193,7 +184,7 @@ const TelepathyClient = new Lang.Class({ | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             if (channel.get_invalidated()) | ||||
|             if (Shell.is_channel_invalidated(channel)) | ||||
|               continue; | ||||
| 
 | ||||
|             // 'notify' will be true when coming from an actual HandleChannels
 | ||||
| @@ -220,15 +211,13 @@ const TelepathyClient = new Lang.Class({ | ||||
|         // We can only approve the rooms if we have been invited to it
 | ||||
|         let selfContact = channel.group_get_self_contact(); | ||||
|         if (selfContact == null) { | ||||
|             context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT, | ||||
|                                         message: 'Not invited to the room' })); | ||||
|             Shell.decline_dispatch_op(context, 'Not invited to the room'); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         let [invited, inviter, reason, msg] = channel.group_get_local_pending_contact_info(selfContact); | ||||
|         if (!invited) { | ||||
|             context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT, | ||||
|                                         message: 'Not invited to the room' })); | ||||
|             Shell.decline_dispatch_op(context, 'Not invited to the room'); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
| @@ -248,9 +237,8 @@ const TelepathyClient = new Lang.Class({ | ||||
|         let channel = channels[0]; | ||||
|         let chanType = channel.get_channel_type(); | ||||
| 
 | ||||
|         if (channel.get_invalidated()) { | ||||
|             context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT, | ||||
|                                         message: 'Channel is invalidated' })); | ||||
|         if (Shell.is_channel_invalidated(channel)) { | ||||
|             Shell.decline_dispatch_op(context, 'Channel is invalidated'); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
| @@ -261,8 +249,7 @@ const TelepathyClient = new Lang.Class({ | ||||
|         else if (chanType == Tp.IFACE_CHANNEL_TYPE_FILE_TRANSFER) | ||||
|             this._approveFileTransfer(account, conn, channel, dispatchOp, context); | ||||
|         else | ||||
|             context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT, | ||||
|                                         message: 'Unsupported channel type' })); | ||||
|             Shell.decline_dispatch_op(context, 'Unsupported channel type'); | ||||
|     }, | ||||
| 
 | ||||
|     _approveTextChannel: function(account, conn, channel, dispatchOp, context) { | ||||
| @@ -392,7 +379,8 @@ const TelepathyClient = new Lang.Class({ | ||||
|     _ensureSubscriptionSource: function() { | ||||
|         if (this._subscriptionSource == null) { | ||||
|             this._subscriptionSource = new MessageTray.Source(_("Subscription request"), | ||||
|                                                               'gtk-dialog-question'); | ||||
|                                                               'gtk-dialog-question', | ||||
|                                                               St.IconType.FULLCOLOR); | ||||
|             Main.messageTray.add(this._subscriptionSource); | ||||
|             this._subscriptionSource.connect('destroy', Lang.bind(this, function () { | ||||
|                 this._subscriptionSource = null; | ||||
| @@ -428,7 +416,8 @@ const TelepathyClient = new Lang.Class({ | ||||
|     _ensureAccountSource: function() { | ||||
|         if (this._accountSource == null) { | ||||
|             this._accountSource = new MessageTray.Source(_("Connection error"), | ||||
|                                                          'gtk-dialog-error'); | ||||
|                                                          'gtk-dialog-error', | ||||
|                                                          St.IconType.FULLCOLOR); | ||||
|             Main.messageTray.add(this._accountSource); | ||||
|             this._accountSource.connect('destroy', Lang.bind(this, function () { | ||||
|                 this._accountSource = null; | ||||
| @@ -482,22 +471,6 @@ const ChatSource = new Lang.Class({ | ||||
|         this._getLogMessages(); | ||||
|     }, | ||||
| 
 | ||||
|     buildRightClickMenu: function() { | ||||
|         let item; | ||||
| 
 | ||||
|         let rightClickMenu = this.parent(); | ||||
|         item = new PopupMenu.PopupMenuItem(''); | ||||
|         item.actor.connect('notify::mapped', Lang.bind(this, function() { | ||||
|             item.label.set_text(this.isMuted ? _("Unmute") : _("Mute")); | ||||
|         })); | ||||
|         item.connect('activate', Lang.bind(this, function() { | ||||
|             this.setMuted(!this.isMuted); | ||||
|             this.emit('done-displaying-content'); | ||||
|         })); | ||||
|         rightClickMenu.add(item.actor); | ||||
|         return rightClickMenu; | ||||
|     }, | ||||
| 
 | ||||
|     _updateAlias: function() { | ||||
|         let oldAlias = this.title; | ||||
|         let newAlias = this._contact.get_alias(); | ||||
| @@ -509,42 +482,53 @@ const ChatSource = new Lang.Class({ | ||||
|         this._notification.appendAliasChange(oldAlias, newAlias); | ||||
|     }, | ||||
| 
 | ||||
|     getIcon: function() { | ||||
|     createIcon: function(size) { | ||||
|         this._iconBox = new St.Bin({ style_class: 'avatar-box' }); | ||||
|         this._iconBox._size = size; | ||||
|         let textureCache = St.TextureCache.get_default(); | ||||
|         let file = this._contact.get_avatar_file(); | ||||
| 
 | ||||
|         if (file) { | ||||
|             return new Gio.FileIcon({ file: file }); | ||||
|             let uri = file.get_uri(); | ||||
|             this._iconBox.child = textureCache.load_uri_async(uri, this._iconBox._size, this._iconBox._size); | ||||
|         } else { | ||||
|             return new Gio.ThemedIcon({ name: 'avatar-default' }); | ||||
|             this._iconBox.child = new St.Icon({ icon_name: 'avatar-default', | ||||
|                                                 icon_type: St.IconType.FULLCOLOR, | ||||
|                                                 icon_size: this._iconBox._size }); | ||||
|         } | ||||
| 
 | ||||
|         return this._iconBox; | ||||
|     }, | ||||
| 
 | ||||
|     getSecondaryIcon: function() { | ||||
|         let iconName; | ||||
|     createSecondaryIcon: function() { | ||||
|         let iconBox = new St.Bin(); | ||||
|         iconBox.child = new St.Icon({ style_class: 'secondary-icon', | ||||
|                                       icon_type: St.IconType.FULLCOLOR }); | ||||
|         let presenceType = this._contact.get_presence_type(); | ||||
| 
 | ||||
|         switch (presenceType) { | ||||
|             case Tp.ConnectionPresenceType.AVAILABLE: | ||||
|                 iconName = 'user-available'; | ||||
|                 iconBox.child.icon_name = 'user-available'; | ||||
|                 break; | ||||
|             case Tp.ConnectionPresenceType.BUSY: | ||||
|                 iconName = 'user-busy'; | ||||
|                 iconBox.child.icon_name = 'user-busy'; | ||||
|                 break; | ||||
|             case Tp.ConnectionPresenceType.OFFLINE: | ||||
|                 iconName = 'user-offline'; | ||||
|                 iconBox.child.icon_name = 'user-offline'; | ||||
|                 break; | ||||
|             case Tp.ConnectionPresenceType.HIDDEN: | ||||
|                 iconName = 'user-invisible'; | ||||
|                 iconBox.child.icon_name = 'user-invisible'; | ||||
|                 break; | ||||
|             case Tp.ConnectionPresenceType.AWAY: | ||||
|                 iconName = 'user-away'; | ||||
|                 iconBox.child.icon_name = 'user-away'; | ||||
|                 break; | ||||
|             case Tp.ConnectionPresenceType.EXTENDED_AWAY: | ||||
|                 iconName = 'user-idle'; | ||||
|                 iconBox.child.icon_name = 'user-idle'; | ||||
|                 break; | ||||
|             default: | ||||
|                 iconName = 'user-offline'; | ||||
|                 iconBox.child.icon_name = 'user-offline'; | ||||
|        } | ||||
|        return new Gio.ThemedIcon({ name: iconName }); | ||||
|        return iconBox; | ||||
|     }, | ||||
| 
 | ||||
|     _updateAvatarIcon: function() { | ||||
| @@ -555,9 +539,7 @@ const ChatSource = new Lang.Class({ | ||||
|     open: function(notification) { | ||||
|           if (this._client.is_handling_channel(this._channel)) { | ||||
|               // We are handling the channel, try to pass it to Empathy
 | ||||
|               this._client.delegate_channels_async([this._channel], | ||||
|                   global.get_current_time(), | ||||
|                   'org.freedesktop.Telepathy.Client.Empathy.Chat', null); | ||||
|               this._client.delegate_channels_async([this._channel], global.get_current_time(), '', null); | ||||
|           } | ||||
|           else { | ||||
|               // We are not the handler, just ask to present the channel
 | ||||
| @@ -596,7 +578,7 @@ const ChatSource = new Lang.Class({ | ||||
|             this._pendingMessages.push(message); | ||||
|         } | ||||
| 
 | ||||
|         this.countUpdated(); | ||||
|         this._updateCount(); | ||||
| 
 | ||||
|         let showTimestamp = false; | ||||
| 
 | ||||
| @@ -642,17 +624,8 @@ const ChatSource = new Lang.Class({ | ||||
|         this.destroy(); | ||||
|     }, | ||||
| 
 | ||||
|     /* All messages are new messages for Telepathy sources */ | ||||
|     get count() { | ||||
|         return this._pendingMessages.length; | ||||
|     }, | ||||
| 
 | ||||
|     get unseenCount() { | ||||
|         return this.count; | ||||
|     }, | ||||
| 
 | ||||
|     get countVisible() { | ||||
|         return this.count > 0; | ||||
|     _updateCount: function() { | ||||
|         this._setCount(this._pendingMessages.length, this._pendingMessages.length > 0); | ||||
|     }, | ||||
| 
 | ||||
|     _messageReceived: function(channel, message) { | ||||
| @@ -660,7 +633,7 @@ const ChatSource = new Lang.Class({ | ||||
|             return; | ||||
| 
 | ||||
|         this._pendingMessages.push(message); | ||||
|         this.countUpdated(); | ||||
|         this._updateCount(); | ||||
| 
 | ||||
|         message = makeMessageFromTpMessage(message, NotificationDirection.RECEIVED); | ||||
|         this._notification.appendMessage(message); | ||||
| @@ -726,7 +699,7 @@ const ChatSource = new Lang.Class({ | ||||
| 
 | ||||
|         title = GLib.markup_escape_text(this.title, -1); | ||||
| 
 | ||||
|         this._notification.update(this._notification.title, null, { customContent: true, secondaryGIcon: this.getSecondaryIcon() }); | ||||
|         this._notification.update(this._notification.title, null, { customContent: true, secondaryIcon: this.createSecondaryIcon() }); | ||||
| 
 | ||||
|         if (message) | ||||
|             msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>'; | ||||
| @@ -737,8 +710,10 @@ const ChatSource = new Lang.Class({ | ||||
| 
 | ||||
|         if (idx >= 0) { | ||||
|             this._pendingMessages.splice(idx, 1); | ||||
|             this.countUpdated(); | ||||
|             this._updateCount(); | ||||
|         } | ||||
|         else | ||||
|             throw new Error('Message not in our pending list: ' + message); | ||||
|     }, | ||||
| 
 | ||||
|     _ackMessages: function() { | ||||
| @@ -754,7 +729,7 @@ const ChatNotification = new Lang.Class({ | ||||
|     Extends: MessageTray.Notification, | ||||
| 
 | ||||
|     _init: function(source) { | ||||
|         this.parent(source, source.title, null, { customContent: true, secondaryGIcon: source.getSecondaryIcon() }); | ||||
|         this.parent(source, source.title, null, { customContent: true, secondaryIcon: source.createSecondaryIcon() }); | ||||
|         this.setResident(true); | ||||
| 
 | ||||
|         this._responseEntry = new St.Entry({ style_class: 'chat-response', | ||||
| @@ -763,27 +738,16 @@ const ChatNotification = new Lang.Class({ | ||||
|         this._responseEntry.clutter_text.connect('text-changed', Lang.bind(this, this._onEntryChanged)); | ||||
|         this.setActionArea(this._responseEntry); | ||||
| 
 | ||||
|         this._responseEntry.clutter_text.connect('key-focus-in', Lang.bind(this, function() { | ||||
|             this.focused = true; | ||||
|         })); | ||||
|         this._responseEntry.clutter_text.connect('key-focus-out', Lang.bind(this, function() { | ||||
|             this.focused = false; | ||||
|             this.emit('unfocused'); | ||||
|         })); | ||||
| 
 | ||||
|         this._oldMaxScrollAdjustment = 0; | ||||
|         this._createScrollArea(); | ||||
|         this._lastGroup = null; | ||||
|         this._lastGroupActor = null; | ||||
| 
 | ||||
|         // Keep track of the bottom position for the current adjustment and
 | ||||
|         // force a scroll to the bottom if things change while we were at the
 | ||||
|         // bottom
 | ||||
|         this._oldMaxScrollValue = this._scrollArea.vscroll.adjustment.value; | ||||
|         this._scrollArea.add_style_class_name('chat-notification-scrollview'); | ||||
|         this._scrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) { | ||||
|             if (adjustment.value == this._oldMaxScrollValue) | ||||
|             let currentValue = adjustment.value + adjustment.page_size; | ||||
|             if (currentValue == this._oldMaxScrollAdjustment) | ||||
|                 this.scrollTo(St.Side.BOTTOM); | ||||
|             this._oldMaxScrollValue = Math.max(adjustment.lower, adjustment.upper - adjustment.page_size); | ||||
|             this._oldMaxScrollAdjustment = adjustment.upper; | ||||
|         })); | ||||
| 
 | ||||
|         this._inputHistory = new History.HistoryManager({ entry: this._responseEntry.clutter_text }); | ||||
| @@ -935,32 +899,24 @@ const ChatNotification = new Lang.Class({ | ||||
| 
 | ||||
|         let format; | ||||
| 
 | ||||
|         // Show only the hour if date is on today
 | ||||
|         if(daysAgo < 1){ | ||||
|             format = "<b>%H:%M</b>"; | ||||
|         } | ||||
|         // Show the word "Yesterday" and time if date is on yesterday
 | ||||
|         else if(daysAgo <2){ | ||||
|             /* Translators: this is a time format string followed by the word "Yesterday". i.e. "14:30 on Yesterday"*/ | ||||
|             // xgettext:no-c-format
 | ||||
|             format = _("<b>%H:%M</b> on Yesterday"); | ||||
|         } | ||||
|         // Show a week day and time if date is in the last week
 | ||||
|         else if (daysAgo < 7) { | ||||
|             /* Translators: this is a time format string followed by a week day name. i.e. "14:30 on Monday*/ | ||||
|         if (daysAgo < 1 || (daysAgo < 7 && now.getDay() != date.getDay())) { | ||||
|             /* Translators: this is a time format string followed by a date. | ||||
|              If applicable, replace %X with a strftime format valid for your | ||||
|              locale, without seconds. */ | ||||
|             // xgettext:no-c-format
 | ||||
|             format = _("<b>%H:%M</b> on <b>%A</b>"); | ||||
|             format = _("Sent at <b>%X</b> on <b>%A</b>"); | ||||
| 
 | ||||
|         } else if (date.getYear() == now.getYear()) { | ||||
|             /* Translators: this is a time format in the style of "14:30 on Wednesday, May 25", | ||||
|              shown when you get a chat message in the same year */ | ||||
|             /* Translators: this is a time format in the style of "Wednesday, May 25", | ||||
|              shown when you get a chat message in the same year. */ | ||||
|             // xgettext:no-c-format
 | ||||
|             format = _("<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>"); | ||||
|             format = _("Sent on <b>%A</b>, <b>%B %d</b>"); | ||||
|         } else { | ||||
|             /* Translators: this is a time format in the style of "14:30 on Wednesday, May 25, 2012", | ||||
|              shown when you get a chat message in a different year */ | ||||
|             /* Translators: this is a time format in the style of "Wednesday, May 25, 2012", | ||||
|              shown when you get a chat message in a different year. */ | ||||
|             // xgettext:no-c-format
 | ||||
|             format = _("<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>, %Y"); | ||||
|             format = _("Sent on <b>%A</b>, <b>%B %d</b>, %Y"); | ||||
|         } | ||||
| 
 | ||||
|         return date.toLocaleFormat(format); | ||||
| @@ -973,7 +929,8 @@ const ChatNotification = new Lang.Class({ | ||||
|         let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate), | ||||
|                                        group: 'meta', | ||||
|                                        styles: ['chat-meta-message'], | ||||
|                                        childProps: { expand: true, x_fill: false }, | ||||
|                                        childProps: { expand: true, x_fill: false, | ||||
|                                                      x_align: St.Align.END }, | ||||
|                                        noTimestamp: true, | ||||
|                                        timestamp: lastMessageTime }); | ||||
| 
 | ||||
| @@ -1074,8 +1031,9 @@ const ApproverSource = new Lang.Class({ | ||||
|         this.parent(); | ||||
|     }, | ||||
| 
 | ||||
|     getIcon: function() { | ||||
|         return this._gicon; | ||||
|     createIcon: function(size) { | ||||
|         return new St.Icon({ gicon: this._gicon, | ||||
|                              icon_size: size }); | ||||
|     } | ||||
| }); | ||||
| 
 | ||||
| @@ -1136,7 +1094,7 @@ const AudioVideoNotification = new Lang.Class({ | ||||
|         this.parent(source, title, null, { customContent: true }); | ||||
|         this.setResident(true); | ||||
| 
 | ||||
|         this.addButton('reject', _("Decline")); | ||||
|         this.addButton('reject', _("Reject")); | ||||
|         /* translators: this is a button label (verb), not a noun */ | ||||
|         this.addButton('answer', _("Answer")); | ||||
| 
 | ||||
| @@ -1226,6 +1184,7 @@ const SubscriptionRequestNotification = new Lang.Class({ | ||||
|         } | ||||
|         else { | ||||
|             iconBox.child = new St.Icon({ icon_name: 'avatar-default', | ||||
|                                           icon_type: St.IconType.FULLCOLOR, | ||||
|                                           icon_size: iconBox._size }); | ||||
|         } | ||||
| 
 | ||||
| @@ -1370,14 +1329,15 @@ const AccountNotification = new Lang.Class({ | ||||
|             case 'reconnect': | ||||
|                 // If it fails again, a new notification should pop up with the
 | ||||
|                 // new error.
 | ||||
|                 account.reconnect_async(null); | ||||
|                 account.reconnect_async(null, null); | ||||
|                 break; | ||||
|             case 'edit': | ||||
|                 let cmd = '/usr/bin/empathy-accounts' | ||||
|                         + ' --select-account=%s' | ||||
|                         .format(account.get_path_suffix()); | ||||
|                 let app_info = Gio.app_info_create_from_commandline(cmd, null, 0); | ||||
|                 app_info.launch([], global.create_app_launch_context()); | ||||
|                 let app_info = Gio.app_info_create_from_commandline(cmd, null, 0, | ||||
|                     null); | ||||
|                 app_info.launch([], null, null); | ||||
|                 break; | ||||
|             } | ||||
|             this.destroy(); | ||||
| @@ -1432,4 +1392,3 @@ const AccountNotification = new Lang.Class({ | ||||
|         this.parent(); | ||||
|     } | ||||
| }); | ||||
| const Component = TelepathyClient; | ||||
| @@ -2,10 +2,9 @@ | ||||
|  | ||||
| const AccountsService = imports.gi.AccountsService; | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gdm  = imports.gi.Gdm; | ||||
| const Gdm = imports.gi.Gdm; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const GnomeDesktop = imports.gi.GnomeDesktop; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Lang = imports.lang; | ||||
| const Signals = imports.signals; | ||||
| @@ -21,38 +20,6 @@ const UserMenu = imports.ui.userMenu; | ||||
| const Batch = imports.gdm.batch; | ||||
| const GdmUtil = imports.gdm.util; | ||||
|  | ||||
| // The timeout before going back automatically to the lock screen (in seconds) | ||||
| const IDLE_TIMEOUT = 2 * 60; | ||||
|  | ||||
| function versionCompare(required, reference) { | ||||
|     required = required.split('.'); | ||||
|     reference = reference.split('.'); | ||||
|  | ||||
|     for (let i = 0; i < required.length; i++) { | ||||
|         if (required[i] != reference[i]) | ||||
|             return required[i] < reference[i]; | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| function isSupported() { | ||||
|     try { | ||||
|         let params = GLib.Variant.new('(ss)', ['org.gnome.DisplayManager.Manager', 'Version']); | ||||
|         let result = Gio.DBus.system.call_sync('org.gnome.DisplayManager', | ||||
|                                                '/org/gnome/DisplayManager/Manager', | ||||
|                                                'org.freedesktop.DBus.Properties', | ||||
|                                                'Get', params, null, | ||||
|                                                Gio.DBusCallFlags.NONE, | ||||
|                                                -1, null); | ||||
|  | ||||
|         let version = result.deep_unpack()[0].deep_unpack(); | ||||
|         return versionCompare('3.5.91', version); | ||||
|     } catch(e) { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| // A widget showing the user avatar and name | ||||
| const UserWidget = new Lang.Class({ | ||||
|     Name: 'UserWidget', | ||||
| @@ -71,7 +38,6 @@ const UserWidget = new Lang.Class({ | ||||
|         this.actor.add(this._label, | ||||
|                        { expand: true, | ||||
|                          x_fill: true, | ||||
|                          y_fill: false, | ||||
|                          y_align: St.Align.MIDDLE }); | ||||
|  | ||||
|         this._userLoadedId = this._user.connect('notify::is-loaded', | ||||
| @@ -120,26 +86,22 @@ const UnlockDialog = new Lang.Class({ | ||||
|         this._userName = GLib.get_user_name(); | ||||
|         this._user = this._userManager.get_user(this._userName); | ||||
|  | ||||
|         this._failCounter = 0; | ||||
|         this._firstQuestion = true; | ||||
|  | ||||
|         this._greeterClient = new Gdm.Client(); | ||||
|         this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient, { reauthenticationOnly: true }); | ||||
|  | ||||
|         this._userVerifier.connect('reset', Lang.bind(this, this._reset)); | ||||
|         this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion)); | ||||
|         this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage)); | ||||
|         this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete)); | ||||
|         this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed)); | ||||
|         this._userVerifier.connect('reset', Lang.bind(this, this._onReset)); | ||||
|  | ||||
|         this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint)); | ||||
|         this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint)); | ||||
|         this._userVerifier.connect('show-fingerprint-prompt', Lang.bind(this, this._showFingerprintPrompt)); | ||||
|         this._userVerifier.connect('hide-fingerprint-prompt', Lang.bind(this, this._hideFingerprintPrompt)); | ||||
|  | ||||
|         this._userWidget = new UserWidget(this._user); | ||||
|         this.contentLayout.add_actor(this._userWidget.actor); | ||||
|  | ||||
|         this._promptLayout = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout', | ||||
|                                                 vertical: true }); | ||||
|                                                 vertical: false }); | ||||
|  | ||||
|         this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' }); | ||||
|         this._promptLayout.add(this._promptLabel, | ||||
| @@ -147,13 +109,9 @@ const UnlockDialog = new Lang.Class({ | ||||
|  | ||||
|         this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry', | ||||
|                                            can_focus: true }); | ||||
|         this._promptEntry.clutter_text.set_password_char('\u25cf'); | ||||
|         ShellEntry.addContextMenu(this._promptEntry, { isPassword: true }); | ||||
|         ShellEntry.addContextMenu(this._promptEntry); | ||||
|         this.setInitialKeyFocus(this._promptEntry); | ||||
|         this._promptEntry.clutter_text.connect('activate', Lang.bind(this, this._doUnlock)); | ||||
|         this._promptEntry.clutter_text.connect('text-changed', Lang.bind(this, function() { | ||||
|             this._updateOkButtonSensitivity(this._promptEntry.text.length > 0); | ||||
|         })); | ||||
|  | ||||
|         this._promptLayout.add(this._promptEntry, | ||||
|                                { expand: true, | ||||
| @@ -161,24 +119,14 @@ const UnlockDialog = new Lang.Class({ | ||||
|  | ||||
|         this.contentLayout.add_actor(this._promptLayout); | ||||
|  | ||||
|         this._promptMessage = new St.Label({ visible: false }); | ||||
|         this.contentLayout.add(this._promptMessage, { x_fill: true }); | ||||
|         // Translators: this message is shown below the password entry field | ||||
|         // to indicate the user can swipe their finger instead | ||||
|         this._promptFingerprintMessage = new St.Label({ text: _("(or swipe finger)"), | ||||
|                                                         style_class: 'login-dialog-prompt-fingerprint-message' }); | ||||
|         this._promptFingerprintMessage.hide(); | ||||
|         this.contentLayout.add_actor(this._promptFingerprintMessage); | ||||
|  | ||||
|         this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint' }); | ||||
|         this._promptLoginHint.hide(); | ||||
|         this.contentLayout.add_actor(this._promptLoginHint); | ||||
|  | ||||
|         let cancelButton = { label: _("Cancel"), | ||||
|                              action: Lang.bind(this, this._escape), | ||||
|                              key: Clutter.KEY_Escape }; | ||||
|         this._okButton = { label: _("Unlock"), | ||||
|                            action: Lang.bind(this, this._doUnlock), | ||||
|                            default: true }; | ||||
|         this.setButtons([cancelButton, this._okButton]); | ||||
|  | ||||
|         this._updateSensitivity(true); | ||||
|  | ||||
|         let otherUserLabel = new St.Label({ text: _("Log in as another user"), | ||||
|         let otherUserLabel = new St.Label({ text: _("Login as another user"), | ||||
|                                             style_class: 'login-dialog-not-listed-label' }); | ||||
|         this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button', | ||||
|                                                 can_focus: true, | ||||
| @@ -187,91 +135,60 @@ const UnlockDialog = new Lang.Class({ | ||||
|                                                 x_align: St.Align.START, | ||||
|                                                 x_fill: true }); | ||||
|         this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked)); | ||||
|         this.dialogLayout.add(this._otherUserButton, | ||||
|                               { x_align: St.Align.START, | ||||
|                                 x_fill: false }); | ||||
|         this.contentLayout.add(this._otherUserButton, | ||||
|                                { x_align: St.Align.START, | ||||
|                                  x_fill: false }); | ||||
|  | ||||
|         let batch = new Batch.Hold(); | ||||
|         this._userVerifier.begin(this._userName, batch); | ||||
|         this._okButton = { label: _("Unlock"), | ||||
|                            action: Lang.bind(this, this._doUnlock), | ||||
|                            default: true }; | ||||
|         this.setButtons([this._okButton]); | ||||
|         this.setActionKey(Clutter.KEY_Escape, Lang.bind(this, this._escape)); | ||||
|  | ||||
|         this._updateOkButton(false); | ||||
|         this._reset(); | ||||
|  | ||||
|         GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() { | ||||
|             this.emit('loaded'); | ||||
|             return false; | ||||
|         })); | ||||
|  | ||||
|         this._idleMonitor = new GnomeDesktop.IdleMonitor(); | ||||
|         this._idleWatchId = this._idleMonitor.add_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape)); | ||||
|     }, | ||||
|  | ||||
|     _updateSensitivity: function(sensitive) { | ||||
|         this._promptEntry.reactive = sensitive; | ||||
|         this._promptEntry.clutter_text.editable = sensitive; | ||||
|         this._updateOkButtonSensitivity(sensitive && this._promptEntry.text.length > 0); | ||||
|     }, | ||||
|  | ||||
|     _updateOkButtonSensitivity: function(sensitive) { | ||||
|     _updateOkButton: function(sensitive) { | ||||
|         this._okButton.button.reactive = sensitive; | ||||
|         this._okButton.button.can_focus = sensitive; | ||||
|     }, | ||||
|  | ||||
|     _showMessage: function(userVerifier, message, styleClass) { | ||||
|         if (message) { | ||||
|             this._promptMessage.text = message; | ||||
|             this._promptMessage.styleClass = styleClass; | ||||
|             GdmUtil.fadeInActor(this._promptMessage); | ||||
|         } else { | ||||
|             GdmUtil.fadeOutActor(this._promptMessage); | ||||
|         } | ||||
|     _reset: function() { | ||||
|         this._userVerifier.begin(this._userName, new Batch.Hold()); | ||||
|     }, | ||||
|  | ||||
|     _onAskQuestion: function(verifier, serviceName, question, passwordChar) { | ||||
|         if (this._firstQuestion && this._firstQuestionAnswer) { | ||||
|             this._userVerifier.answerQuery(serviceName, this._firstQuestionAnswer); | ||||
|             this._firstQuestionAnswer = null; | ||||
|             this._firstQuestion = false; | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._promptLabel.text = question; | ||||
|  | ||||
|         if (!this._firstQuestion) | ||||
|             this._promptEntry.text = ''; | ||||
|         else | ||||
|             this._firstQuestion = false; | ||||
|  | ||||
|         this._promptEntry.text = ''; | ||||
|         this._promptEntry.clutter_text.set_password_char(passwordChar); | ||||
|         this._promptEntry.menu.isPassword = passwordChar != ''; | ||||
|  | ||||
|         this._currentQuery = serviceName; | ||||
|         this._updateSensitivity(true); | ||||
|         this._updateOkButton(true); | ||||
|     }, | ||||
|  | ||||
|     _showLoginHint: function(verifier, message) { | ||||
|         this._promptLoginHint.set_text(message) | ||||
|         GdmUtil.fadeInActor(this._promptLoginHint); | ||||
|     _showFingerprintPrompt: function() { | ||||
|         GdmUtil.fadeInActor(this._promptFingerprintMessage); | ||||
|     }, | ||||
|  | ||||
|     _hideLoginHint: function() { | ||||
|         GdmUtil.fadeOutActor(this._promptLoginHint); | ||||
|     _hideFingerprintPrompt: function() { | ||||
|         GdmUtil.fadeOutActor(this._promptFingerprintMessage); | ||||
|     }, | ||||
|  | ||||
|     _doUnlock: function() { | ||||
|         if (this._firstQuestion) { | ||||
|             // we haven't received a query yet, so stash the answer | ||||
|             // and make ourself non-reactive | ||||
|             // the actual reply to GDM will be sent as soon as asked | ||||
|             this._firstQuestionAnswer = this._promptEntry.text; | ||||
|             this._updateSensitivity(false); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (!this._currentQuery) | ||||
|             return; | ||||
|  | ||||
|         let query = this._currentQuery; | ||||
|         this._currentQuery = null; | ||||
|  | ||||
|         this._updateSensitivity(false); | ||||
|         this._updateOkButton(false); | ||||
|  | ||||
|         this._userVerifier.answerQuery(query, this._promptEntry.text); | ||||
|     }, | ||||
| @@ -281,28 +198,17 @@ const UnlockDialog = new Lang.Class({ | ||||
|         this.emit('unlocked'); | ||||
|     }, | ||||
|  | ||||
|     _onReset: function() { | ||||
|         this.emit('failed'); | ||||
|     }, | ||||
|  | ||||
|     _onVerificationFailed: function() { | ||||
|         this._currentQuery = null; | ||||
|         this._firstQuestion = true; | ||||
|  | ||||
|         this._promptEntry.text = ''; | ||||
|         this._promptEntry.clutter_text.set_password_char('\u25cf'); | ||||
|         this._promptEntry.menu.isPassword = true; | ||||
|  | ||||
|         this._updateSensitivity(false); | ||||
|     }, | ||||
|  | ||||
|     _escape: function() { | ||||
|         this._userVerifier.cancel(); | ||||
|         this.emit('failed'); | ||||
|     }, | ||||
|  | ||||
|     _escape: function() { | ||||
|         this._onVerificationFailed(); | ||||
|     }, | ||||
|  | ||||
|     _otherUserClicked: function(button, event) { | ||||
|         Gdm.goto_login_session_sync(null); | ||||
|         this._userManager.goto_login_session(); | ||||
|  | ||||
|         this._userVerifier.cancel(); | ||||
|         this.emit('failed'); | ||||
| @@ -310,12 +216,6 @@ const UnlockDialog = new Lang.Class({ | ||||
|  | ||||
|     destroy: function() { | ||||
|         this._userVerifier.clear(); | ||||
|  | ||||
|         if (this._idleWatchId) { | ||||
|             this._idleMonitor.remove_watch(this._idleWatchId); | ||||
|             this._idleWatchId = 0; | ||||
|         } | ||||
|  | ||||
|         this.parent(); | ||||
|     }, | ||||
|  | ||||
|   | ||||
| @@ -9,24 +9,19 @@ const Pango = imports.gi.Pango; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
| const Tp = imports.gi.TelepathyGLib; | ||||
| const UPowerGlib = imports.gi.UPowerGlib; | ||||
| const Atk = imports.gi.Atk; | ||||
|  | ||||
| const BoxPointer = imports.ui.boxpointer; | ||||
| const GnomeSession = imports.misc.gnomeSession; | ||||
| const LoginManager = imports.misc.loginManager; | ||||
| const Main = imports.ui.main; | ||||
| const PanelMenu = imports.ui.panelMenu; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| const Params = imports.misc.params; | ||||
| const Util = imports.misc.util; | ||||
|  | ||||
| const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown'; | ||||
| const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver'; | ||||
| const DISABLE_USER_SWITCH_KEY = 'disable-user-switching'; | ||||
| const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen'; | ||||
| const DISABLE_LOG_OUT_KEY = 'disable-log-out'; | ||||
| const LOCK_ENABLED_KEY = 'lock-enabled'; | ||||
| const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out'; | ||||
|  | ||||
| const DIALOG_ICON_SIZE = 64; | ||||
|  | ||||
| @@ -48,21 +43,12 @@ const IMStatus = { | ||||
| const UserAvatarWidget = new Lang.Class({ | ||||
|     Name: 'UserAvatarWidget', | ||||
|  | ||||
|     _init: function(user, params) { | ||||
|     _init: function(user) { | ||||
|         this._user = user; | ||||
|         params = Params.parse(params, { reactive: false, | ||||
|                                         iconSize: DIALOG_ICON_SIZE, | ||||
|                                         styleClass: 'status-chooser-user-icon' }); | ||||
|         this._iconSize = params.iconSize; | ||||
|  | ||||
|         this.actor = new St.Bin({ style_class: params.styleClass, | ||||
|                                   track_hover: params.reactive, | ||||
|                                   reactive: params.reactive }); | ||||
|     }, | ||||
|  | ||||
|     setSensitive: function(sensitive) { | ||||
|         this.actor.can_focus = sensitive; | ||||
|         this.actor.reactive = sensitive; | ||||
|         this.actor = new St.Bin({ style_class: 'status-chooser-user-icon', | ||||
|                                   track_hover: true, | ||||
|                                   reactive: true }); | ||||
|     }, | ||||
|  | ||||
|     update: function() { | ||||
| @@ -76,8 +62,9 @@ const UserAvatarWidget = new Lang.Class({ | ||||
|             this.actor.style = 'background-image: url("%s");'.format(iconFile); | ||||
|         } else { | ||||
|             this.actor.style = null; | ||||
|             this.actor.child = new St.Icon({ icon_name: 'avatar-default-symbolic', | ||||
|                                              icon_size: this._iconSize }); | ||||
|             this.actor.child = new St.Icon({ icon_name: 'avatar-default', | ||||
|                                              icon_type: St.IconType.SYMBOLIC, | ||||
|                                              icon_size: DIALOG_ICON_SIZE }); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
| @@ -109,7 +96,6 @@ const IMUserNameItem = new Lang.Class({ | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent({ reactive: false, | ||||
|                       can_focus: false, | ||||
|                       style_class: 'status-chooser-user-name' }); | ||||
|  | ||||
|         this._wrapper = new Shell.GenericContainer(); | ||||
| @@ -147,13 +133,12 @@ const IMStatusChooserItem = new Lang.Class({ | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent({ reactive: false, | ||||
|                       can_focus: false, | ||||
|                       style_class: 'status-chooser' }); | ||||
|  | ||||
|         this._userManager = AccountsService.UserManager.get_default(); | ||||
|         this._user = this._userManager.get_user(GLib.get_user_name()); | ||||
|  | ||||
|         this._avatar = new UserAvatarWidget(this._user, { reactive: true }); | ||||
|         this._avatar = new UserAvatarWidget(this._user); | ||||
|         this._iconBin = new St.Button({ child: this._avatar.actor }); | ||||
|         this.addActor(this._iconBin); | ||||
|  | ||||
| @@ -173,22 +158,22 @@ const IMStatusChooserItem = new Lang.Class({ | ||||
|  | ||||
|         let item; | ||||
|  | ||||
|         item = new IMStatusItem(_("Available"), 'user-available-symbolic'); | ||||
|         item = new IMStatusItem(_("Available"), 'user-available'); | ||||
|         this._combo.addMenuItem(item, IMStatus.AVAILABLE); | ||||
|  | ||||
|         item = new IMStatusItem(_("Busy"), 'user-busy-symbolic'); | ||||
|         item = new IMStatusItem(_("Busy"), 'user-busy'); | ||||
|         this._combo.addMenuItem(item, IMStatus.BUSY); | ||||
|  | ||||
|         item = new IMStatusItem(_("Invisible"), 'user-invisible-symbolic'); | ||||
|         item = new IMStatusItem(_("Invisible"), 'user-invisible'); | ||||
|         this._combo.addMenuItem(item, IMStatus.HIDDEN); | ||||
|  | ||||
|         item = new IMStatusItem(_("Away"), 'user-away-symbolic'); | ||||
|         item = new IMStatusItem(_("Away"), 'user-away'); | ||||
|         this._combo.addMenuItem(item, IMStatus.AWAY); | ||||
|  | ||||
|         item = new IMStatusItem(_("Idle"), 'user-idle-symbolic'); | ||||
|         item = new IMStatusItem(_("Idle"), 'user-idle'); | ||||
|         this._combo.addMenuItem(item, IMStatus.IDLE); | ||||
|  | ||||
|         item = new IMStatusItem(_("Offline"), 'user-offline-symbolic'); | ||||
|         item = new IMStatusItem(_("Unavailable"), 'user-offline'); | ||||
|         this._combo.addMenuItem(item, IMStatus.OFFLINE); | ||||
|  | ||||
|         this._combo.connect('active-item-changed', | ||||
| @@ -216,21 +201,20 @@ const IMStatusChooserItem = new Lang.Class({ | ||||
|                                  Lang.bind(this, this._IMAccountsChanged)); | ||||
|         this._accountMgr.prepare_async(null, Lang.bind(this, | ||||
|             function(mgr) { | ||||
|                 let [presence, status, msg] = mgr.get_most_available_presence(); | ||||
|  | ||||
|                 let savedPresence = global.settings.get_int('saved-im-presence'); | ||||
|  | ||||
|                 this._IMAccountsChanged(mgr); | ||||
|  | ||||
|                 if (this._networkMonitor.network_available) | ||||
|                     this._restorePresence(); | ||||
|                 else | ||||
|                     this._setComboboxPresence(Tp.ConnectionPresenceType.OFFLINE); | ||||
|             })); | ||||
|  | ||||
|         this._networkMonitor = Gio.NetworkMonitor.get_default(); | ||||
|         this._networkMonitor.connect('network-changed', | ||||
|             Lang.bind(this, function(monitor, available) { | ||||
|                 this._IMAccountsChanged(this._accountMgr); | ||||
|  | ||||
|                 if (available && !this._imPresenceRestored) | ||||
|                     this._restorePresence(); | ||||
|                 if (savedPresence == presence) { | ||||
|                     this._IMStatusChanged(mgr, presence, status, msg); | ||||
|                 } else { | ||||
|                     this._setComboboxPresence(savedPresence); | ||||
|                     status = this._statusForPresence(savedPresence); | ||||
|                     msg = msg ? msg : ''; | ||||
|                     mgr.set_all_requested_presences(savedPresence, status, msg); | ||||
|                 } | ||||
|             })); | ||||
|  | ||||
|         this._userLoadedId = this._user.connect('notify::is-loaded', | ||||
| @@ -243,25 +227,6 @@ const IMStatusChooserItem = new Lang.Class({ | ||||
|             if (this.actor.mapped) | ||||
|                 this._updateUser(); | ||||
|         })); | ||||
|  | ||||
|         this.connect('sensitive-changed', function(sensitive) { | ||||
|             this._avatar.setSensitive(sensitive); | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|     _restorePresence: function() { | ||||
|         let [presence, status, msg] = this._accountMgr.get_most_available_presence(); | ||||
|  | ||||
|         let savedPresence = global.settings.get_int('saved-im-presence'); | ||||
|  | ||||
|         if (savedPresence == presence) { | ||||
|             this._IMStatusChanged(this._accountMgr, presence, status, msg); | ||||
|         } else { | ||||
|             this._setComboboxPresence(savedPresence); | ||||
|             status = this._statusForPresence(savedPresence); | ||||
|             msg = msg ? msg : ''; | ||||
|             this._accountMgr.set_all_requested_presences(savedPresence, status, msg); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
| @@ -320,8 +285,7 @@ const IMStatusChooserItem = new Lang.Class({ | ||||
|         let accounts = mgr.get_valid_accounts().filter(function(account) { | ||||
|             return account.enabled; | ||||
|         }); | ||||
|         let sensitive = accounts.length > 0 && this._networkMonitor.network_available; | ||||
|         this._combo.setSensitive(sensitive); | ||||
|         this._combo.setSensitive(accounts.length > 0); | ||||
|     }, | ||||
|  | ||||
|     _IMStatusChanged: function(accountMgr, presence, status, message) { | ||||
| @@ -474,7 +438,6 @@ const UserMenuButton = new Lang.Class({ | ||||
|         let box = new St.BoxLayout({ name: 'panelUserMenu' }); | ||||
|         this.actor.add_actor(box); | ||||
|  | ||||
|         this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA }); | ||||
|         this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA }); | ||||
|  | ||||
|         this._userManager = AccountsService.UserManager.get_default(); | ||||
| @@ -483,33 +446,30 @@ const UserMenuButton = new Lang.Class({ | ||||
|         this._presence = new GnomeSession.Presence(); | ||||
|         this._session = new GnomeSession.SessionManager(); | ||||
|         this._haveShutdown = true; | ||||
|         this._haveSuspend = true; | ||||
|  | ||||
|         this._accountMgr = Tp.AccountManager.dup(); | ||||
|  | ||||
|         this._loginManager = LoginManager.getLoginManager(); | ||||
|         this._upClient = new UPowerGlib.Client(); | ||||
|         this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); | ||||
|  | ||||
|         this._iconBox = new St.Bin(); | ||||
|         box.add(this._iconBox, { y_align: St.Align.MIDDLE, y_fill: false }); | ||||
|  | ||||
|         let textureCache = St.TextureCache.get_default(); | ||||
|         this._offlineIcon = new St.Icon({ icon_name: 'user-offline-symbolic', | ||||
|         this._offlineIcon = new St.Icon({ icon_name: 'user-offline', | ||||
|                                           style_class: 'popup-menu-icon' }); | ||||
|         this._availableIcon = new St.Icon({ icon_name: 'user-available-symbolic', | ||||
|         this._availableIcon = new St.Icon({ icon_name: 'user-available', | ||||
|                                             style_class: 'popup-menu-icon' }); | ||||
|         this._busyIcon = new St.Icon({ icon_name: 'user-busy-symbolic', | ||||
|         this._busyIcon = new St.Icon({ icon_name: 'user-busy', | ||||
|                                        style_class: 'popup-menu-icon' }); | ||||
|         this._invisibleIcon = new St.Icon({ icon_name: 'user-invisible-symbolic', | ||||
|         this._invisibleIcon = new St.Icon({ icon_name: 'user-invisible', | ||||
|                                             style_class: 'popup-menu-icon' }); | ||||
|         this._awayIcon = new St.Icon({ icon_name: 'user-away-symbolic', | ||||
|         this._awayIcon = new St.Icon({ icon_name: 'user-away', | ||||
|                                        style_class: 'popup-menu-icon' }); | ||||
|         this._idleIcon = new St.Icon({ icon_name: 'user-idle-symbolic', | ||||
|         this._idleIcon = new St.Icon({ icon_name: 'user-idle', | ||||
|                                        style_class: 'popup-menu-icon' }); | ||||
|         this._pendingIcon = new St.Icon({ icon_name: 'user-status-pending-symbolic', | ||||
|         this._pendingIcon = new St.Icon({ icon_name: 'user-status-pending', | ||||
|                                           style_class: 'popup-menu-icon' }); | ||||
|         this._lockedIcon = new St.Icon({ icon_name: 'changes-prevent-symbolic', | ||||
|                                          style_class: 'popup-menu-icon' }); | ||||
|  | ||||
|         this._accountMgr.connect('most-available-presence-changed', | ||||
|                                   Lang.bind(this, this._updatePresenceIcon)); | ||||
| @@ -567,29 +527,19 @@ const UserMenuButton = new Lang.Class({ | ||||
|         // the lockdown setting changes, which should be close enough. | ||||
|         this.menu.connect('open-state-changed', Lang.bind(this, | ||||
|             function(menu, open) { | ||||
|                 if (!open) | ||||
|                     return; | ||||
|  | ||||
|                 this._updateHaveShutdown(); | ||||
|                 this._updateHaveSuspend(); | ||||
|                 this._updateHaveHibernate(); | ||||
|                 if (open) | ||||
|                     this._updateHaveShutdown(); | ||||
|             })); | ||||
|         this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY, | ||||
|                                        Lang.bind(this, this._updateHaveShutdown)); | ||||
|  | ||||
|         Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); | ||||
|         this._sessionUpdated(); | ||||
|         this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff)); | ||||
|     }, | ||||
|  | ||||
|     _sessionUpdated: function() { | ||||
|         this.actor.visible = !Main.sessionMode.isGreeter; | ||||
|  | ||||
|         let allowSettings = Main.sessionMode.allowSettings; | ||||
|         this._statusChooser.setSensitive(allowSettings); | ||||
|         this._systemSettings.visible = allowSettings; | ||||
|  | ||||
|         this.setSensitive(!Main.sessionMode.isLocked); | ||||
|         this._updatePresenceIcon(); | ||||
|     setLockedState: function(locked) { | ||||
|         if (locked) | ||||
|             this.menu.close(); | ||||
|         this.actor.reactive = !locked; | ||||
|     }, | ||||
|  | ||||
|     _onDestroy: function() { | ||||
| @@ -612,19 +562,19 @@ const UserMenuButton = new Lang.Class({ | ||||
|     _updateSwitchUser: function() { | ||||
|         let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY); | ||||
|         let multiUser = this._userManager.can_switch() && this._userManager.has_multiple_users; | ||||
|         let multiSession = Gdm.get_session_ids().length > 1; | ||||
|  | ||||
|         this._loginScreenItem.actor.visible = allowSwitch && multiUser; | ||||
|         this._loginScreenItem.label.set_text(multiUser ? _("Switch User") | ||||
|                                                        : _("Switch Session")); | ||||
|         this._loginScreenItem.actor.visible = allowSwitch && (multiUser || multiSession); | ||||
|     }, | ||||
|  | ||||
|     _updateLogout: function() { | ||||
|         let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY); | ||||
|         let alwaysShow = global.settings.get_boolean(ALWAYS_SHOW_LOG_OUT_KEY); | ||||
|         let systemAccount = this._user.system_account; | ||||
|         let localAccount = this._user.local_account; | ||||
|         let multiUser = this._userManager.has_multiple_users; | ||||
|         let multiSession = Gdm.get_session_ids().length > 1; | ||||
|  | ||||
|         this._logoutItem.actor.visible = allowLogout && (alwaysShow || multiUser || multiSession || systemAccount || !localAccount); | ||||
|         this._logoutItem.actor.visible = allowLogout && (multiUser || multiSession); | ||||
|     }, | ||||
|  | ||||
|     _updateLockScreen: function() { | ||||
| @@ -648,22 +598,9 @@ const UserMenuButton = new Lang.Class({ | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
|     _updateHaveSuspend: function() { | ||||
|         this._loginManager.canSuspend(Lang.bind(this, | ||||
|             function(result) { | ||||
|                 this._haveSuspend = result; | ||||
|                 this._updateSuspendOrPowerOff(); | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     _updateHaveHibernate: function() { | ||||
|         this._loginManager.canHibernate(Lang.bind(this, | ||||
|             function(result) { | ||||
|                 this._haveHibernate = result; | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     _updateSuspendOrPowerOff: function() { | ||||
|         this._haveSuspend = this._upClient.get_can_suspend(); | ||||
|  | ||||
|         if (!this._suspendOrPowerOffItem) | ||||
|             return; | ||||
|  | ||||
| @@ -686,9 +623,7 @@ const UserMenuButton = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _updatePresenceIcon: function(accountMgr, presence, status, message) { | ||||
|         if (Main.sessionMode.isLocked) | ||||
|             this._iconBox.child = this._lockedIcon; | ||||
|         else if (presence == Tp.ConnectionPresenceType.AVAILABLE) | ||||
|         if (presence == Tp.ConnectionPresenceType.AVAILABLE) | ||||
|             this._iconBox.child = this._availableIcon; | ||||
|         else if (presence == Tp.ConnectionPresenceType.BUSY) | ||||
|             this._iconBox.child = this._busyIcon; | ||||
| @@ -719,10 +654,8 @@ const UserMenuButton = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onAccountRemoved: function(accountMgr, account) { | ||||
|         if (account._changingId) { | ||||
|             account.disconnect(account._changingId); | ||||
|             account._changingId = 0; | ||||
|         } | ||||
|         account.disconnect(account._changingId); | ||||
|         account._changingId = 0; | ||||
|         this._updateChangingPresence(); | ||||
|     }, | ||||
|  | ||||
| @@ -748,7 +681,8 @@ const UserMenuButton = new Lang.Class({ | ||||
|         let item; | ||||
|  | ||||
|         item = new IMStatusChooserItem(); | ||||
|         item.connect('activate', Lang.bind(this, this._onMyAccountActivate)); | ||||
|         if (Main.sessionMode.allowSettings) | ||||
|             item.connect('activate', Lang.bind(this, this._onMyAccountActivate)); | ||||
|         this.menu.addMenuItem(item); | ||||
|         this._statusChooser = item; | ||||
|  | ||||
| @@ -760,10 +694,11 @@ const UserMenuButton = new Lang.Class({ | ||||
|         item = new PopupMenu.PopupSeparatorMenuItem(); | ||||
|         this.menu.addMenuItem(item); | ||||
|  | ||||
|         item = new PopupMenu.PopupMenuItem(_("Settings")); | ||||
|         item.connect('activate', Lang.bind(this, this._onPreferencesActivate)); | ||||
|         this.menu.addMenuItem(item); | ||||
|         this._systemSettings = item; | ||||
|         if (Main.sessionMode.allowSettings) { | ||||
|             item = new PopupMenu.PopupMenuItem(_("System Settings")); | ||||
|             item.connect('activate', Lang.bind(this, this._onPreferencesActivate)); | ||||
|             this.menu.addMenuItem(item); | ||||
|         } | ||||
|  | ||||
|         item = new PopupMenu.PopupSeparatorMenuItem(); | ||||
|         this.menu.addMenuItem(item); | ||||
| @@ -774,7 +709,7 @@ const UserMenuButton = new Lang.Class({ | ||||
|         this._loginScreenItem = item; | ||||
|  | ||||
|         item = new PopupMenu.PopupMenuItem(_("Log Out")); | ||||
|         item.connect('activate', Lang.bind(this, this.logOut)); | ||||
|         item.connect('activate', Lang.bind(this, this._onQuitSessionActivate)); | ||||
|         this.menu.addMenuItem(item); | ||||
|         this._logoutItem = item; | ||||
|  | ||||
| @@ -831,19 +766,17 @@ const UserMenuButton = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onLockScreenActivate: function() { | ||||
|         this.menu.close(BoxPointer.PopupAnimation.NONE); | ||||
|         Main.overview.hide(); | ||||
|         Main.screenShield.lock(true); | ||||
|     }, | ||||
|  | ||||
|     _onLoginScreenActivate: function() { | ||||
|         this.menu.close(BoxPointer.PopupAnimation.NONE); | ||||
|         Main.overview.hide(); | ||||
|         Main.screenShield.lock(false); | ||||
|         Gdm.goto_login_session_sync(null); | ||||
|         this._userManager.goto_login_session(); | ||||
|     }, | ||||
|  | ||||
|     logOut: function() { | ||||
|     _onQuitSessionActivate: function() { | ||||
|         Main.overview.hide(); | ||||
|         this._session.LogoutRemote(0); | ||||
|     }, | ||||
| @@ -855,60 +788,20 @@ const UserMenuButton = new Lang.Class({ | ||||
|         this._session.RebootRemote(); | ||||
|     }, | ||||
|  | ||||
|     shutdown: function() { | ||||
|         this._session.ShutdownRemote(); | ||||
|     }, | ||||
|  | ||||
|     suspend: function() { | ||||
|         if (!this._haveSuspend) | ||||
|             return false; | ||||
|  | ||||
|         // Ensure we only suspend after locking the screen | ||||
|         if (this._screenSaverSettings.get_boolean(LOCK_ENABLED_KEY)) { | ||||
|             let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() { | ||||
|                 Main.screenShield.disconnect(tmpId); | ||||
|  | ||||
|                 this._loginManager.suspend(); | ||||
|             })); | ||||
|  | ||||
|             this.menu.close(BoxPointer.PopupAnimation.NONE); | ||||
|             Main.screenShield.lock(true); | ||||
|         } else { | ||||
|             this._loginManager.suspend(); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     hibernate: function() { | ||||
|         if (!this._haveHibernate) | ||||
|             return false; | ||||
|  | ||||
|         // Ensure we only suspend after locking the screen | ||||
|         if (this._screenSaverSettings.get_boolean(LOCK_ENABLED_KEY)) { | ||||
|             let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() { | ||||
|                 Main.screenShield.disconnect(tmpId); | ||||
|  | ||||
|                 this._loginManager.hibernate(); | ||||
|             })); | ||||
|  | ||||
|             this.menu.close(BoxPointer.PopupAnimation.NONE); | ||||
|             Main.screenShield.lock(true); | ||||
|         } else { | ||||
|             this._loginManager.hibernate(); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _onSuspendOrPowerOffActivate: function() { | ||||
|         Main.overview.hide(); | ||||
|  | ||||
|         if (this._haveShutdown && | ||||
|             this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) { | ||||
|             this.shutdown(); | ||||
|             this._session.ShutdownRemote(); | ||||
|         } else { | ||||
|             this.suspend(); | ||||
|             let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() { | ||||
|                 Main.screenShield.disconnect(tmpId); | ||||
|  | ||||
|                 this._upClient.suspend_sync(null); | ||||
|             })); | ||||
|  | ||||
|             Main.screenShield.lock(true); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gio = imports.gi.Gio; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Meta = imports.gi.Meta; | ||||
| @@ -10,267 +9,176 @@ const Lang = imports.lang; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const AppDisplay = imports.ui.appDisplay; | ||||
| const Main = imports.ui.main; | ||||
| const RemoteSearch = imports.ui.remoteSearch; | ||||
| const Search = imports.ui.search; | ||||
| const SearchDisplay = imports.ui.searchDisplay; | ||||
| const ShellEntry = imports.ui.shellEntry; | ||||
| const Tweener = imports.ui.tweener; | ||||
| const Wanda = imports.ui.wanda; | ||||
| const WorkspacesView = imports.ui.workspacesView; | ||||
|  | ||||
| const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings'; | ||||
| const BaseTab = new Lang.Class({ | ||||
|     Name: 'BaseTab', | ||||
|  | ||||
| const FocusTrap = new Lang.Class({ | ||||
|     Name: 'FocusTrap', | ||||
|     Extends: St.Widget, | ||||
|     _init: function(titleActor, pageActor, name, a11yIcon) { | ||||
|         this.title = titleActor; | ||||
|         this.page = new St.Bin({ child: pageActor, | ||||
|                                  x_align: St.Align.START, | ||||
|                                  y_align: St.Align.START, | ||||
|                                  x_fill: true, | ||||
|                                  y_fill: true, | ||||
|                                  style_class: 'view-tab-page' }); | ||||
|  | ||||
|     vfunc_navigate_focus: function(from, direction) { | ||||
|         if (direction == Gtk.DirectionType.TAB_FORWARD || | ||||
|             direction == Gtk.DirectionType.TAB_BACKWARD) | ||||
|             return this.parent(from, direction); | ||||
|         return false; | ||||
|         if (this.title.can_focus) { | ||||
|             Main.ctrlAltTabManager.addGroup(this.title, name, a11yIcon); | ||||
|         } else { | ||||
|             Main.ctrlAltTabManager.addGroup(this.page, name, a11yIcon, | ||||
|                                             { proxy: this.title, | ||||
|                                               focusCallback: Lang.bind(this, this._a11yFocus) }); | ||||
|         } | ||||
|  | ||||
|         this.visible = false; | ||||
|     }, | ||||
|  | ||||
|     show: function(animate) { | ||||
|         this.visible = true; | ||||
|         this.page.show(); | ||||
|  | ||||
|         if (!animate) | ||||
|             return; | ||||
|  | ||||
|         this.page.opacity = 0; | ||||
|         Tweener.addTween(this.page, | ||||
|                          { opacity: 255, | ||||
|                            time: 0.1, | ||||
|                            transition: 'easeOutQuad' }); | ||||
|     }, | ||||
|  | ||||
|     hide: function() { | ||||
|         this.visible = false; | ||||
|         Tweener.addTween(this.page, | ||||
|                          { opacity: 0, | ||||
|                            time: 0.1, | ||||
|                            transition: 'easeOutQuad', | ||||
|                            onComplete: Lang.bind(this, | ||||
|                                function() { | ||||
|                                    this.page.hide(); | ||||
|                                }) | ||||
|                          }); | ||||
|     }, | ||||
|  | ||||
|     _a11yFocus: function() { | ||||
|         this._activate(); | ||||
|         this.page.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); | ||||
|     }, | ||||
|  | ||||
|     _activate: function() { | ||||
|         this.emit('activated'); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(BaseTab.prototype); | ||||
|  | ||||
|  | ||||
| const ViewTab = new Lang.Class({ | ||||
|     Name: 'ViewTab', | ||||
|     Extends: BaseTab, | ||||
|  | ||||
|     _init: function(id, label, pageActor, a11yIcon) { | ||||
|         this.id = id; | ||||
|  | ||||
|         let titleActor = new St.Button({ label: label, | ||||
|                                          style_class: 'view-tab-title' }); | ||||
|         titleActor.connect('clicked', Lang.bind(this, this._activate)); | ||||
|  | ||||
|         this.parent(titleActor, pageActor, label, a11yIcon); | ||||
|     } | ||||
| }); | ||||
|  | ||||
|  | ||||
| const ViewSelector = new Lang.Class({ | ||||
|     Name: 'ViewSelector', | ||||
|  | ||||
|     _init : function(searchEntry, showAppsButton) { | ||||
|         this.actor = new St.BoxLayout({ name: 'viewSelector', | ||||
|                                         vertical: true }); | ||||
|  | ||||
|         this._showAppsButton = showAppsButton; | ||||
|         this._showAppsButton.connect('notify::checked', Lang.bind(this, this._onShowAppsButtonToggled)); | ||||
|  | ||||
|         this._pageArea = new Shell.Stack(); | ||||
|         this.actor.add(this._pageArea, { x_fill: true, | ||||
|                                          y_fill: true, | ||||
|                                          expand: true }); | ||||
|  | ||||
|         this._activePage = null; | ||||
| const SearchTab = new Lang.Class({ | ||||
|     Name: 'SearchTab', | ||||
|     Extends: BaseTab, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.active = false; | ||||
|         this._searchPending = false; | ||||
|         this._searchTimeoutId = 0; | ||||
|  | ||||
|         this._searchSystem = new Search.SearchSystem(); | ||||
|         this._openSearchSystem = new Search.OpenSearchSystem(); | ||||
|  | ||||
|         this._entry = searchEntry; | ||||
|         this._entry = new St.Entry({ name: 'searchEntry', | ||||
|                                      /* Translators: this is the text displayed | ||||
|                                         in the search entry when no search is | ||||
|                                         active; it should not exceed ~30 | ||||
|                                         characters. */ | ||||
|                                      hint_text: _("Type to search..."), | ||||
|                                      track_hover: true, | ||||
|                                      can_focus: true }); | ||||
|         ShellEntry.addContextMenu(this._entry); | ||||
|  | ||||
|         this._text = this._entry.clutter_text; | ||||
|         this._text.connect('text-changed', Lang.bind(this, this._onTextChanged)); | ||||
|         this._text.connect('key-press-event', Lang.bind(this, this._onKeyPress)); | ||||
|  | ||||
|         this._inactiveIcon = new St.Icon({ style_class: 'search-entry-icon', | ||||
|                                            icon_name: 'edit-find', | ||||
|                                            icon_type: St.IconType.SYMBOLIC }); | ||||
|         this._activeIcon = new St.Icon({ style_class: 'search-entry-icon', | ||||
|                                          icon_name: 'edit-clear', | ||||
|                                          icon_type: St.IconType.SYMBOLIC }); | ||||
|         this._entry.set_secondary_icon(this._inactiveIcon); | ||||
|  | ||||
|         this._iconClickedId = 0; | ||||
|  | ||||
|         this._searchResults = new SearchDisplay.SearchResults(this._searchSystem, this._openSearchSystem); | ||||
|         this.parent(this._entry, this._searchResults.actor, _("Search"), 'edit-find'); | ||||
|  | ||||
|         this._text.connect('text-changed', Lang.bind(this, this._onTextChanged)); | ||||
|         this._text.connect('key-press-event', Lang.bind(this, function (o, e) { | ||||
|             // We can't connect to 'activate' here because search providers | ||||
|             // might want to do something with the modifiers in activateDefault. | ||||
|             let symbol = e.get_key_symbol(); | ||||
|             if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) { | ||||
|                 if (this._searchTimeoutId > 0) { | ||||
|                     Mainloop.source_remove(this._searchTimeoutId); | ||||
|                     this._doSearch(); | ||||
|                 } | ||||
|                 this._searchResults.activateDefault(); | ||||
|                 return true; | ||||
|             } | ||||
|             return false; | ||||
|         })); | ||||
|  | ||||
|         this._entry.connect('notify::mapped', Lang.bind(this, this._onMapped)); | ||||
|  | ||||
|         global.stage.connect('notify::key-focus', Lang.bind(this, this._onStageKeyFocusChanged)); | ||||
|  | ||||
|         this._capturedEventId = 0; | ||||
|  | ||||
|         this._text.connect('key-focus-in', Lang.bind(this, function() { | ||||
|             this._searchResults.highlightDefault(true); | ||||
|         })); | ||||
|         this._text.connect('key-focus-out', Lang.bind(this, function() { | ||||
|             this._searchResults.highlightDefault(false); | ||||
|         })); | ||||
|         this._entry.connect('notify::mapped', Lang.bind(this, this._onMapped)); | ||||
|         global.stage.connect('notify::key-focus', Lang.bind(this, this._onStageKeyFocusChanged)); | ||||
|  | ||||
|         this._inactiveIcon = new St.Icon({ style_class: 'search-entry-icon', | ||||
|                                            icon_name: 'edit-find-symbolic' }); | ||||
|         this._activeIcon = new St.Icon({ style_class: 'search-entry-icon', | ||||
|                                          icon_name: 'edit-clear-symbolic' }); | ||||
|         this._entry.set_secondary_icon(this._inactiveIcon); | ||||
|  | ||||
|         this._iconClickedId = 0; | ||||
|         this._capturedEventId = 0; | ||||
|  | ||||
|         this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay(); | ||||
|         this._workspacesPage = this._addPage(this._workspacesDisplay.actor, null, | ||||
|                                              _("Windows"), 'text-x-generic-symbolic'); | ||||
|  | ||||
|         this._appDisplay = new AppDisplay.AllAppDisplay(); | ||||
|         this._appsPage = this._addPage(this._appDisplay.actor, null, | ||||
|                                        _("Applications"), 'system-run-symbolic'); | ||||
|  | ||||
|         this._searchResults = new SearchDisplay.SearchResults(this._searchSystem); | ||||
|         this._searchPage = this._addPage(this._searchResults.actor, this._entry, | ||||
|                                          _("Search"), 'edit-find-symbolic'); | ||||
|  | ||||
|         // Default search providers | ||||
|         // Wanda comes obviously first | ||||
|         this.addSearchProvider(new Wanda.WandaSearchProvider()); | ||||
|         this.addSearchProvider(new AppDisplay.AppSearchProvider()); | ||||
|         this.addSearchProvider(new AppDisplay.SettingsSearchProvider()); | ||||
|  | ||||
|         // Load remote search providers provided by applications | ||||
|         RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider)); | ||||
|  | ||||
|         // Since the entry isn't inside the results container we install this | ||||
|         // dummy widget as the last results container child so that we can | ||||
|         // include the entry in the keynav tab path | ||||
|         this._focusTrap = new FocusTrap({ can_focus: true }); | ||||
|         // include the entry in the keynav tab path... | ||||
|         this._focusTrap = new St.Bin({ can_focus: true }); | ||||
|         this._focusTrap.connect('key-focus-in', Lang.bind(this, function() { | ||||
|             this._entry.grab_key_focus(); | ||||
|         })); | ||||
|         // ... but make it unfocusable using arrow keys keynav by making its | ||||
|         // bounding box always contain the possible focus source's bounding | ||||
|         // box since StWidget's keynav logic won't ever select it as a target | ||||
|         // in that case. | ||||
|         this._focusTrap.add_constraint(new Clutter.BindConstraint({ source: this._searchResults.actor, | ||||
|                                                                     coordinate: Clutter.BindCoordinate.ALL })); | ||||
|         this._searchResults.actor.add_actor(this._focusTrap); | ||||
|  | ||||
|         global.focus_manager.add_group(this._searchResults.actor); | ||||
|  | ||||
|         Main.overview.connect('item-drag-begin', | ||||
|                               Lang.bind(this, this._resetShowAppsButton)); | ||||
|  | ||||
|         this._stageKeyPressId = 0; | ||||
|         Main.overview.connect('showing', Lang.bind(this, | ||||
|             function () { | ||||
|                 this._resetShowAppsButton(); | ||||
|                 this._stageKeyPressId = global.stage.connect('key-press-event', | ||||
|                                                              Lang.bind(this, this._onStageKeyPress)); | ||||
|             })); | ||||
|         Main.overview.connect('hiding', Lang.bind(this, | ||||
|             function () { | ||||
|                 this._resetShowAppsButton(); | ||||
|                 if (this._stageKeyPressId != 0) { | ||||
|                     global.stage.disconnect(this._stageKeyPressId); | ||||
|                     this._stageKeyPressId = 0; | ||||
|                 } | ||||
|             })); | ||||
|  | ||||
|         // Public constraints which may be used to tie actors' height or | ||||
|         // vertical position to the current tab's content; as the content's | ||||
|         // height and position depend on the view selector's style properties | ||||
|         // (e.g. font size, padding, spacing, ...) it would be extremely hard | ||||
|         // and ugly to get these from the outside. While it would be possible | ||||
|         // to use position and height properties directly, outside code would | ||||
|         // need to ensure that the content is properly allocated before | ||||
|         // accessing the properties. | ||||
|         this.constrainHeight = new Clutter.BindConstraint({ source: this._pageArea, | ||||
|                                                             coordinate: Clutter.BindCoordinate.HEIGHT }); | ||||
|  | ||||
|         global.display.add_keybinding('toggle-application-view', | ||||
|                                       new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }), | ||||
|                                       Meta.KeyBindingFlags.NONE, | ||||
|                                       Lang.bind(this, this._showWithAppsPage)); | ||||
|     }, | ||||
|  | ||||
|     _showWithAppsPage: function() { | ||||
|         Main.overview.show(); | ||||
|         this._showAppsButton.set_checked(true); | ||||
|     }, | ||||
|  | ||||
|     show: function() { | ||||
|         this._activePage = this._workspacesPage; | ||||
|  | ||||
|         this._appsPage.hide(); | ||||
|         this._searchPage.hide(); | ||||
|         this._workspacesDisplay.show(); | ||||
|  | ||||
|         if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) | ||||
|             Main.overview.fadeOutDesktop(); | ||||
|  | ||||
|         this._showPage(this._workspacesPage); | ||||
|     }, | ||||
|  | ||||
|     zoomFromOverview: function() { | ||||
|         this._workspacesDisplay.zoomFromOverview(); | ||||
|  | ||||
|         if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) | ||||
|             Main.overview.fadeInDesktop(); | ||||
|     }, | ||||
|  | ||||
|     hide: function() { | ||||
|         this._workspacesDisplay.hide(); | ||||
|     }, | ||||
|  | ||||
|     _addPage: function(actor, a11yFocus, name, a11yIcon) { | ||||
|         let page = new St.Bin({ child: actor, | ||||
|                                 x_align: St.Align.START, | ||||
|                                 y_align: St.Align.START, | ||||
|                                 x_fill: true, | ||||
|                                 y_fill: true }); | ||||
|         if (a11yFocus) | ||||
|             Main.ctrlAltTabManager.addGroup(a11yFocus, name, a11yIcon); | ||||
|         else | ||||
|             Main.ctrlAltTabManager.addGroup(actor, name, a11yIcon, | ||||
|                                             { proxy: this.actor, | ||||
|                                               focusCallback: Lang.bind(this, | ||||
|                                                   function() { | ||||
|                                                       this._a11yFocusPage(page); | ||||
|                                                   }) | ||||
|                                             });; | ||||
|         this._pageArea.add_actor(page); | ||||
|         return page; | ||||
|     }, | ||||
|  | ||||
|     _showPage: function(page) { | ||||
|         if(page == this._activePage) | ||||
|             return; | ||||
|  | ||||
|         if(this._activePage) { | ||||
|             Tweener.addTween(this._activePage, | ||||
|                              { opacity: 0, | ||||
|                                time: 0.1, | ||||
|                                transition: 'easeOutQuad', | ||||
|                                onComplete: Lang.bind(this, | ||||
|                                    function() { | ||||
|                                        this._activePage.hide(); | ||||
|                                        this._activePage = page; | ||||
|                                    }) | ||||
|                              }); | ||||
|         } | ||||
|  | ||||
|         page.show(); | ||||
|         Tweener.addTween(page, | ||||
|                          { opacity: 255, | ||||
|                            time: 0.1, | ||||
|                            transition: 'easeOutQuad' | ||||
|                          }); | ||||
|     }, | ||||
|  | ||||
|     _a11yFocusPage: function(page) { | ||||
|         this._showAppsButton.checked = page == this._appsPage; | ||||
|         page.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); | ||||
|     }, | ||||
|  | ||||
|     _onShowAppsButtonToggled: function() { | ||||
|         if (this.active) | ||||
|             this.reset(); | ||||
|         else | ||||
|             this._showPage(this._showAppsButton.checked ? this._appsPage | ||||
|                                                         : this._workspacesPage); | ||||
|     }, | ||||
|  | ||||
|     _resetShowAppsButton: function() { | ||||
|         this._showAppsButton.checked = false; | ||||
|     }, | ||||
|  | ||||
|     _onStageKeyPress: function(actor, event) { | ||||
|         let modifiers = event.get_state(); | ||||
|         let symbol = event.get_key_symbol(); | ||||
|  | ||||
|         if (symbol == Clutter.Escape) { | ||||
|             if (this.active) | ||||
|                 this.reset(); | ||||
|             else if (this._showAppsButton.checked) | ||||
|                 this._resetShowAppsButton(); | ||||
|             else | ||||
|                 Main.overview.hide(); | ||||
|             return true; | ||||
|         } else if (Clutter.keysym_to_unicode(symbol) || | ||||
|                    (symbol == Clutter.BackSpace && this.active)) { | ||||
|             this.startSearch(event); | ||||
|         } else if (!this.active) { | ||||
|             if (symbol == Clutter.Tab || symbol == Clutter.Down) { | ||||
|                 this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); | ||||
|                 return true; | ||||
|             } else if (symbol == Clutter.ISO_Left_Tab) { | ||||
|                 this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_BACKWARD, false); | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     _searchCancelled: function() { | ||||
|         this._showPage(this._showAppsButton.checked ? this._appsPage | ||||
|                                                     : this._workspacesPage); | ||||
|         this.parent(); | ||||
|  | ||||
|         // Leave the entry focused when it doesn't have any text; | ||||
|         // when replacing a selected search term, Clutter emits | ||||
| @@ -319,9 +227,19 @@ const ViewSelector = new Lang.Class({ | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     addSearchProvider: function(provider) { | ||||
|         this._searchSystem.registerProvider(provider); | ||||
|         this._searchResults.createProviderMeta(provider); | ||||
|     }, | ||||
|  | ||||
|     removeSearchProvider: function(provider) { | ||||
|         this._searchSystem.unregisterProvider(provider); | ||||
|         this._searchResults.destroyProviderMeta(provider); | ||||
|     }, | ||||
|  | ||||
|     startSearch: function(event) { | ||||
|         global.stage.set_key_focus(this._text); | ||||
|         this._text.event(event, true); | ||||
|         this._text.event(event, false); | ||||
|     }, | ||||
|  | ||||
|     // the entry does not show the hint | ||||
| @@ -345,13 +263,14 @@ const ViewSelector = new Lang.Class({ | ||||
|                         this.reset(); | ||||
|                     })); | ||||
|             } | ||||
|             this._activate(); | ||||
|         } else { | ||||
|             if (this._iconClickedId > 0) | ||||
|                 this._entry.disconnect(this._iconClickedId); | ||||
|             this._iconClickedId = 0; | ||||
|  | ||||
|             this._entry.set_secondary_icon(this._inactiveIcon); | ||||
|             this._searchCancelled(); | ||||
|             this.emit('search-cancelled'); | ||||
|         } | ||||
|         if (!this.active) { | ||||
|             if (this._searchTimeoutId > 0) { | ||||
| @@ -372,15 +291,6 @@ const ViewSelector = new Lang.Class({ | ||||
|                 this.reset(); | ||||
|                 return true; | ||||
|             } | ||||
|         } else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) { | ||||
|             // We can't connect to 'activate' here because search providers | ||||
|             // might want to do something with the modifiers in activateDefault. | ||||
|             if (this._searchTimeoutId > 0) { | ||||
|                 Mainloop.source_remove(this._searchTimeoutId); | ||||
|                 this._doSearch(); | ||||
|             } | ||||
|             this._searchResults.activateDefault(); | ||||
|             return true; | ||||
|         } else if (this.active) { | ||||
|             let arrowNext, nextDirection; | ||||
|             if (entry.get_text_direction() == Clutter.TextDirection.RTL) { | ||||
| @@ -430,17 +340,259 @@ const ViewSelector = new Lang.Class({ | ||||
|         let text = this._text.get_text().replace(/^\s+/g, '').replace(/\s+$/g, ''); | ||||
|         this._searchResults.doSearch(text); | ||||
|  | ||||
|         this._showPage(this._searchPage); | ||||
|         return false; | ||||
|     } | ||||
| }); | ||||
|  | ||||
|  | ||||
| const ViewSelector = new Lang.Class({ | ||||
|     Name: 'ViewSelector', | ||||
|  | ||||
|     _init : function() { | ||||
|         this.actor = new St.BoxLayout({ name: 'viewSelector', | ||||
|                                         vertical: true }); | ||||
|  | ||||
|         // The tab bar is located at the top of the view selector and | ||||
|         // holds both "normal" tab labels and the search entry. The former | ||||
|         // is left aligned, the latter right aligned - unless the text | ||||
|         // direction is RTL, in which case the order is reversed. | ||||
|         this._tabBar = new Shell.GenericContainer(); | ||||
|         this._tabBar.connect('get-preferred-width', | ||||
|                              Lang.bind(this, this._getPreferredTabBarWidth)); | ||||
|         this._tabBar.connect('get-preferred-height', | ||||
|                              Lang.bind(this, this._getPreferredTabBarHeight)); | ||||
|         this._tabBar.connect('allocate', | ||||
|                              Lang.bind(this, this._allocateTabBar)); | ||||
|         this.actor.add(this._tabBar); | ||||
|  | ||||
|         // Box to hold "normal" tab labels | ||||
|         this._tabBox = new St.BoxLayout({ name: 'viewSelectorTabBar' }); | ||||
|         this._tabBar.add_actor(this._tabBox); | ||||
|  | ||||
|         // The searchArea just holds the entry | ||||
|         this._searchArea = new St.Bin({ name: 'searchArea' }); | ||||
|         this._tabBar.add_actor(this._searchArea); | ||||
|  | ||||
|         // The page area holds the tab pages. Every page is given the | ||||
|         // area's full allocation, so that the pages would appear on top | ||||
|         // of each other if the inactive ones weren't hidden. | ||||
|         this._pageArea = new Shell.Stack(); | ||||
|         this.actor.add(this._pageArea, { x_fill: true, | ||||
|                                          y_fill: true, | ||||
|                                          expand: true }); | ||||
|  | ||||
|         this._tabs = []; | ||||
|         this._activeTab = null; | ||||
|  | ||||
|         this._searchTab = new SearchTab(); | ||||
|         this._searchArea.set_child(this._searchTab.title); | ||||
|         this._addTab(this._searchTab); | ||||
|  | ||||
|         this._searchTab.connect('search-cancelled', Lang.bind(this, | ||||
|             function() { | ||||
|                 this._switchTab(this._activeTab); | ||||
|             })); | ||||
|  | ||||
|         Main.overview.connect('item-drag-begin', | ||||
|                               Lang.bind(this, this._switchDefaultTab)); | ||||
|  | ||||
|         this._stageKeyPressId = 0; | ||||
|         Main.overview.connect('showing', Lang.bind(this, | ||||
|             function () { | ||||
|                 this._switchDefaultTab(); | ||||
|                 this._stageKeyPressId = global.stage.connect('key-press-event', | ||||
|                                                              Lang.bind(this, this._onStageKeyPress)); | ||||
|             })); | ||||
|         Main.overview.connect('hiding', Lang.bind(this, | ||||
|             function () { | ||||
|                 this._switchDefaultTab(); | ||||
|                 if (this._stageKeyPressId != 0) { | ||||
|                     global.stage.disconnect(this._stageKeyPressId); | ||||
|                     this._stageKeyPressId = 0; | ||||
|                 } | ||||
|             })); | ||||
|  | ||||
|         // Public constraints which may be used to tie actors' height or | ||||
|         // vertical position to the current tab's content; as the content's | ||||
|         // height and position depend on the view selector's style properties | ||||
|         // (e.g. font size, padding, spacing, ...) it would be extremely hard | ||||
|         // and ugly to get these from the outside. While it would be possible | ||||
|         // to use position and height properties directly, outside code would | ||||
|         // need to ensure that the content is properly allocated before | ||||
|         // accessing the properties. | ||||
|         this.constrainY = new Clutter.BindConstraint({ source: this._pageArea, | ||||
|                                                        coordinate: Clutter.BindCoordinate.Y }); | ||||
|         this.constrainHeight = new Clutter.BindConstraint({ source: this._pageArea, | ||||
|                                                             coordinate: Clutter.BindCoordinate.HEIGHT }); | ||||
|     }, | ||||
|  | ||||
|     _addTab: function(tab) { | ||||
|         tab.page.hide(); | ||||
|         this._pageArea.add_actor(tab.page); | ||||
|         tab.connect('activated', Lang.bind(this, function(tab) { | ||||
|             this._switchTab(tab); | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     addViewTab: function(id, title, pageActor, a11yIcon) { | ||||
|         let viewTab = new ViewTab(id, title, pageActor, a11yIcon); | ||||
|         this._tabs.push(viewTab); | ||||
|         this._tabBox.add(viewTab.title); | ||||
|         this._addTab(viewTab); | ||||
|     }, | ||||
|  | ||||
|     _switchTab: function(tab) { | ||||
|         let firstSwitch = this._activeTab == null; | ||||
|  | ||||
|         if (this._activeTab && this._activeTab.visible) { | ||||
|             if (this._activeTab == tab) | ||||
|                 return; | ||||
|             this._activeTab.title.remove_style_pseudo_class('selected'); | ||||
|             this._activeTab.hide(); | ||||
|         } | ||||
|  | ||||
|         if (tab != this._searchTab) { | ||||
|             tab.title.add_style_pseudo_class('selected'); | ||||
|             this._activeTab = tab; | ||||
|             if (this._searchTab.visible) { | ||||
|                 this._searchTab.hide(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Only fade when switching between tabs, | ||||
|         // not when setting the initially selected one. | ||||
|         if (!tab.visible) | ||||
|             tab.show(!firstSwitch); | ||||
|     }, | ||||
|  | ||||
|     switchTab: function(id) { | ||||
|         for (let i = 0; i < this._tabs.length; i++) | ||||
|             if (this._tabs[i].id == id) { | ||||
|                 this._switchTab(this._tabs[i]); | ||||
|                 break; | ||||
|             } | ||||
|     }, | ||||
|  | ||||
|     _switchDefaultTab: function() { | ||||
|         if (this._tabs.length > 0) | ||||
|             this._switchTab(this._tabs[0]); | ||||
|     }, | ||||
|  | ||||
|     _nextTab: function() { | ||||
|         if (this._tabs.length == 0 || | ||||
|             this._tabs[this._tabs.length - 1] == this._activeTab) | ||||
|             return; | ||||
|  | ||||
|         for (let i = 0; i < this._tabs.length; i++) | ||||
|             if (this._tabs[i] == this._activeTab) { | ||||
|                 this._switchTab(this._tabs[i + 1]); | ||||
|                 return; | ||||
|             } | ||||
|     }, | ||||
|  | ||||
|     _prevTab: function() { | ||||
|         if (this._tabs.length == 0 || this._tabs[0] == this._activeTab) | ||||
|             return; | ||||
|  | ||||
|         for (let i = 0; i < this._tabs.length; i++) | ||||
|             if (this._tabs[i] == this._activeTab) { | ||||
|                 this._switchTab(this._tabs[i - 1]); | ||||
|                 return; | ||||
|             } | ||||
|     }, | ||||
|  | ||||
|     _getPreferredTabBarWidth: function(box, forHeight, alloc) { | ||||
|         let children = box.get_children(); | ||||
|         for (let i = 0; i < children.length; i++) { | ||||
|             let [childMin, childNat] = children[i].get_preferred_width(forHeight); | ||||
|             alloc.min_size += childMin; | ||||
|             alloc.natural_size += childNat; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _getPreferredTabBarHeight: function(box, forWidth, alloc) { | ||||
|         let children = box.get_children(); | ||||
|         for (let i = 0; i < children.length; i++) { | ||||
|             let [childMin, childNatural] = children[i].get_preferred_height(forWidth); | ||||
|             if (childMin > alloc.min_size) | ||||
|                 alloc.min_size = childMin; | ||||
|             if (childNatural > alloc.natural_size) | ||||
|                 alloc.natural_size = childNatural; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _allocateTabBar: function(container, box, flags) { | ||||
|         let allocWidth = box.x2 - box.x1; | ||||
|         let allocHeight = box.y2 - box.y1; | ||||
|  | ||||
|         let [searchMinWidth, searchNatWidth] = this._searchArea.get_preferred_width(-1); | ||||
|         let [barMinWidth, barNatWidth] = this._tabBox.get_preferred_width(-1); | ||||
|         let childBox = new Clutter.ActorBox(); | ||||
|         childBox.y1 = 0; | ||||
|         childBox.y2 = allocHeight; | ||||
|         if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) { | ||||
|             childBox.x1 = allocWidth - barNatWidth; | ||||
|             childBox.x2 = allocWidth; | ||||
|         } else { | ||||
|             childBox.x1 = 0; | ||||
|             childBox.x2 = barNatWidth; | ||||
|         } | ||||
|         this._tabBox.allocate(childBox, flags); | ||||
|  | ||||
|         if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) { | ||||
|             childBox.x1 = 0; | ||||
|             childBox.x2 = searchNatWidth; | ||||
|         } else { | ||||
|             childBox.x1 = allocWidth - searchNatWidth; | ||||
|             childBox.x2 = allocWidth; | ||||
|         } | ||||
|         this._searchArea.allocate(childBox, flags); | ||||
|  | ||||
|         Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, | ||||
|             function() { | ||||
|                 this.constrainY.offset = this.actor.y; | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
|     _onStageKeyPress: function(actor, event) { | ||||
|         let modifiers = event.get_state(); | ||||
|         let symbol = event.get_key_symbol(); | ||||
|  | ||||
|         if (symbol == Clutter.Escape) { | ||||
|             if (this._searchTab.active) | ||||
|                 this._searchTab.reset(); | ||||
|             else | ||||
|                 Main.overview.hide(); | ||||
|             return true; | ||||
|         } else if (Clutter.keysym_to_unicode(symbol) || | ||||
|                    (symbol == Clutter.BackSpace && this._searchTab.active)) { | ||||
|             this._searchTab.startSearch(event); | ||||
|         } else if (!this._searchTab.active) { | ||||
|             if (modifiers & Clutter.ModifierType.CONTROL_MASK) { | ||||
|                 if (symbol == Clutter.Page_Up) { | ||||
|                     this._prevTab(); | ||||
|                     return true; | ||||
|                 } else if (symbol == Clutter.Page_Down) { | ||||
|                     this._nextTab(); | ||||
|                     return true; | ||||
|                 } | ||||
|             } else if (symbol == Clutter.Tab) { | ||||
|                 this._activeTab.page.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); | ||||
|                 return true; | ||||
|             } else if (symbol == Clutter.ISO_Left_Tab) { | ||||
|                 this._activeTab.page.navigate_focus(null, Gtk.DirectionType.TAB_BACKWARD, false); | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     addSearchProvider: function(provider) { | ||||
|         this._searchSystem.registerProvider(provider); | ||||
|         this._searchResults.createProviderMeta(provider); | ||||
|         this._searchTab.addSearchProvider(provider); | ||||
|     }, | ||||
|  | ||||
|     removeSearchProvider: function(provider) { | ||||
|         this._searchSystem.unregisterProvider(provider); | ||||
|         this._searchResults.destroyProviderMeta(provider); | ||||
|         this._searchTab.removeSearchProvider(provider); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(ViewSelector.prototype); | ||||
|   | ||||
| @@ -1,13 +1,14 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GdkPixbuf = imports.gi.GdkPixbuf; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gio = imports.gi.Gio; | ||||
| const Lang = imports.lang; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Signals = imports.signals; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const IconGrid = imports.ui.iconGrid; | ||||
| const Layout = imports.ui.layout; | ||||
| const Main = imports.ui.main; | ||||
| const Search = imports.ui.search; | ||||
|  | ||||
| @@ -48,12 +49,23 @@ const WandaIcon = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     createIcon: function(iconSize) { | ||||
|         if (this._animations) | ||||
|             this._animations.destroy(); | ||||
|  | ||||
|         if (!this._imageFile) { | ||||
|             return new St.Icon({ icon_name: 'face-smile', | ||||
|                                  icon_size: iconSize }); | ||||
|                                  icon_type: St.IconType.FULLCOLOR, | ||||
|                                  icon_size: iconSize | ||||
|                                }); | ||||
|         } | ||||
|  | ||||
|         this._animations = St.TextureCache.get_default().load_sliced_image(this._imageFile, this._imgWidth, this._imgHeight); | ||||
|         this._animations.connect('destroy', Lang.bind(this, function() { | ||||
|             if (this._timeoutId) | ||||
|                 GLib.source_remove(this._timeoutId); | ||||
|             this._timeoutId = 0; | ||||
|             this._animations = null; | ||||
|         })); | ||||
|         this._animations.connect('notify::mapped', Lang.bind(this, function() { | ||||
|             if (this._animations.mapped && !this._timeoutId) { | ||||
|                 this._timeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, FISH_SPEED, Lang.bind(this, this._update)); | ||||
| @@ -66,16 +78,11 @@ const WandaIcon = new Lang.Class({ | ||||
|             } | ||||
|         })); | ||||
|  | ||||
|         this._i = 0; | ||||
|  | ||||
|         return this._animations; | ||||
|     }, | ||||
|  | ||||
|     _createIconTexture: function(size) { | ||||
|         if (size == this.iconSize) | ||||
|             return; | ||||
|  | ||||
|         this.parent(size); | ||||
|     }, | ||||
|  | ||||
|     _update: function() { | ||||
|         let n = this._animations.get_n_children(); | ||||
|         if (n == 0) { | ||||
| @@ -135,18 +142,17 @@ const FortuneDialog = new Lang.Class({ | ||||
|         this._button.connect('clicked', Lang.bind(this, this.destroy)); | ||||
|         this._button.child = this._box; | ||||
|  | ||||
|         this._bin = new St.Bin({ x_align: St.Align.MIDDLE, | ||||
|                                  y_align: St.Align.MIDDLE }); | ||||
|         this._bin.add_constraint(new Layout.MonitorConstraint({ primary: true })); | ||||
|         this._bin.add_actor(this._button); | ||||
|         let monitor = Main.layoutManager.primaryMonitor; | ||||
|  | ||||
|         Main.layoutManager.addChrome(this._bin); | ||||
|         Main.layoutManager.addChrome(this._button); | ||||
|         this._button.set_position(Math.floor(monitor.width / 2 - this._button.width / 2), | ||||
|                                   Math.floor(monitor.height / 2 - this._button.height / 2)); | ||||
|  | ||||
|         GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 10, Lang.bind(this, this.destroy)); | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
|         this._bin.destroy(); | ||||
|         this._button.destroy(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -167,8 +173,16 @@ const WandaSearchProvider = new Lang.Class({ | ||||
|                     // only one which speaks the truth! | ||||
|                     'name': capitalize(fish[0]), | ||||
|                     'createIcon': function(iconSize) { | ||||
|                         return new St.Icon({ gicon: Gio.icon_new_for_string('face-smile'), | ||||
|                                              icon_size: iconSize }); | ||||
|                         // for DND only (maybe could be improved) | ||||
|                         // DON'T use St.Icon here, it crashes the shell | ||||
|                         // (dnd.js code assumes it can query the actor size | ||||
|                         // without parenting it, while StWidget accesses | ||||
|                         // StThemeNode in get_preferred_width/height, which | ||||
|                         // triggers an assertion failure) | ||||
|                         return St.TextureCache.get_default().load_icon_name(null, | ||||
|                                                                             'face-smile', | ||||
|                                                                             St.IconType.FULLCOLOR, | ||||
|                                                                             iconSize); | ||||
|                     } | ||||
|                   }]); | ||||
|     }, | ||||
|   | ||||
| @@ -39,8 +39,6 @@ const WindowAttentionHandler = new Lang.Class({ | ||||
|         let [title, banner] = this._getTitleAndBanner(app, window); | ||||
|  | ||||
|         let notification = new MessageTray.Notification(source, title, banner); | ||||
|         notification.setForFeedback(true); | ||||
|  | ||||
|         source.notify(notification); | ||||
|  | ||||
|         source.signalIDs.push(window.connect('notify::title', Lang.bind(this, function() { | ||||
|   | ||||
| @@ -12,11 +12,11 @@ const AltTab = imports.ui.altTab; | ||||
| const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup; | ||||
| const Main = imports.ui.main; | ||||
| const Tweener = imports.ui.tweener; | ||||
| const Util = imports.misc.util; | ||||
|  | ||||
| const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings'; | ||||
| const WINDOW_ANIMATION_TIME = 0.25; | ||||
| const DIM_BRIGHTNESS = -0.3; | ||||
| const DIM_DESATURATION = 0.6; | ||||
| const DIM_BRIGHTNESS = -0.1; | ||||
| const DIM_TIME = 0.500; | ||||
| const UNDIM_TIME = 0.250; | ||||
|  | ||||
| @@ -25,27 +25,23 @@ const WindowDimmer = new Lang.Class({ | ||||
|     Name: 'WindowDimmer', | ||||
|  | ||||
|     _init: function(actor) { | ||||
|         this._desaturateEffect = new Clutter.DesaturateEffect(); | ||||
|         this._brightnessEffect = new Clutter.BrightnessContrastEffect(); | ||||
|         actor.add_effect(this._desaturateEffect); | ||||
|         actor.add_effect(this._brightnessEffect); | ||||
|         this.actor = actor; | ||||
|         this._enabled = true; | ||||
|         this._dimFactor = 0.0; | ||||
|         this._syncEnabled(); | ||||
|     }, | ||||
|  | ||||
|     _syncEnabled: function() { | ||||
|         this._brightnessEffect.enabled = (this._enabled && this._dimFactor > 0); | ||||
|     }, | ||||
|  | ||||
|     setEnabled: function(enabled) { | ||||
|         this._enabled = enabled; | ||||
|         this._syncEnabled(); | ||||
|         this._desaturateEffect.enabled = enabled; | ||||
|         this._brightnessEffect.enabled = enabled; | ||||
|     }, | ||||
|  | ||||
|     set dimFactor(factor) { | ||||
|         this._dimFactor = factor; | ||||
|         this._desaturateEffect.set_factor(factor * DIM_DESATURATION); | ||||
|         this._brightnessEffect.set_brightness(factor * DIM_BRIGHTNESS); | ||||
|         this._syncEnabled(); | ||||
|     }, | ||||
|  | ||||
|     get dimFactor() { | ||||
| @@ -54,17 +50,10 @@ const WindowDimmer = new Lang.Class({ | ||||
| }); | ||||
|  | ||||
| function getWindowDimmer(actor) { | ||||
|     let enabled = Meta.prefs_get_attach_modal_dialogs(); | ||||
|     if (actor._windowDimmer) | ||||
|         actor._windowDimmer.setEnabled(enabled); | ||||
|     if (!actor._windowDimmer) | ||||
|         actor._windowDimmer = new WindowDimmer(actor); | ||||
|  | ||||
|     if (enabled) { | ||||
|         if (!actor._windowDimmer) | ||||
|             actor._windowDimmer = new WindowDimmer(actor); | ||||
|         return actor._windowDimmer; | ||||
|     } else { | ||||
|         return null; | ||||
|     } | ||||
|     return actor._windowDimmer; | ||||
| } | ||||
|  | ||||
| const WindowManager = new Lang.Class({ | ||||
| @@ -102,32 +91,32 @@ const WindowManager = new Lang.Class({ | ||||
|         this._shellwm.connect('destroy', Lang.bind(this, this._destroyWindow)); | ||||
|  | ||||
|         this._workspaceSwitcherPopup = null; | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-switch-to-workspace-left', | ||||
|                                             Util.wrapKeybinding(Lang.bind(this, this._showWorkspaceSwitcher), true)); | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-switch-to-workspace-right', | ||||
|                                             Util.wrapKeybinding(Lang.bind(this, this._showWorkspaceSwitcher), true)); | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-switch-to-workspace-up', | ||||
|                                             Util.wrapKeybinding(Lang.bind(this, this._showWorkspaceSwitcher), true)); | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-switch-to-workspace-down', | ||||
|                                             Util.wrapKeybinding(Lang.bind(this, this._showWorkspaceSwitcher), true)); | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-move-to-workspace-left', | ||||
|         Meta.keybindings_set_custom_handler('switch-to-workspace-left', | ||||
|                                             Lang.bind(this, this._showWorkspaceSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-move-to-workspace-right', | ||||
|         Meta.keybindings_set_custom_handler('switch-to-workspace-right', | ||||
|                                             Lang.bind(this, this._showWorkspaceSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-move-to-workspace-up', | ||||
|         Meta.keybindings_set_custom_handler('switch-to-workspace-up', | ||||
|                                             Lang.bind(this, this._showWorkspaceSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-move-to-workspace-down', | ||||
|         Meta.keybindings_set_custom_handler('switch-to-workspace-down', | ||||
|                                             Lang.bind(this, this._showWorkspaceSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-switch-windows', | ||||
|         Meta.keybindings_set_custom_handler('move-to-workspace-left', | ||||
|                                             Lang.bind(this, this._showWorkspaceSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('move-to-workspace-right', | ||||
|                                             Lang.bind(this, this._showWorkspaceSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('move-to-workspace-up', | ||||
|                                             Lang.bind(this, this._showWorkspaceSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('move-to-workspace-down', | ||||
|                                             Lang.bind(this, this._showWorkspaceSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('switch-windows', | ||||
|                                             Lang.bind(this, this._startAppSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-switch-group', | ||||
|         Meta.keybindings_set_custom_handler('switch-group', | ||||
|                                             Lang.bind(this, this._startAppSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-switch-windows-backward', | ||||
|         Meta.keybindings_set_custom_handler('switch-windows-backward', | ||||
|                                             Lang.bind(this, this._startAppSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-switch-group-backward', | ||||
|         Meta.keybindings_set_custom_handler('switch-group-backward', | ||||
|                                             Lang.bind(this, this._startAppSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-switch-panels', | ||||
|                                             Util.wrapKeybinding(Lang.bind(this, this._startA11ySwitcher), true)); | ||||
|         Meta.keybindings_set_custom_handler('switch-panels', | ||||
|                                             Lang.bind(this, this._startA11ySwitcher)); | ||||
|         global.display.add_keybinding('open-application-menu', | ||||
|                                       new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }), | ||||
|                                       Meta.KeyBindingFlags.NONE, | ||||
| @@ -271,7 +260,9 @@ const WindowManager = new Lang.Class({ | ||||
|         if (!actor) | ||||
|             return; | ||||
|         let dimmer = getWindowDimmer(actor); | ||||
|         if (!dimmer) | ||||
|         let enabled = Meta.prefs_get_attach_modal_dialogs(); | ||||
|         dimmer.setEnabled(enabled); | ||||
|         if (!enabled) | ||||
|             return; | ||||
|         Tweener.addTween(dimmer, | ||||
|                          { dimFactor: 1.0, | ||||
| @@ -285,12 +276,15 @@ const WindowManager = new Lang.Class({ | ||||
|         if (!actor) | ||||
|             return; | ||||
|         let dimmer = getWindowDimmer(actor); | ||||
|         if (!dimmer) | ||||
|         let enabled = Meta.prefs_get_attach_modal_dialogs(); | ||||
|         dimmer.setEnabled(enabled); | ||||
|         if (!enabled) | ||||
|             return; | ||||
|         Tweener.addTween(dimmer, | ||||
|                          { dimFactor: 0.0, | ||||
|                            time: UNDIM_TIME, | ||||
|                            transition: 'linear' }); | ||||
|                            transition: 'linear' | ||||
|                          }); | ||||
|     }, | ||||
|  | ||||
|     _mapWindow : function(shellwm, actor) { | ||||
| @@ -310,29 +304,12 @@ const WindowManager = new Lang.Class({ | ||||
|         })); | ||||
|         if (actor.meta_window.is_attached_dialog()) { | ||||
|             this._checkDimming(actor.get_meta_window().get_transient_for()); | ||||
|             if (this._shouldAnimate()) { | ||||
|                 actor.set_scale(1.0, 0.0); | ||||
|                 actor.scale_gravity = Clutter.Gravity.CENTER; | ||||
|                 actor.show(); | ||||
|                 this._mapping.push(actor); | ||||
|  | ||||
|                 Tweener.addTween(actor, | ||||
|                                  { scale_y: 1, | ||||
|                                    time: WINDOW_ANIMATION_TIME, | ||||
|                                    transition: "easeOutQuad", | ||||
|                                    onComplete: this._mapWindowDone, | ||||
|                                    onCompleteScope: this, | ||||
|                                    onCompleteParams: [shellwm, actor], | ||||
|                                    onOverwrite: this._mapWindowOverwrite, | ||||
|                                    onOverwriteScope: this, | ||||
|                                    onOverwriteParams: [shellwm, actor] | ||||
|                                  }); | ||||
|             if (!this._shouldAnimate()) { | ||||
|                 shellwm.completed_map(actor); | ||||
|                 return; | ||||
|             } | ||||
|             shellwm.completed_map(actor); | ||||
|             return; | ||||
|         } | ||||
|         if (!this._shouldAnimateActor(actor)) { | ||||
|         } else if (!this._shouldAnimateActor(actor)) { | ||||
|             shellwm.completed_map(actor); | ||||
|             return; | ||||
|         } | ||||
| @@ -388,8 +365,7 @@ const WindowManager = new Lang.Class({ | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             actor.set_scale(1.0, 1.0); | ||||
|             actor.scale_gravity = Clutter.Gravity.CENTER; | ||||
|             actor.opacity = 255; | ||||
|             actor.show(); | ||||
|             this._destroying.push(actor); | ||||
|  | ||||
| @@ -399,7 +375,7 @@ const WindowManager = new Lang.Class({ | ||||
|             })); | ||||
|  | ||||
|             Tweener.addTween(actor, | ||||
|                              { scale_y: 0, | ||||
|                              { opacity: 0, | ||||
|                                time: WINDOW_ANIMATION_TIME, | ||||
|                                transition: "easeOutQuad", | ||||
|                                onComplete: this._destroyWindowDone, | ||||
| @@ -553,32 +529,30 @@ const WindowManager = new Lang.Class({ | ||||
|         let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK; | ||||
|         if (!tabPopup.show(backwards, binding.get_name(), binding.get_mask())) | ||||
|             tabPopup.destroy(); | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _startA11ySwitcher : function(display, screen, window, binding) { | ||||
|         let modifiers = binding.get_modifiers(); | ||||
|         let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK; | ||||
|         Main.ctrlAltTabManager.popup(backwards, binding.get_mask()); | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _openAppMenu : function(display, screen, window, event, binding) { | ||||
|         Main.panel.openAppMenu(); | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _showWorkspaceSwitcher : function(display, screen, window, binding) { | ||||
|         if (screen.n_workspaces == 1) | ||||
|             return false; | ||||
|             return; | ||||
|  | ||||
|         let [,,action,,,direction] = binding.get_name().split('-'); | ||||
|         let [action,,,direction] = binding.get_name().split('-'); | ||||
|         let direction = Meta.MotionDirection[direction.toUpperCase()]; | ||||
|         let newWs; | ||||
|  | ||||
|  | ||||
|         if (direction != Meta.MotionDirection.UP && | ||||
|             direction != Meta.MotionDirection.DOWN) | ||||
|             return false; | ||||
|             return; | ||||
|  | ||||
|         if (action == 'switch') | ||||
|             newWs = this.actionMoveWorkspace(direction); | ||||
| @@ -594,8 +568,6 @@ const WindowManager = new Lang.Class({ | ||||
|             } | ||||
|             this._workspaceSwitcherPopup.display(direction, newWs.index()); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     actionMoveWorkspace: function(direction) { | ||||
|   | ||||
| @@ -33,13 +33,21 @@ const DRAGGING_WINDOW_OPACITY = 100; | ||||
| const BUTTON_LAYOUT_SCHEMA = 'org.gnome.shell.overrides'; | ||||
| const BUTTON_LAYOUT_KEY = 'button-layout'; | ||||
|  | ||||
| // When calculating a layout, we calculate the scale of windows and the percent | ||||
| // of the available area the new layout uses. If the values for the new layout, | ||||
| // when weighted with the values as below, are worse than the previous layout's, | ||||
| // we stop looking for a new layout and use the previous layout. | ||||
| // Otherwise, we keep looking for a new layout. | ||||
| const LAYOUT_SCALE_WEIGHT = 1; | ||||
| const LAYOUT_SPACE_WEIGHT = 0.1; | ||||
| // Define a layout scheme for small window counts. For larger | ||||
| // counts we fall back to an algorithm. We need more schemes here | ||||
| // unless we have a really good algorithm. | ||||
|  | ||||
| // Each triplet is [xCenter, yCenter, scale] where the scale | ||||
| // is relative to the width of the workspace. | ||||
| const POSITIONS = { | ||||
|         1: [[0.5, 0.5, 0.95]], | ||||
|         2: [[0.25, 0.5, 0.48], [0.75, 0.5, 0.48]], | ||||
|         3: [[0.25, 0.25, 0.48],  [0.75, 0.25, 0.48],  [0.5, 0.75, 0.48]], | ||||
|         4: [[0.25, 0.25, 0.47],   [0.75, 0.25, 0.47], [0.75, 0.75, 0.47], [0.25, 0.75, 0.47]], | ||||
|         5: [[0.165, 0.25, 0.32], [0.495, 0.25, 0.32], [0.825, 0.25, 0.32], [0.25, 0.75, 0.32], [0.75, 0.75, 0.32]] | ||||
| }; | ||||
| // Used in _orderWindowsPermutations, 5! = 120 which is probably the highest we can go | ||||
| const POSITIONING_PERMUTATIONS_MAX = 5; | ||||
|  | ||||
| function _interpolate(start, end, step) { | ||||
|     return start + (end - start) * step; | ||||
| @@ -159,24 +167,6 @@ const WindowClone = new Lang.Class({ | ||||
|         this._selected = false; | ||||
|     }, | ||||
|  | ||||
|     get slot() { | ||||
|         let x, y, w, h; | ||||
|  | ||||
|         if (this.inDrag) { | ||||
|             x = this.dragOrigX; | ||||
|             y = this.dragOrigY; | ||||
|             w = this.actor.width * this.dragOrigScale; | ||||
|             h = this.actor.height * this.dragOrigScale; | ||||
|         } else { | ||||
|             x = this.actor.x; | ||||
|             y = this.actor.y; | ||||
|             w = this.actor.width * this.actor.scale_x; | ||||
|             h = this.actor.height * this.actor.scale_y; | ||||
|         } | ||||
|  | ||||
|         return [x, y, w, h]; | ||||
|     }, | ||||
|  | ||||
|     setStackAbove: function (actor) { | ||||
|         this._stackAbove = actor; | ||||
|         if (this.inDrag || this._zooming) | ||||
| @@ -453,11 +443,6 @@ const WindowOverlay = new Lang.Class({ | ||||
|         this._updateCaptionId = metaWindow.connect('notify::title', | ||||
|             Lang.bind(this, function(w) { | ||||
|                 this.title.text = w.title; | ||||
|                 // we need this for the next call to get_preferred_width | ||||
|                 // to return useful results | ||||
|                 this.title.set_size(-1, -1); | ||||
|  | ||||
|                 this._repositionSelf(); | ||||
|             })); | ||||
|  | ||||
|         let button = new St.Button({ style_class: 'window-close' }); | ||||
| @@ -514,19 +499,18 @@ const WindowOverlay = new Lang.Class({ | ||||
|         this.title.opacity = 0; | ||||
|         this._parentActor.raise_top(); | ||||
|         Tweener.addTween(this.title, | ||||
|                          { opacity: 255, | ||||
|                            time: CLOSE_BUTTON_FADE_TIME, | ||||
|                            transition: 'easeOutQuad' }); | ||||
|                         { opacity: 255, | ||||
|                           time: CLOSE_BUTTON_FADE_TIME, | ||||
|                           transition: 'easeOutQuad' }); | ||||
|     }, | ||||
|  | ||||
|     chromeWidth: function () { | ||||
|         return this.closeButton.width - this.closeButton._overlap; | ||||
|     }, | ||||
|  | ||||
|     chromeHeights: function () { | ||||
|         return [this.closeButton.height - this.closeButton._overlap, | ||||
|                 this.title.height + this.title._spacing]; | ||||
|     }, | ||||
|  | ||||
|     _repositionSelf: function() { | ||||
|         let [cloneX, cloneY, cloneWidth, cloneHeight] = this._windowClone.slot; | ||||
|         this.updatePositions(cloneX, cloneY, cloneWidth, cloneHeight, false); | ||||
|                this.title.height + this.title._spacing]; | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
| @@ -566,8 +550,9 @@ const WindowOverlay = new Lang.Class({ | ||||
|         else | ||||
|             button.set_position(Math.floor(buttonX), Math.floor(buttonY)); | ||||
|  | ||||
|         let [titleMinWidth, titleNatWidth] = title.get_preferred_width(-1); | ||||
|         let titleWidth = Math.max(titleMinWidth, Math.min(titleNatWidth, cloneWidth)); | ||||
|         if (!title.fullWidth) | ||||
|             title.fullWidth = title.width; | ||||
|         let titleWidth = Math.min(title.fullWidth, cloneWidth); | ||||
|  | ||||
|         let titleX = cloneX + (cloneWidth - titleWidth) / 2; | ||||
|         let titleY = cloneY + cloneHeight + title._spacing; | ||||
| @@ -683,280 +668,6 @@ const WindowPositionFlags = { | ||||
|     ANIMATE: 1 << 1 | ||||
| }; | ||||
|  | ||||
| const LayoutStrategy = new Lang.Class({ | ||||
|     Name: 'LayoutStrategy', | ||||
|     Abstract: true, | ||||
|  | ||||
|     _init: function(monitor, rowSpacing, columnSpacing, bottomPadding) { | ||||
|         this._monitor = monitor; | ||||
|         this._rowSpacing = rowSpacing; | ||||
|         this._columnSpacing = columnSpacing; | ||||
|         this._bottomPadding = bottomPadding; | ||||
|     }, | ||||
|  | ||||
|     _newRow: function() { | ||||
|         // Row properties: | ||||
|         // | ||||
|         // * x, y are the position of row, relative to area | ||||
|         // | ||||
|         // * width, height are the scaled versions of fullWidth, fullHeight | ||||
|         // | ||||
|         // * width also has the spacing in between windows. It's not in | ||||
|         //   fullWidth, as the spacing is constant, whereas fullWidth is | ||||
|         //   meant to be scaled | ||||
|         // | ||||
|         // * neither height/fullHeight have any sort of spacing or padding | ||||
|         // | ||||
|         // * if cellWidth is present, all windows in the row will occupy | ||||
|         //   the space of cellWidth, centered. | ||||
|         return { x: 0, y: 0, | ||||
|                  width: 0, height: 0, | ||||
|                  fullWidth: 0, fullHeight: 0, | ||||
|                  cellWidth: 0, | ||||
|                  windows: [] }; | ||||
|     }, | ||||
|  | ||||
|     // Compute the size and fancy scale for @window using the | ||||
|     // base scale, @scale. | ||||
|     // | ||||
|     // Returns a list structure: [ scaledWidth, scaledHeight, fancyScale ] | ||||
|     // where scaledWidth and scaledHeight are the window's | ||||
|     // width and height, scaled by fancyScale for convenience. | ||||
|     _computeWindowSizeAndScale: function(window, scale) { | ||||
|         let width = window.actor.width; | ||||
|         let height = window.actor.height; | ||||
|         let ratio; | ||||
|  | ||||
|         if (width > height) | ||||
|             ratio = width / this._monitor.width; | ||||
|         else | ||||
|             ratio = height / this._monitor.height; | ||||
|  | ||||
|         let fancyScale = (2 / (1 + ratio)) * scale; | ||||
|         return [width * fancyScale, height * fancyScale, fancyScale]; | ||||
|     }, | ||||
|  | ||||
|     // Compute the size of each row, by assigning to the properties | ||||
|     // row.width, row.height, row.fullWidth, row.fullHeight, and | ||||
|     // (optionally) row.cellWidth, for each row in @layout.rows. | ||||
|     // This method is intended to be called by subclasses. | ||||
|     _computeRowSizes: function(layout) { | ||||
|         throw new Error('_computeRowSizes not implemented'); | ||||
|     }, | ||||
|  | ||||
|     // Compute strategy-specific window slots for each window in | ||||
|     // @windows, given the @layout. The strategy may also use @layout | ||||
|     // as strategy-specific storage. | ||||
|     // | ||||
|     // This must calculate: | ||||
|     //  * maxColumns - The maximum number of columns used by the layout. | ||||
|     //  * gridWidth - The total width used by the grid, unscaled, unspaced. | ||||
|     //  * gridHeight - The totial height used by the grid, unscaled, unspaced. | ||||
|     //  * rows - A list of rows, which should be instantiated by _newRow. | ||||
|     computeLayout: function(windows, layout) { | ||||
|         throw new Error('computeLayout not implemented'); | ||||
|     }, | ||||
|  | ||||
|     // Given @layout, compute the overall scale and space of the layout. | ||||
|     // The scale is the individual, non-fancy scale of each window, and | ||||
|     // the space is the percentage of the available area eventually | ||||
|     // used by the layout. | ||||
|  | ||||
|     // This method does not return anything, but instead installs | ||||
|     // the properties "scale" and "space" on @layout directly. | ||||
|     // | ||||
|     // Make sure to call this methods before calling computeWindowSlots(), | ||||
|     // as it depends on the scale property installed in @layout here. | ||||
|     computeScaleAndSpace: function(layout) { | ||||
|         let area = layout.area; | ||||
|  | ||||
|         let hspacing = (layout.maxColumns - 1) * this._columnSpacing; | ||||
|         let vspacing = (layout.numRows - 1) * this._rowSpacing + this._bottomPadding; | ||||
|  | ||||
|         let spacedWidth = area.width - hspacing; | ||||
|         let spacedHeight = area.height - vspacing; | ||||
|  | ||||
|         let horizontalScale = spacedWidth / layout.gridWidth; | ||||
|         let verticalScale = spacedHeight / layout.gridHeight; | ||||
|  | ||||
|         // Thumbnails should be less than 70% of the original size | ||||
|         let scale = Math.min(horizontalScale, verticalScale, WINDOW_CLONE_MAXIMUM_SCALE); | ||||
|  | ||||
|         let scaledLayoutWidth = layout.gridWidth * scale + hspacing; | ||||
|         let scaledLayoutHeight = layout.gridHeight * scale + vspacing; | ||||
|         let space = (scaledLayoutWidth * scaledLayoutHeight) / (area.width * area.height); | ||||
|  | ||||
|         layout.scale = scale; | ||||
|         layout.space = space; | ||||
|     }, | ||||
|  | ||||
|     computeWindowSlots: function(layout, area) { | ||||
|         this._computeRowSizes(layout); | ||||
|  | ||||
|         let { rows: rows, scale: scale, state: state } = layout; | ||||
|  | ||||
|         let slots = []; | ||||
|  | ||||
|         let y = 0; | ||||
|         for (let i = 0; i < rows.length; i++) { | ||||
|             let row = rows[i]; | ||||
|             row.x = area.x + (area.width - row.width) / 2; | ||||
|             row.y = area.y + y; | ||||
|             y += row.height + this._rowSpacing; | ||||
|         } | ||||
|  | ||||
|         let height = y - this._rowSpacing + this._bottomPadding; | ||||
|         let baseY = (area.height - height) / 2; | ||||
|  | ||||
|         for (let i = 0; i < rows.length; i++) { | ||||
|             let row = rows[i]; | ||||
|             row.y += baseY; | ||||
|             let baseX = row.x; | ||||
|             for (let j = 0; j < row.windows.length; j++) { | ||||
|                 let window = row.windows[j]; | ||||
|  | ||||
|                 let [width, height, s] = this._computeWindowSizeAndScale(window, scale); | ||||
|                 let y = row.y + row.height - height; | ||||
|  | ||||
|                 let x = baseX; | ||||
|                 if (row.cellWidth) { | ||||
|                     x += (row.cellWidth - width) / 2; | ||||
|                     width = row.cellWidth; | ||||
|                 } | ||||
|  | ||||
|                 slots.push([x, y, s]); | ||||
|                 baseX += width + this._columnSpacing; | ||||
|             } | ||||
|         } | ||||
|         return slots; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const UnalignedLayoutStrategy = new Lang.Class({ | ||||
|     Name: 'UnalignedLayoutStrategy', | ||||
|     Extends: LayoutStrategy, | ||||
|  | ||||
|     _computeRowSizes: function(layout) { | ||||
|         let { rows: rows, scale: scale } = layout; | ||||
|         for (let i = 0; i < rows.length; i++) { | ||||
|             let row = rows[i]; | ||||
|             row.width = row.fullWidth * scale + (row.windows.length - 1) * this._columnSpacing; | ||||
|             row.height = row.fullHeight * scale; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _keepSameRow: function(row, window, width, idealRowWidth) { | ||||
|         if (row.fullWidth + width <= idealRowWidth) | ||||
|             return true; | ||||
|  | ||||
|         let oldRatio = row.fullWidth / idealRowWidth; | ||||
|         let newRatio = (row.fullWidth + width) / idealRowWidth; | ||||
|  | ||||
|         if (Math.abs(1 - newRatio) < Math.abs(1 - oldRatio)) | ||||
|             return true; | ||||
|  | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     computeLayout: function(windows, layout) { | ||||
|         let numRows = layout.numRows; | ||||
|  | ||||
|         let rows = []; | ||||
|         let totalWidth = 0; | ||||
|         for (let i = 0; i < windows.length; i++) { | ||||
|             totalWidth += windows[i].actor.width; | ||||
|         } | ||||
|  | ||||
|         let idealRowWidth = totalWidth / numRows; | ||||
|         let windowIdx = 0; | ||||
|         for (let i = 0; i < numRows; i++) { | ||||
|             let col = 0; | ||||
|             let row = this._newRow(); | ||||
|             rows.push(row); | ||||
|  | ||||
|             for (; windowIdx < windows.length; windowIdx++) { | ||||
|                 let window = windows[windowIdx]; | ||||
|                 let [width, height] = this._computeWindowSizeAndScale(window, 1); | ||||
|                 row.fullHeight = Math.max(row.fullHeight, height); | ||||
|  | ||||
|                 // either new width is < idealWidth or new width is nearer from idealWidth then oldWidth | ||||
|                 if (this._keepSameRow(row, window, width, idealRowWidth) || (i == numRows - 1)) { | ||||
|                     row.windows.push(window); | ||||
|                     row.fullWidth += width; | ||||
|                 } else { | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         let gridHeight = 0; | ||||
|         let maxRow; | ||||
|         for (let i = 0; i < numRows; i++) { | ||||
|             let row = rows[i]; | ||||
|             if (!maxRow || row.fullWidth > maxRow.fullWidth) | ||||
|                 maxRow = row; | ||||
|             gridHeight += row.fullHeight; | ||||
|         } | ||||
|  | ||||
|         layout.rows = rows; | ||||
|         layout.maxColumns = maxRow.windows.length; | ||||
|         layout.gridWidth = maxRow.fullWidth; | ||||
|         layout.gridHeight = gridHeight; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const GridLayoutStrategy = new Lang.Class({ | ||||
|     Name: 'GridLayoutStrategy', | ||||
|     Extends: LayoutStrategy, | ||||
|  | ||||
|     _computeRowSizes: function(layout) { | ||||
|         let { rows: rows, scale: scale } = layout; | ||||
|  | ||||
|         let gridWidth = layout.numColumns * layout.maxWindowWidth; | ||||
|         let hspacing = (layout.numColumns - 1) * this._columnSpacing; | ||||
|         for (let i = 0; i < rows.length; i++) { | ||||
|             let row = rows[i]; | ||||
|             row.fullWidth = layout.gridWidth; | ||||
|             row.fullHeight = layout.maxWindowHeight; | ||||
|  | ||||
|             row.width = row.fullWidth * scale + hspacing; | ||||
|             row.height = row.fullHeight * scale; | ||||
|             row.cellWidth = layout.maxWindowWidth * scale; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     computeLayout: function(windows, layout) { | ||||
|         let { numRows: numRows, numColumns: numColumns } = layout; | ||||
|         let rows = []; | ||||
|         let windowIdx = 0; | ||||
|  | ||||
|         let maxWindowWidth = 0; | ||||
|         let maxWindowHeight = 0; | ||||
|         for (let i = 0; i < numRows; i++) { | ||||
|             let row = this._newRow(); | ||||
|             rows.push(row); | ||||
|             for (; windowIdx < windows.length; windowIdx++) { | ||||
|                 if (row.windows.length >= numColumns) | ||||
|                     break; | ||||
|  | ||||
|                 let window = windows[windowIdx]; | ||||
|                 row.windows.push(window); | ||||
|  | ||||
|                 let [width, height] = this._computeWindowSizeAndScale(window, 1); | ||||
|                 maxWindowWidth = Math.max(maxWindowWidth, width); | ||||
|                 maxWindowHeight = Math.max(maxWindowHeight, height); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         layout.rows = rows; | ||||
|         layout.maxColumns = numColumns; | ||||
|         layout.gridWidth = numColumns * maxWindowWidth; | ||||
|         layout.gridHeight = numRows * maxWindowHeight; | ||||
|         layout.maxWindowWidth = maxWindowWidth; | ||||
|         layout.maxWindowHeight = maxWindowHeight; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| /** | ||||
|  * @metaWorkspace: a #Meta.Workspace, or null | ||||
|  */ | ||||
| @@ -978,7 +689,7 @@ const Workspace = new Lang.Class({ | ||||
|         // Without this the drop area will be overlapped. | ||||
|         this._windowOverlaysGroup.set_size(0, 0); | ||||
|  | ||||
|         this.actor = new St.Widget({ style_class: 'window-picker' }); | ||||
|         this.actor = new Clutter.Group(); | ||||
|         this.actor.set_size(0, 0); | ||||
|  | ||||
|         this._dropRect = new Clutter.Rectangle({ opacity: 0 }); | ||||
| @@ -1009,17 +720,12 @@ const Workspace = new Lang.Class({ | ||||
|                                                                Lang.bind(this, this._windowRemoved)); | ||||
|         } | ||||
|         this._windowEnteredMonitorId = global.screen.connect('window-entered-monitor', | ||||
|                                                              Lang.bind(this, this._windowEnteredMonitor)); | ||||
|                                                            Lang.bind(this, this._windowEnteredMonitor)); | ||||
|         this._windowLeftMonitorId = global.screen.connect('window-left-monitor', | ||||
|                                                           Lang.bind(this, this._windowLeftMonitor)); | ||||
|                                                            Lang.bind(this, this._windowLeftMonitor)); | ||||
|         this._repositionWindowsId = 0; | ||||
|  | ||||
|         this.leavingOverview = false; | ||||
|  | ||||
|         this._positionWindowsFlags = 0; | ||||
|         this._positionWindowsId = 0; | ||||
|  | ||||
|         this._currentLayout = null; | ||||
|     }, | ||||
|  | ||||
|     setGeometry: function(x, y, width, height) { | ||||
| @@ -1028,13 +734,15 @@ const Workspace = new Lang.Class({ | ||||
|         this._width = width; | ||||
|         this._height = height; | ||||
|  | ||||
|         Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { | ||||
|             this._dropRect.set_position(x, y); | ||||
|             this._dropRect.set_size(width, height); | ||||
|             return false; | ||||
|         })); | ||||
|         // This is sometimes called during allocation, so we do this later | ||||
|         Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, | ||||
|             function () { | ||||
|                 this._dropRect.set_position(x, y); | ||||
|                 this._dropRect.set_size(width, height); | ||||
|                 this.positionWindows(WindowPositionFlags.ANIMATE); | ||||
|                 return false; | ||||
|             })); | ||||
|  | ||||
|         this.positionWindows(WindowPositionFlags.ANIMATE); | ||||
|     }, | ||||
|  | ||||
|     _lookupIndex: function (metaWindow) { | ||||
| @@ -1054,15 +762,238 @@ const Workspace = new Lang.Class({ | ||||
|         return this._windows.length == 0; | ||||
|     }, | ||||
|  | ||||
|     // Only use this for n <= 20 say | ||||
|     _factorial: function(n) { | ||||
|         let result = 1; | ||||
|         for (let i = 2; i <= n; i++) | ||||
|             result *= i; | ||||
|         return result; | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * _permutation: | ||||
|      * @permutationIndex: An integer from [0, list.length!) | ||||
|      * @list: (inout): Array of objects to permute; will be modified in place | ||||
|      * | ||||
|      * Given an integer between 0 and length of array, re-order the array in-place | ||||
|      * into a permutation denoted by the index. | ||||
|      */ | ||||
|     _permutation: function(permutationIndex, list) { | ||||
|         for (let j = 2; j <= list.length; j++) { | ||||
|             let firstIndex = (permutationIndex % j); | ||||
|             let secondIndex = j - 1; | ||||
|             // Swap | ||||
|             let tmp = list[firstIndex]; | ||||
|             list[firstIndex] = list[secondIndex]; | ||||
|             list[secondIndex] = tmp; | ||||
|             permutationIndex = Math.floor(permutationIndex / j); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * _forEachPermutations: | ||||
|      * @list: Array | ||||
|      * @func: Function which takes a single array argument | ||||
|      * | ||||
|      * Call @func with each permutation of @list as an argument. | ||||
|      */ | ||||
|     _forEachPermutations: function(list, func) { | ||||
|         let nCombinations = this._factorial(list.length); | ||||
|         for (let i = 0; i < nCombinations; i++) { | ||||
|             let listCopy = list.concat(); | ||||
|             this._permutation(i, listCopy); | ||||
|             func(listCopy); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * _computeWindowMotion: | ||||
|      * @actor: A #WindowClone's #ClutterActor | ||||
|      * @slot: An element of #POSITIONS | ||||
|      * @slotGeometry: Layout of @slot | ||||
|      * | ||||
|      * Returns a number corresponding to how much perceived motion | ||||
|      * would be involved in moving the window to the given slot. | ||||
|      * Currently this is the square of the distance between the | ||||
|      * centers. | ||||
|      */ | ||||
|     _computeWindowMotion: function (actor, slot) { | ||||
|         let [xCenter, yCenter, fraction] = slot; | ||||
|         let xDelta, yDelta, distanceSquared; | ||||
|         let actorWidth, actorHeight; | ||||
|  | ||||
|         let x = actor.x; | ||||
|         let y = actor.y; | ||||
|         let scale = actor.scale_x; | ||||
|  | ||||
|         if (actor._delegate.inDrag) { | ||||
|             x = actor._delegate.dragOrigX; | ||||
|             y = actor._delegate.dragOrigY; | ||||
|             scale = actor._delegate.dragOrigScale; | ||||
|         } | ||||
|  | ||||
|         actorWidth = actor.width * scale; | ||||
|         actorHeight = actor.height * scale; | ||||
|         xDelta = x + actorWidth / 2.0 - xCenter * this._width - this._x; | ||||
|         yDelta = y + actorHeight / 2.0 - yCenter * this._height - this._y; | ||||
|         distanceSquared = xDelta * xDelta + yDelta * yDelta; | ||||
|  | ||||
|         return distanceSquared; | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * _orderWindowsPermutations: | ||||
|      * | ||||
|      * Iterate over all permutations of the windows, and determine the | ||||
|      * permutation which has the least total motion. | ||||
|      */ | ||||
|     _orderWindowsPermutations: function (clones, slots) { | ||||
|         let minimumMotionPermutation = null; | ||||
|         let minimumMotion = -1; | ||||
|         let permIndex = 0; | ||||
|         this._forEachPermutations(clones, Lang.bind(this, function (permutation) { | ||||
|             let motion = 0; | ||||
|             for (let i = 0; i < permutation.length; i++) { | ||||
|                 let cloneActor = permutation[i].actor; | ||||
|                 let slot = slots[i]; | ||||
|  | ||||
|                 let delta = this._computeWindowMotion(cloneActor, slot); | ||||
|  | ||||
|                 motion += delta; | ||||
|  | ||||
|                 // Bail out early if we're already larger than the | ||||
|                 // previous best | ||||
|                 if (minimumMotionPermutation != null && | ||||
|                     motion > minimumMotion) | ||||
|                     continue; | ||||
|             } | ||||
|  | ||||
|             if (minimumMotionPermutation == null || motion < minimumMotion) { | ||||
|                 minimumMotionPermutation = permutation; | ||||
|                 minimumMotion = motion; | ||||
|             } | ||||
|             permIndex++; | ||||
|         })); | ||||
|         return minimumMotionPermutation; | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * _orderWindowsGreedy: | ||||
|      * | ||||
|      * Iterate over available slots in order, placing into each one the window | ||||
|      * we find with the smallest motion to that slot. | ||||
|      */ | ||||
|     _orderWindowsGreedy: function(clones, slots) { | ||||
|         let result = []; | ||||
|         let slotIndex = 0; | ||||
|         // Copy since we mutate below | ||||
|         let clonesCopy = clones.concat(); | ||||
|         for (let i = 0; i < slots.length; i++) { | ||||
|             let slot = slots[i]; | ||||
|             let minimumMotionIndex = -1; | ||||
|             let minimumMotion = -1; | ||||
|             for (let j = 0; j < clonesCopy.length; j++) { | ||||
|                 let cloneActor = clonesCopy[j].actor; | ||||
|                 let delta = this._computeWindowMotion(cloneActor, slot); | ||||
|                 if (minimumMotionIndex == -1 || delta < minimumMotion) { | ||||
|                     minimumMotionIndex = j; | ||||
|                     minimumMotion = delta; | ||||
|                 } | ||||
|             } | ||||
|             result.push(clonesCopy[minimumMotionIndex]); | ||||
|             clonesCopy.splice(minimumMotionIndex, 1); | ||||
|         } | ||||
|         return result; | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * _orderWindowsByMotionAndStartup: | ||||
|      * @windows: Array of #MetaWindow | ||||
|      * @slots: Array of slots | ||||
|      * | ||||
|      * Returns a copy of @windows, ordered in such a way that they require least motion | ||||
|      * to move to the final screen coordinates of @slots.  Ties are broken in a stable | ||||
|      * fashion by the order in which the windows were created. | ||||
|      */ | ||||
|     _orderWindowsByMotionAndStartup: function(clones, slots) { | ||||
|         clones.sort(function(w1, w2) { | ||||
|             return w2.metaWindow.get_stable_sequence() - w1.metaWindow.get_stable_sequence(); | ||||
|         }); | ||||
|         if (clones.length <= POSITIONING_PERMUTATIONS_MAX) | ||||
|             return this._orderWindowsPermutations(clones, slots); | ||||
|         else | ||||
|             return this._orderWindowsGreedy(clones, slots); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * _getSlotGeometry: | ||||
|      * @slot: A layout slot | ||||
|      * | ||||
|      * Returns: the screen-relative [x, y, width, height] | ||||
|      * of a given window layout slot. | ||||
|      */ | ||||
|     _getSlotGeometry: function(slot) { | ||||
|         let [xCenter, yCenter, fraction] = slot; | ||||
|  | ||||
|         let width = this._width * fraction; | ||||
|         let height = this._height * fraction; | ||||
|  | ||||
|         let x = this._x + xCenter * this._width - width / 2 ; | ||||
|         let y = this._y + yCenter * this._height - height / 2; | ||||
|  | ||||
|         return [x, y, width, height]; | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * _computeWindowLayout: | ||||
|      * @metaWindow: A #MetaWindow | ||||
|      * @slot: A layout slot | ||||
|      * | ||||
|      * Given a window and slot to fit it in, compute its | ||||
|      * screen-relative [x, y, scale] where scale applies | ||||
|      * to both X and Y directions. | ||||
|      */ | ||||
|     _computeWindowLayout: function(metaWindow, slot) { | ||||
|         let [x, y, width, height] = this._getSlotGeometry(slot); | ||||
|  | ||||
|         let rect = metaWindow.get_outer_rect(); | ||||
|         let buttonOuterHeight, captionHeight; | ||||
|         let buttonOuterWidth = 0; | ||||
|  | ||||
|         if (this._windowOverlays[0]) { | ||||
|             [buttonOuterHeight, captionHeight] = this._windowOverlays[0].chromeHeights(); | ||||
|             buttonOuterWidth = this._windowOverlays[0].chromeWidth(); | ||||
|         } else | ||||
|             [buttonOuterHeight, captionHeight] = [0, 0]; | ||||
|  | ||||
|         let scale = Math.min((width - buttonOuterWidth) / rect.width, | ||||
|                              (height - buttonOuterHeight - captionHeight) / rect.height, | ||||
|                              WINDOW_CLONE_MAXIMUM_SCALE); | ||||
|  | ||||
|         x = Math.floor(x + (width - scale * rect.width) / 2); | ||||
|  | ||||
|         // We want to center the window in case we have just one | ||||
|         if (metaWindow.get_workspace().n_windows == 1) | ||||
|             y = Math.floor(y + (height - scale * rect.height) / 2); | ||||
|         else | ||||
|             y = Math.floor(y + height - rect.height * scale - captionHeight); | ||||
|  | ||||
|         return [x, y, scale]; | ||||
|     }, | ||||
|  | ||||
|     setReservedSlot: function(clone) { | ||||
|         if (this._reservedSlot == clone) | ||||
|             return; | ||||
|  | ||||
|         if (clone && this.containsMetaWindow(clone.metaWindow)) | ||||
|             clone = null; | ||||
|  | ||||
|         this._reservedSlot = clone; | ||||
|         this._currentLayout = null; | ||||
|         if (clone && this.containsMetaWindow(clone.metaWindow)) { | ||||
|             this._reservedSlot = null; | ||||
|             this.positionWindows(WindowPositionFlags.ANIMATE); | ||||
|             return; | ||||
|         } | ||||
|         if (clone) | ||||
|             this._reservedSlot = clone; | ||||
|         else | ||||
|             this._reservedSlot = null; | ||||
|         this.positionWindows(WindowPositionFlags.ANIMATE); | ||||
|     }, | ||||
|  | ||||
| @@ -1072,32 +1003,13 @@ const Workspace = new Lang.Class({ | ||||
|      *  INITIAL - this is the initial positioning of the windows. | ||||
|      *  ANIMATE - Indicates that we need animate changing position. | ||||
|      */ | ||||
|     positionWindows: function(flags) { | ||||
|         this._positionWindowsFlags |= flags; | ||||
|  | ||||
|         if (this._positionWindowsId > 0) | ||||
|             return; | ||||
|  | ||||
|         this._positionWindowsId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { | ||||
|             this._realPositionWindows(this._positionWindowsFlags); | ||||
|             this._positionWindowsFlags = 0; | ||||
|             this._positionWindowsId = 0; | ||||
|             return false; | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     _realPositionWindows : function(flags) { | ||||
|     positionWindows : function(flags) { | ||||
|         if (this._repositionWindowsId > 0) { | ||||
|             Mainloop.source_remove(this._repositionWindowsId); | ||||
|             this._repositionWindowsId = 0; | ||||
|         } | ||||
|  | ||||
|         let clones = this._windows.slice(); | ||||
|  | ||||
|         clones.sort(function(a, b) { | ||||
|             return a.metaWindow.get_stable_sequence() - b.metaWindow.get_stable_sequence(); | ||||
|         }); | ||||
|  | ||||
|         if (this._reservedSlot) | ||||
|             clones.push(this._reservedSlot); | ||||
|  | ||||
| @@ -1105,7 +1017,8 @@ const Workspace = new Lang.Class({ | ||||
|         let animate = flags & WindowPositionFlags.ANIMATE; | ||||
|  | ||||
|         // Start the animations | ||||
|         let slots = this._computeAllWindowSlots(clones); | ||||
|         let slots = this._computeAllWindowSlots(clones.length); | ||||
|         clones = this._orderWindowsByMotionAndStartup(clones, slots); | ||||
|  | ||||
|         let currentWorkspace = global.screen.get_active_workspace(); | ||||
|         let isOnCurrentWorkspace = this.metaWorkspace == null || this.metaWorkspace == currentWorkspace; | ||||
| @@ -1122,36 +1035,33 @@ const Workspace = new Lang.Class({ | ||||
|             if (clone.inDrag) | ||||
|                 continue; | ||||
|  | ||||
|             let [x, y, scale] = slot; | ||||
|             let [x, y, scale] = this._computeWindowLayout(metaWindow, slot); | ||||
|  | ||||
|             if (overlay && initialPositioning) | ||||
|                 overlay.hide(); | ||||
|  | ||||
|             if (animate && isOnCurrentWorkspace) { | ||||
|                 if (!metaWindow.showing_on_its_workspace()) { | ||||
|                     /* Hidden windows should fade in and grow | ||||
|                      * therefore we need to resize them now so they | ||||
|                      * can be scaled up later */ | ||||
|                     if (initialPositioning) { | ||||
|                         clone.actor.opacity = 0; | ||||
|                         clone.actor.scale_x = 0; | ||||
|                         clone.actor.scale_y = 0; | ||||
|                         clone.actor.x = x; | ||||
|                         clone.actor.y = y; | ||||
|                     } | ||||
|                      if (initialPositioning) { | ||||
|                          clone.actor.opacity = 0; | ||||
|                          clone.actor.scale_x = 0; | ||||
|                          clone.actor.scale_y = 0; | ||||
|                          clone.actor.x = x; | ||||
|                          clone.actor.y = y; | ||||
|                      } | ||||
|  | ||||
|                     // Make the window slightly transparent to indicate it's hidden | ||||
|                     Tweener.addTween(clone.actor, | ||||
|                                      { opacity: 255, | ||||
|                                        time: Overview.ANIMATION_TIME, | ||||
|                                        transition: 'easeInQuad' | ||||
|                                      }); | ||||
|                      // Make the window slightly transparent to indicate it's hidden | ||||
|                      Tweener.addTween(clone.actor, | ||||
|                                       { opacity: 255, | ||||
|                                         time: Overview.ANIMATION_TIME, | ||||
|                                         transition: 'easeInQuad' | ||||
|                                       }); | ||||
|                 } | ||||
|  | ||||
|                 this._animateClone(clone, overlay, x, y, scale, initialPositioning); | ||||
|             } else { | ||||
|                 // cancel any active tweens (otherwise they might override our changes) | ||||
|                 Tweener.removeTweens(clone.actor); | ||||
|                 clone.actor.set_position(x, y); | ||||
|                 clone.actor.set_scale(scale, scale); | ||||
|                 this._updateWindowOverlayPositions(clone, overlay, x, y, scale, false); | ||||
| @@ -1178,26 +1088,26 @@ const Workspace = new Lang.Class({ | ||||
|  | ||||
|     _animateClone: function(clone, overlay, x, y, scale, initialPositioning) { | ||||
|         Tweener.addTween(clone.actor, | ||||
|                          { x: x, | ||||
|                            y: y, | ||||
|                            scale_x: scale, | ||||
|                            scale_y: scale, | ||||
|                            time: Overview.ANIMATION_TIME, | ||||
|                            transition: 'easeOutQuad', | ||||
|                            onComplete: Lang.bind(this, function() { | ||||
|                                this._showWindowOverlay(clone, overlay, true); | ||||
|                            }) | ||||
|                          }); | ||||
|                                  { x: x, | ||||
|                                    y: y, | ||||
|                                    scale_x: scale, | ||||
|                                    scale_y: scale, | ||||
|                                    time: Overview.ANIMATION_TIME, | ||||
|                                    transition: 'easeOutQuad', | ||||
|                                    onComplete: Lang.bind(this, function() { | ||||
|                                          this._showWindowOverlay(clone, overlay, true); | ||||
|                                    }) | ||||
|                                  }); | ||||
|  | ||||
|         this._updateWindowOverlayPositions(clone, overlay, x, y, scale, true); | ||||
|     }, | ||||
|  | ||||
|     _updateWindowOverlayPositions: function(clone, overlay, x, y, scale, animate) { | ||||
|         if (!overlay) | ||||
|             return; | ||||
|  | ||||
|         let [cloneWidth, cloneHeight] = clone.actor.get_size(); | ||||
|         overlay.updatePositions(x, y, cloneWidth * scale, cloneHeight * scale, animate); | ||||
|         cloneWidth = scale * cloneWidth; | ||||
|         cloneHeight = scale * cloneHeight; | ||||
|         if (overlay) | ||||
|             overlay.updatePositions(x, y, cloneWidth, cloneHeight, animate); | ||||
|     }, | ||||
|  | ||||
|     _showWindowOverlay: function(clone, overlay, fade) { | ||||
| @@ -1291,7 +1201,6 @@ const Workspace = new Lang.Class({ | ||||
|         this._cursorX = x; | ||||
|         this._cursorY = y; | ||||
|  | ||||
|         this._currentLayout = null; | ||||
|         this._repositionWindowsId = Mainloop.timeout_add(750, | ||||
|             Lang.bind(this, this._delayedWindowRepositioning)); | ||||
|     }, | ||||
| @@ -1344,7 +1253,6 @@ const Workspace = new Lang.Class({ | ||||
|             clone.actor.set_position (this._x, this._y); | ||||
|         } | ||||
|  | ||||
|         this._currentLayout = null; | ||||
|         this.positionWindows(WindowPositionFlags.ANIMATE); | ||||
|     }, | ||||
|  | ||||
| @@ -1382,8 +1290,6 @@ const Workspace = new Lang.Class({ | ||||
|  | ||||
|     // Animate the full-screen to Overview transition. | ||||
|     zoomToOverview : function() { | ||||
|         this._currentLayout = null; | ||||
|  | ||||
|         // Position and scale the windows. | ||||
|         if (Main.overview.animationInProgress) | ||||
|             this.positionWindows(WindowPositionFlags.ANIMATE | WindowPositionFlags.INITIAL); | ||||
| @@ -1466,9 +1372,6 @@ const Workspace = new Lang.Class({ | ||||
|         if (this._repositionWindowsId > 0) | ||||
|             Mainloop.source_remove(this._repositionWindowsId); | ||||
|  | ||||
|         if (this._positionWindowsId > 0) | ||||
|             Meta.later_remove(this._positionWindowsId); | ||||
|  | ||||
|         // Usually, the windows will be destroyed automatically with | ||||
|         // their parent (this.actor), but we might have a zoomed window | ||||
|         // which has been reparented to the stage - _windows[0] holds | ||||
| @@ -1548,108 +1451,29 @@ const Workspace = new Lang.Class({ | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _isBetterLayout: function(oldLayout, newLayout) { | ||||
|         if (oldLayout.scale === undefined) | ||||
|             return true; | ||||
|     _computeWindowSlot : function(windowIndex, numberOfWindows) { | ||||
|         if (numberOfWindows in POSITIONS) | ||||
|             return POSITIONS[numberOfWindows][windowIndex]; | ||||
|  | ||||
|         let spacePower = (newLayout.space - oldLayout.space) * LAYOUT_SPACE_WEIGHT; | ||||
|         let scalePower = (newLayout.scale - oldLayout.scale) * LAYOUT_SCALE_WEIGHT; | ||||
|         // If we don't have a predefined scheme for this window count, | ||||
|         // arrange the windows in a grid pattern. | ||||
|         let gridWidth = Math.ceil(Math.sqrt(numberOfWindows)); | ||||
|         let gridHeight = Math.ceil(numberOfWindows / gridWidth); | ||||
|  | ||||
|         if (newLayout.scale > oldLayout.scale && newLayout.space > oldLayout.space) { | ||||
|             // Win win -- better scale and better space | ||||
|             return true; | ||||
|         } else if (newLayout.scale > oldLayout.scale && newLayout.space <= oldLayout.space) { | ||||
|             // Keep new layout only if scale gain outweights aspect space loss | ||||
|             return scalePower > spacePower; | ||||
|         } else if (newLayout.scale <= oldLayout.scale && newLayout.space > oldLayout.space) { | ||||
|             // Keep new layout only if aspect space gain outweights scale loss | ||||
|             return spacePower > scalePower; | ||||
|         } else { | ||||
|             // Lose -- worse scale and space | ||||
|             return false; | ||||
|         } | ||||
|         let fraction = 0.95 * (1. / gridWidth); | ||||
|  | ||||
|         let xCenter = (.5 / gridWidth) + ((windowIndex) % gridWidth) / gridWidth; | ||||
|         let yCenter = (.5 / gridHeight) + Math.floor((windowIndex / gridWidth)) / gridHeight; | ||||
|  | ||||
|         return [xCenter, yCenter, fraction]; | ||||
|     }, | ||||
|  | ||||
|     _computeLayout: function(windows, area, rowSpacing, columnSpacing, bottomPadding) { | ||||
|         // We look for the largest scale that allows us to fit the | ||||
|         // largest row/tallest column on the workspace. | ||||
|  | ||||
|         let lastLayout = {}; | ||||
|  | ||||
|         for (let numRows = 1; ; numRows++) { | ||||
|             let numColumns = Math.ceil(windows.length / numRows); | ||||
|  | ||||
|             // If adding a new row does not change column count just stop | ||||
|             // (for instance: 9 windows, with 3 rows -> 3 columns, 4 rows -> | ||||
|             // 3 columns as well => just use 3 rows then) | ||||
|             if (numColumns == lastLayout.numColumns) | ||||
|                 break; | ||||
|  | ||||
|             let strategyClass = numRows > 2 ? GridLayoutStrategy : UnalignedLayoutStrategy; | ||||
|             let strategy = new strategyClass(this._monitor, rowSpacing, columnSpacing, bottomPadding); | ||||
|  | ||||
|             let layout = { area: area, strategy: strategy, numRows: numRows, numColumns: numColumns }; | ||||
|             strategy.computeLayout(windows, layout); | ||||
|             strategy.computeScaleAndSpace(layout); | ||||
|  | ||||
|             if (!this._isBetterLayout(lastLayout, layout)) | ||||
|                 break; | ||||
|  | ||||
|             lastLayout = layout; | ||||
|     _computeAllWindowSlots: function(totalWindows) { | ||||
|         let slots = []; | ||||
|         for (let i = 0; i < totalWindows; i++) { | ||||
|             slots.push(this._computeWindowSlot(i, totalWindows)); | ||||
|         } | ||||
|  | ||||
|         return lastLayout; | ||||
|     }, | ||||
|  | ||||
|     _rectEqual: function(one, two) { | ||||
|         if (one == two) | ||||
|             return true; | ||||
|  | ||||
|         return (one.x == two.x && | ||||
|                 one.y == two.y && | ||||
|                 one.width == two.width && | ||||
|                 one.height == two.height); | ||||
|     }, | ||||
|  | ||||
|     _computeAllWindowSlots: function(windows) { | ||||
|         let totalWindows = windows.length; | ||||
|         let node = this.actor.get_theme_node(); | ||||
|  | ||||
|         // Window grid spacing | ||||
|         let columnSpacing = node.get_length('-horizontal-spacing'); | ||||
|         let rowSpacing = node.get_length('-vertical-spacing'); | ||||
|  | ||||
|         if (!totalWindows) | ||||
|             return []; | ||||
|  | ||||
|         let closeButtonHeight, captionHeight; | ||||
|         if (this._windowOverlays.length) { | ||||
|             // All of the overlays have the same chrome sizes, | ||||
|             // so just pick the first one. | ||||
|             let overlay = this._windowOverlays[0]; | ||||
|             [closeButtonHeight, captionHeight] = overlay.chromeHeights(); | ||||
|         } else { | ||||
|             [closeButtonHeight, captionHeight] = [0, 0]; | ||||
|         } | ||||
|  | ||||
|         rowSpacing += captionHeight; | ||||
|  | ||||
|         let area = { x: this._x, y: this._y, width: this._width, height: this._height }; | ||||
|         area.y += closeButtonHeight; | ||||
|         area.height -= closeButtonHeight; | ||||
|  | ||||
|         if (!this._currentLayout) | ||||
|             this._currentLayout = this._computeLayout(windows, area, rowSpacing, columnSpacing, captionHeight); | ||||
|  | ||||
|         let layout = this._currentLayout; | ||||
|         let strategy = layout.strategy; | ||||
|  | ||||
|         if (!this._rectEqual(area, layout.area)) { | ||||
|             layout.area = area; | ||||
|             strategy.computeScaleAndSpace(layout); | ||||
|         } | ||||
|  | ||||
|         return strategy.computeWindowSlots(layout, area); | ||||
|         return slots; | ||||
|     }, | ||||
|  | ||||
|     _onCloneSelected : function (clone, time) { | ||||
|   | ||||
| @@ -620,7 +620,9 @@ const ThumbnailsBox = new Lang.Class({ | ||||
|         if (!source.realWindow && !source.shellWorkspaceLaunch && source != Main.xdndHandler) | ||||
|             return DND.DragMotionResult.CONTINUE; | ||||
|  | ||||
|         let canCreateWorkspaces = Meta.prefs_get_dynamic_workspaces(); | ||||
|         if (!Meta.prefs_get_dynamic_workspaces()) | ||||
|             return DND.DragMotionResult.CONTINUE; | ||||
|  | ||||
|         let spacing = this.actor.get_theme_node().get_length('spacing'); | ||||
|  | ||||
|         this._dropWorkspace = -1; | ||||
| @@ -645,7 +647,7 @@ const ThumbnailsBox = new Lang.Class({ | ||||
|             if (i == this._dropPlaceholderPos) | ||||
|                 targetBottom += this._dropPlaceholder.get_height(); | ||||
|  | ||||
|             if (y > targetTop && y <= targetBottom && source != Main.xdndHandler && canCreateWorkspaces) { | ||||
|             if (y > targetTop && y <= targetBottom && source != Main.xdndHandler) { | ||||
|                 placeholderPos = i; | ||||
|                 break; | ||||
|             } else if (y > targetBottom && y <= nextTargetTop) { | ||||
| @@ -783,7 +785,7 @@ const ThumbnailsBox = new Lang.Class({ | ||||
|         this._indicator.raise_top(); | ||||
|     }, | ||||
|  | ||||
|     removeThumbnails: function(start, count) { | ||||
|     removeThumbmails: function(start, count) { | ||||
|         let currentPos = 0; | ||||
|         for (let k = 0; k < this._thumbnails.length; k++) { | ||||
|             let thumbnail = this._thumbnails[k]; | ||||
| @@ -1043,16 +1045,9 @@ const ThumbnailsBox = new Lang.Class({ | ||||
|         childBox.y2 = box.y2; | ||||
|         this._background.allocate(childBox, flags); | ||||
|  | ||||
|         let indicatorY1 = this._indicatorY; | ||||
|         let indicatorY2; | ||||
|         let indicatorY = this._indicatorY; | ||||
|         // when not animating, the workspace position overrides this._indicatorY | ||||
|         let indicatorWorkspace = !this._animatingIndicator ? global.screen.get_active_workspace() : null; | ||||
|         let indicatorThemeNode = this._indicator.get_theme_node(); | ||||
|  | ||||
|         let indicatorTopFullBorder = indicatorThemeNode.get_padding(St.Side.TOP) + indicatorThemeNode.get_border_width(St.Side.TOP); | ||||
|         let indicatorBottomFullBorder = indicatorThemeNode.get_padding(St.Side.BOTTOM) + indicatorThemeNode.get_border_width(St.Side.BOTTOM); | ||||
|         let indicatorLeftFullBorder = indicatorThemeNode.get_padding(St.Side.LEFT) + indicatorThemeNode.get_border_width(St.Side.LEFT); | ||||
|         let indicatorRightFullBorder = indicatorThemeNode.get_padding(St.Side.RIGHT) + indicatorThemeNode.get_border_width(St.Side.RIGHT); | ||||
|  | ||||
|         let y = contentBox.y1; | ||||
|  | ||||
| @@ -1098,10 +1093,8 @@ const ThumbnailsBox = new Lang.Class({ | ||||
|             let y2 = Math.round(y + thumbnailHeight); | ||||
|             let roundedVScale = (y2 - y1) / portholeHeight; | ||||
|  | ||||
|             if (thumbnail.metaWorkspace == indicatorWorkspace) { | ||||
|                 indicatorY1 = y1; | ||||
|                 indicatorY2 = y2; | ||||
|             } | ||||
|             if (thumbnail.metaWorkspace == indicatorWorkspace) | ||||
|                 indicatorY = y1; | ||||
|  | ||||
|             // Allocating a scaled actor is funny - x1/y1 correspond to the origin | ||||
|             // of the actor, but x2/y2 are increased by the *unscaled* size. | ||||
| @@ -1126,10 +1119,8 @@ const ThumbnailsBox = new Lang.Class({ | ||||
|             childBox.x1 = contentBox.x2 - thumbnailWidth; | ||||
|             childBox.x2 = contentBox.x2; | ||||
|         } | ||||
|         childBox.x1 -= indicatorLeftFullBorder; | ||||
|         childBox.x2 += indicatorRightFullBorder; | ||||
|         childBox.y1 = indicatorY1 - indicatorTopFullBorder; | ||||
|         childBox.y2 = (indicatorY2 ? indicatorY2 : (indicatorY1 + thumbnailHeight)) + indicatorBottomFullBorder; | ||||
|         childBox.y1 = indicatorY; | ||||
|         childBox.y2 = childBox.y1 + thumbnailHeight; | ||||
|         this._indicator.allocate(childBox, flags); | ||||
|     }, | ||||
|  | ||||
| @@ -1144,9 +1135,7 @@ const ThumbnailsBox = new Lang.Class({ | ||||
|         } | ||||
|  | ||||
|         this._animatingIndicator = true; | ||||
|         let indicatorThemeNode = this._indicator.get_theme_node(); | ||||
|         let indicatorTopFullBorder = indicatorThemeNode.get_padding(St.Side.TOP) + indicatorThemeNode.get_border_width(St.Side.TOP); | ||||
|         this.indicatorY = this._indicator.allocation.y1 + indicatorTopFullBorder; | ||||
|         this.indicatorY = this._indicator.allocation.y1; | ||||
|         Tweener.addTween(this, | ||||
|                          { indicatorY: thumbnail.actor.allocation.y1, | ||||
|                            time: WorkspacesView.WORKSPACE_SWITCH_TIME, | ||||
|   | ||||
| @@ -51,6 +51,7 @@ const WorkspacesView = new Lang.Class({ | ||||
|         this._clipY = 0; | ||||
|         this._clipWidth = 0; | ||||
|         this._clipHeight = 0; | ||||
|         this._workspaceRatioSpacing = 0; | ||||
|         this._spacing = 0; | ||||
|         this._animating = false; // tweening | ||||
|         this._scrolling = false; // swipe-scrolling | ||||
| @@ -71,7 +72,6 @@ const WorkspacesView = new Lang.Class({ | ||||
|             this._workspaces[w].actor.reparent(this.actor); | ||||
|         this._workspaces[activeWorkspaceIndex].actor.raise_top(); | ||||
|  | ||||
|         this._extraWorkspaces = []; | ||||
|         this._updateExtraWorkspaces(); | ||||
|  | ||||
|         // Position/scale the desktop windows and their children after the | ||||
| @@ -83,6 +83,8 @@ const WorkspacesView = new Lang.Class({ | ||||
|                                  Lang.bind(this, function() { | ||||
|                 for (let w = 0; w < this._workspaces.length; w++) | ||||
|                     this._workspaces[w].zoomToOverview(); | ||||
|                 if (!this._extraWorkspaces) | ||||
|                     return; | ||||
|                 for (let w = 0; w < this._extraWorkspaces.length; w++) | ||||
|                     this._extraWorkspaces[w].zoomToOverview(); | ||||
|         })); | ||||
| @@ -122,6 +124,7 @@ const WorkspacesView = new Lang.Class({ | ||||
|         if (!this._settings.get_boolean('workspaces-only-on-primary')) | ||||
|             return; | ||||
|  | ||||
|         this._extraWorkspaces = []; | ||||
|         let monitors = Main.layoutManager.monitors; | ||||
|         for (let i = 0; i < monitors.length; i++) { | ||||
|             if (i == Main.layoutManager.primaryIndex) | ||||
| @@ -136,20 +139,23 @@ const WorkspacesView = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _destroyExtraWorkspaces: function() { | ||||
|         if (!this._extraWorkspaces) | ||||
|             return; | ||||
|  | ||||
|         for (let m = 0; m < this._extraWorkspaces.length; m++) | ||||
|             this._extraWorkspaces[m].destroy(); | ||||
|         this._extraWorkspaces = []; | ||||
|         this._extraWorkspaces = null; | ||||
|     }, | ||||
|  | ||||
|     setGeometry: function(x, y, width, height) { | ||||
|     setGeometry: function(x, y, width, height, spacing) { | ||||
|       if (this._x == x && this._y == y && | ||||
|           this._width == width && this._height == height) | ||||
|           return; | ||||
|  | ||||
|         this._width = width; | ||||
|         this._height = height; | ||||
|         this._x = x; | ||||
|         this._y = y; | ||||
|         this._workspaceRatioSpacing = spacing; | ||||
|  | ||||
|         for (let i = 0; i < this._workspaces.length; i++) | ||||
|             this._workspaces[i].setGeometry(x, y, width, height); | ||||
| @@ -185,6 +191,8 @@ const WorkspacesView = new Lang.Class({ | ||||
|  | ||||
|         for (let w = 0; w < this._workspaces.length; w++) | ||||
|             this._workspaces[w].zoomFromOverview(); | ||||
|         if (!this._extraWorkspaces) | ||||
|             return; | ||||
|         for (let w = 0; w < this._extraWorkspaces.length; w++) | ||||
|             this._extraWorkspaces[w].zoomFromOverview(); | ||||
|     }, | ||||
| @@ -196,6 +204,8 @@ const WorkspacesView = new Lang.Class({ | ||||
|     syncStacking: function(stackIndices) { | ||||
|         for (let i = 0; i < this._workspaces.length; i++) | ||||
|             this._workspaces[i].syncStacking(stackIndices); | ||||
|         if (!this._extraWorkspaces) | ||||
|             return; | ||||
|         for (let i = 0; i < this._extraWorkspaces.length; i++) | ||||
|             this._extraWorkspaces[i].syncStacking(stackIndices); | ||||
|     }, | ||||
| @@ -224,7 +234,7 @@ const WorkspacesView = new Lang.Class({ | ||||
|  | ||||
|             Tweener.removeTweens(workspace.actor); | ||||
|  | ||||
|             let y = (w - active) * (this._height + this._spacing); | ||||
|             let y = (w - active) * (this._height + this._spacing + this._workspaceRatioSpacing); | ||||
|  | ||||
|             if (showAnimation) { | ||||
|                 let params = { y: y, | ||||
| @@ -307,6 +317,8 @@ const WorkspacesView = new Lang.Class({ | ||||
|  | ||||
|             this._updateWorkspaceActors(false); | ||||
|         } | ||||
|  | ||||
|         this._scrollToActive(true); | ||||
|     }, | ||||
|  | ||||
|     _activeWorkspaceChanged: function(wm, from, to, direction) { | ||||
| @@ -366,6 +378,9 @@ const WorkspacesView = new Lang.Class({ | ||||
|             this._firstDragMotion = false; | ||||
|             for (let i = 0; i < this._workspaces.length; i++) | ||||
|                 this._workspaces[i].setReservedSlot(dragEvent.dragActor._delegate); | ||||
|             if (!this._extraWorkspaces) | ||||
|                 return DND.DragMotionResult.CONTINUE; | ||||
|  | ||||
|             for (let i = 0; i < this._extraWorkspaces.length; i++) | ||||
|                 this._extraWorkspaces[i].setReservedSlot(dragEvent.dragActor._delegate); | ||||
|         } | ||||
| @@ -379,6 +394,9 @@ const WorkspacesView = new Lang.Class({ | ||||
|  | ||||
|         for (let i = 0; i < this._workspaces.length; i++) | ||||
|             this._workspaces[i].setReservedSlot(null); | ||||
|  | ||||
|         if (!this._extraWorkspaces) | ||||
|             return; | ||||
|         for (let i = 0; i < this._extraWorkspaces.length; i++) | ||||
|             this._extraWorkspaces[i].setReservedSlot(null); | ||||
|     }, | ||||
| @@ -525,16 +543,6 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|         this._notifyOpacityId = 0; | ||||
|         this._swipeScrollBeginId = 0; | ||||
|         this._swipeScrollEndId = 0; | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA }); | ||||
|         this._settings.connect('changed::dynamic-workspaces', | ||||
|             Lang.bind(this, this._updateSwitcherVisibility)); | ||||
|     }, | ||||
|  | ||||
|     _updateSwitcherVisibility: function() { | ||||
|         this._thumbnailsBox.actor.visible = | ||||
|             this._settings.get_boolean('dynamic-workspaces') || | ||||
|                 global.screen.n_workspaces > 1; | ||||
|     }, | ||||
|  | ||||
|     show: function() { | ||||
| @@ -557,7 +565,6 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|  | ||||
|         this._controls.show(); | ||||
|         this._thumbnailsBox.show(); | ||||
|         this._updateSwitcherVisibility(); | ||||
|  | ||||
|         this._updateWorkspacesViews(); | ||||
|  | ||||
| @@ -859,15 +866,24 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|         let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL); | ||||
|  | ||||
|         let clipWidth = width - controlsVisible; | ||||
|         let clipHeight = fullHeight; | ||||
|         let clipHeight = (fullHeight / fullWidth) * clipWidth; | ||||
|         let clipX = rtl ? x + controlsVisible : x; | ||||
|         let clipY = y + (fullHeight - clipHeight) / 2; | ||||
|  | ||||
|         let widthAdjust = this._zoomOut ? controlsNatural : controlsVisible; | ||||
|         widthAdjust += Main.overview._spacing; | ||||
|         width -= widthAdjust; | ||||
|         if (rtl) | ||||
|             x += widthAdjust; | ||||
|         if (this._zoomOut) { | ||||
|             width -= controlsNatural; | ||||
|             if (rtl) | ||||
|                 x += controlsNatural; | ||||
|         } else { | ||||
|             width -= controlsVisible; | ||||
|             if (rtl) | ||||
|                 x += controlsVisible; | ||||
|         } | ||||
|  | ||||
|         height = (fullHeight / fullWidth) * width; | ||||
|         let difference = fullHeight - height; | ||||
|         y += difference / 2; | ||||
|  | ||||
|  | ||||
|         let monitors = Main.layoutManager.monitors; | ||||
|         let m = 0; | ||||
| @@ -875,7 +891,8 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|             if (i == this._primaryIndex) { | ||||
|                 this._workspacesViews[m].setClipRect(clipX, clipY, | ||||
|                                                      clipWidth, clipHeight); | ||||
|                 this._workspacesViews[m].setGeometry(x, y, width, height); | ||||
|                 this._workspacesViews[m].setGeometry(x, y, width, height, | ||||
|                                                      difference); | ||||
|                 m++; | ||||
|             } else if (!this._workspacesOnlyOnPrimary) { | ||||
|                 this._workspacesViews[m].setClipRect(monitors[i].x, | ||||
| @@ -885,7 +902,7 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|                 this._workspacesViews[m].setGeometry(monitors[i].x, | ||||
|                                                      monitors[i].y, | ||||
|                                                      monitors[i].width, | ||||
|                                                      monitors[i].height); | ||||
|                                                      monitors[i].height, 0); | ||||
|                 m++; | ||||
|             } | ||||
|         } | ||||
| @@ -922,17 +939,16 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|             let monitors = Main.layoutManager.monitors; | ||||
|             let m = 0; | ||||
|             for (let i = 0; i < monitors.length; i++) { | ||||
|                 if (this._workspacesOnlyOnPrimary && | ||||
|                 if (this._workspacesOnlyOnPrimaryChanged && | ||||
|                     i != this._primaryIndex) | ||||
|                     continue; | ||||
|  | ||||
|                 // Assume workspaces are only added at the end | ||||
|                 for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) { | ||||
|                     let metaWorkspace = global.screen.get_workspace_by_index(w); | ||||
|                     this._workspaces[m][w] = | ||||
|                     this._workspaces[m++][w] = | ||||
|                         new Workspace.Workspace(metaWorkspace, i); | ||||
|                 } | ||||
|                 m++; | ||||
|             } | ||||
|  | ||||
|             this._thumbnailsBox.addThumbnails(oldNumWorkspaces, newNumWorkspaces - oldNumWorkspaces); | ||||
| @@ -959,13 +975,12 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             this._thumbnailsBox.removeThumbnails(removedIndex, removedNum); | ||||
|             this._thumbnailsBox.removeThumbmails(removedIndex, removedNum); | ||||
|         } | ||||
|  | ||||
|         for (let i = 0; i < this._workspacesViews.length; i++) | ||||
|             this._workspacesViews[i].updateWorkspaces(oldNumWorkspaces, | ||||
|                                                       newNumWorkspaces); | ||||
|         this._updateSwitcherVisibility(); | ||||
|     }, | ||||
|  | ||||
|     _updateZoom : function() { | ||||
|   | ||||
| @@ -1,19 +1 @@ | ||||
| XSLTPROC_FLAGS = \ | ||||
|         --nonet \ | ||||
|         --stringparam man.output.quietly 1 \ | ||||
|         --stringparam funcsynopsis.style ansi \ | ||||
|         --stringparam man.th.extra1.suppress 1 \ | ||||
|         --stringparam man.authors.section.enabled 0 \ | ||||
|         --stringparam man.copyright.section.enabled 0 | ||||
|  | ||||
| .xml.1: | ||||
| 	$(AM_V_GEN) $(XSLTPROC) $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< | ||||
|  | ||||
| man_MANS = \ | ||||
| 	gnome-shell.1 | ||||
|  | ||||
| xml_files = $(man_MANS:.1=.xml) | ||||
|  | ||||
| EXTRA_DIST = $(xml_files) | ||||
|  | ||||
| DISTCLEANFILES = $(man_MANS) | ||||
| dist_man_MANS = gnome-shell.1 | ||||
|   | ||||
							
								
								
									
										94
									
								
								man/gnome-shell.1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								man/gnome-shell.1
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| .\" Copyright (c) 2009, Marcelo Jorge Vieira (metal) <metal@alucinados.com> | ||||
| .\" | ||||
| .\" This is free documentation; 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. | ||||
| .\" | ||||
| .\" The GNU General Public License's references to "object code" | ||||
| .\" and "executables" are to be interpreted as the output of any | ||||
| .\" document formatting or typesetting system, including | ||||
| .\" intermediate and printed output. | ||||
| .\" | ||||
| .\" This manual 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 manual; if not, write to the Free | ||||
| .\" Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||||
| .\" Boston, MA  02111-1301  USA. | ||||
| .TH GNOME-SHELL 1 | ||||
| .SH NAME | ||||
| gnome-shell \- Graphical shell for the GNOME desktop | ||||
|  | ||||
| .SH SYNOPSIS | ||||
| .B gnome-shell [options] | ||||
|  | ||||
| .SH DESCRIPTION | ||||
| GNOME Shell provides core user interface functions for the GNOME 3 | ||||
| desktop, like switching to windows and launching applications. GNOME | ||||
| Shell takes advantage of the capabilities of modern graphics hardware | ||||
| and introduces innovative user interface concepts to provide a | ||||
| visually attractive and easy to use experience. | ||||
|  | ||||
| .SH OPTIONS | ||||
|  | ||||
| .TP | ||||
| .B \-\-replace | ||||
| Replace the running window manager | ||||
| .br | ||||
|  | ||||
| .TP | ||||
| .B \-\-sm-disable | ||||
| Disable connection to the session manager | ||||
| .br | ||||
|  | ||||
| .TP | ||||
| .B \-\-sm-client-id=ID | ||||
| Specify session management ID | ||||
| .br | ||||
|  | ||||
| .TP | ||||
| .B \-\-sm-save-file=FILE | ||||
| Initialize session from savefile | ||||
| .br | ||||
|  | ||||
| .TP | ||||
| .B \-\-screen=SCREEN | ||||
| X screen to use | ||||
| .br | ||||
|  | ||||
| .TP | ||||
| .B \-d, \-\-display=DISPLAY | ||||
| X display to use | ||||
| .br | ||||
|  | ||||
| .TP | ||||
| .B \-\-sync | ||||
| Make X calls synchronous | ||||
| .br | ||||
|  | ||||
| .TP | ||||
| .B \-\-version | ||||
| Print version and exit | ||||
| .br | ||||
|  | ||||
| .TP | ||||
| .B \-\-help | ||||
| Display help and exit | ||||
| .br | ||||
|  | ||||
| .SH BUGS | ||||
| The bug tracker can be reached by visiting the website | ||||
| \fIhttps://bugzilla.gnome.org/buglist.cgi?product=gnome-shell\fR | ||||
|  | ||||
| Before sending a bug report, please verify that you have the latest | ||||
| version of gnome-shell. Many bugs (major and minor) are fixed at each | ||||
| release, and if yours is out of date, the problem may already have | ||||
| been solved. | ||||
|  | ||||
| .SH ADDITIONAL INFORMATION | ||||
|  | ||||
| For further information, visit the website \fIhttp://live.gnome.org/GnomeShell\fR | ||||
| @@ -1,159 +0,0 @@ | ||||
| <?xml version='1.0'?> | ||||
| <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" | ||||
|         "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> | ||||
|  | ||||
| <refentry id="gnome-shell"> | ||||
|  | ||||
|         <refentryinfo> | ||||
|                 <title>gnome-shell</title> | ||||
|                 <productname>gnome-shell</productname> | ||||
|  | ||||
|                 <authorgroup> | ||||
|                         <author> | ||||
|                                 <contrib>wrote the original gnome-shell man page</contrib> | ||||
|                                 <firstname>Marcelo Jorge</firstname> | ||||
|                                 <surname>Vieira</surname> | ||||
|                                 <email>metal@alucinados.com</email> | ||||
|                         </author> | ||||
|                 </authorgroup> | ||||
|  | ||||
|         </refentryinfo> | ||||
|  | ||||
|         <refmeta> | ||||
|                 <refentrytitle>gnome-shell</refentrytitle> | ||||
|                 <manvolnum>1</manvolnum> | ||||
|         </refmeta> | ||||
|  | ||||
|         <refnamediv> | ||||
|                 <refname>gnome-shell</refname> | ||||
|                 <refpurpose>Graphical shell for the GNOME desktop</refpurpose> | ||||
|         </refnamediv> | ||||
|  | ||||
|         <refsynopsisdiv> | ||||
|                 <cmdsynopsis> | ||||
|                         <command>gnome-shell <arg choice="opt" rep="repeat">OPTION</arg></command> | ||||
|                 </cmdsynopsis> | ||||
|         </refsynopsisdiv> | ||||
|  | ||||
|         <refsect1> | ||||
|                 <title>Description</title> | ||||
|  | ||||
|                 <para>GNOME shell provides core user | ||||
|                 interface functions for the GNOME 3 desktop, like switching | ||||
|                 to windows and launching applications. GNOME shell takes | ||||
|                 advantage of the capabilities of modern graphics hardware | ||||
|                 and introduces innovative user interface concepts to provide | ||||
|                 a visually attractive and easy to use experience.</para> | ||||
|  | ||||
|                 <para>gnome-shell is a required component of | ||||
|                 the GNOME desktop, i.e. it is listed in the | ||||
|                 RequiredComponents field of | ||||
|                 <filename>/usr/share/gnome-session/sessions/gnome.session</filename>. | ||||
|                 It is started in the window manager phase of the session. | ||||
|                 </para> | ||||
|  | ||||
|         </refsect1> | ||||
|  | ||||
|         <refsect1> | ||||
|                 <title>Options</title> | ||||
|  | ||||
|                 <variablelist> | ||||
|  | ||||
|                         <varlistentry> | ||||
|                                 <term><option>-r</option>, <option>--replace</option></term> | ||||
|  | ||||
|                                 <listitem><para>Replace the running window manager</para></listitem> | ||||
|                         </varlistentry> | ||||
|  | ||||
|                         <varlistentry> | ||||
|                                 <term><option>--sm-disable</option></term> | ||||
|  | ||||
|                                 <listitem><para>Disable connection to the session manager</para></listitem> | ||||
|                         </varlistentry> | ||||
|  | ||||
|                         <varlistentry> | ||||
|                                 <term><option>--sm-client-id=<replaceable>ID</replaceable></option></term> | ||||
|  | ||||
|                                 <listitem><para>Specify session management <replaceable>ID</replaceable></para></listitem> | ||||
|                         </varlistentry> | ||||
|  | ||||
|                         <varlistentry> | ||||
|                                 <term><option>--sm-save-file=<replaceable>FILE</replaceable></option></term> | ||||
|  | ||||
|                                 <listitem><para>Initialize session from <replaceable>FILE</replaceable></para></listitem> | ||||
|                         </varlistentry> | ||||
|  | ||||
|                         <varlistentry> | ||||
|                                 <term><option>--screen=<replaceable>SCREEN</replaceable></option></term> | ||||
|  | ||||
|                                 <listitem><para>X screen to use</para></listitem> | ||||
|                         </varlistentry> | ||||
|  | ||||
|                         <varlistentry> | ||||
|                                 <term><option>-d</option>, <option>--display=<replaceable>DISPLAY</replaceable></option></term> | ||||
|  | ||||
|                                 <listitem><para>X Display to use</para></listitem> | ||||
|                         </varlistentry> | ||||
|  | ||||
|                         <varlistentry> | ||||
|                                 <term><option>--sync</option></term> | ||||
|  | ||||
|                                 <listitem><para>Make X calls synchronous</para></listitem> | ||||
|                         </varlistentry> | ||||
|  | ||||
|                         <varlistentry> | ||||
|                                 <term><option>--version</option></term> | ||||
|  | ||||
|                                 <listitem><para>Print version and exit</para></listitem> | ||||
|                         </varlistentry> | ||||
|  | ||||
|                         <varlistentry> | ||||
|                                 <term><option>--help</option></term> | ||||
|  | ||||
|                                 <listitem><para>Display help and exit</para></listitem> | ||||
|                         </varlistentry> | ||||
|  | ||||
|                         <varlistentry> | ||||
|                                 <term><option>--mode=<replaceable>MODE</replaceable></option></term> | ||||
|  | ||||
|                                 <listitem><para>Use a specific mode, e.g. "gdm" for login screen</para></listitem> | ||||
|                         </varlistentry> | ||||
|  | ||||
|                         <varlistentry> | ||||
|                                 <term><option>--list-modes</option></term> | ||||
|  | ||||
|                                 <listitem><para>List possible modes and exit</para></listitem> | ||||
|                         </varlistentry> | ||||
|  | ||||
|                 </variablelist> | ||||
|  | ||||
|         </refsect1> | ||||
|  | ||||
|         <refsect1> | ||||
|                 <title>Files</title> | ||||
|  | ||||
|                 <para> | ||||
|                         <filename>/usr/share/gnome-session/sessions/gnome.session</filename>, | ||||
|                         <filename>/usr/share/applications/gnome-shell.desktop</filename>.</para> | ||||
|         </refsect1> | ||||
|  | ||||
|         <refsect1> | ||||
|                 <title>Bugs</title> | ||||
|  | ||||
|                 <para>The bug tracker can be reached by visiting the | ||||
|                 website <ulink url="https://bugzilla.gnome.org/buglist.cgi?product=gnome-shell">https://bugzilla.gnome.org/buglist.cgi?product=gnome-shell</ulink>. | ||||
|                 Before sending a bug report, please verify that you have | ||||
|                 the latest version of gnome-shell. Many bugs (major and | ||||
|                 minor) are fixed at each release, and if yours is out of | ||||
|                 date, the problem may already have been solved.</para> | ||||
|  | ||||
|         </refsect1> | ||||
|  | ||||
|         <refsect1> | ||||
|                 <title>Additional Information</title> | ||||
|  | ||||
|                 <para>For further information, visit the website | ||||
|                 <ulink url="http://live.gnome.org/GnomeShell">http://live.gnome.org/GnomeShell</ulink>.</para> | ||||
|         </refsect1> | ||||
|  | ||||
| </refentry> | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user