Compare commits
	
		
			6 Commits
		
	
	
		
			3.6.1
			...
			wip/screen
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 352ad8f558 | ||
|   | d5dc886748 | ||
|   | 17044adf96 | ||
|   | 9dfd805be2 | ||
|   | bbbb6b685f | ||
|   | 71c0d5f82d | 
							
								
								
									
										4
									
								
								.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 | ||||
| @@ -51,7 +50,6 @@ po/gnome-shell.pot | ||||
| po/*.header | ||||
| po/*.sed | ||||
| po/*.sin | ||||
| po/.intltool-merge-cache | ||||
| po/Makefile.in.in | ||||
| po/Makevars.template | ||||
| po/POTFILES | ||||
| @@ -64,8 +62,6 @@ src/*-enum-types.[ch] | ||||
| src/*-marshal.[ch] | ||||
| src/Makefile | ||||
| src/Makefile.in | ||||
| src/calendar-server/evolution-calendar.desktop | ||||
| src/calendar-server/evolution-calendar.desktop.in | ||||
| src/calendar-server/org.gnome.Shell.CalendarServer.service | ||||
| src/gnome-shell | ||||
| src/gnome-shell-calendar-server | ||||
|   | ||||
							
								
								
									
										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' }); | ||||
| @@ -13,7 +13,6 @@ EXTRA_DIST =		\ | ||||
| DIST_EXCLUDE =					\ | ||||
| 	.gitignore				\ | ||||
| 	gnome-shell.doap			\ | ||||
| 	HACKING					\ | ||||
| 	MAINTAINERS				\ | ||||
| 	tools/build/* | ||||
|  | ||||
|   | ||||
							
								
								
									
										465
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						| @@ -1,468 +1,3 @@ | ||||
| 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] | ||||
| * dateMenu: Fix regression that caused no date to be displayed [Colin] | ||||
| * WindowTracker: Fix refcounting bug in get_app_for_window() [Giovanni; #678992] | ||||
| * Show the workspace switcher for move-to-workspace keybinding | ||||
|   [Giovanni, Jasper; #674104, #660839, #679005] | ||||
| * userMenu: Move "Power off" item to the bottom [Florian; #678887] | ||||
| * Remove contacts search provider [Florian, Rui; #677442] | ||||
| * network: don't ask for always-ask secrets when interaction isn't allowed | ||||
|   [Dan; #679091] | ||||
| * PolkitAgent: Look for the right password prompt [Matthias; #675300] | ||||
| * Implement extension updates [Jasper; #679099] | ||||
| * userMenu: Don't disconnect account signals when disabled [Guillaume; #669112] | ||||
| * Fix startup notification when opening calendar [Florian; #677907] | ||||
| * networkAgent: use absolute path if configured [Clemens; #679212] | ||||
| * recorder: Port to GStreamer-1.0 API [Florian; #679445] | ||||
| * telepathyClient: don't add log messages on presence changes [Ana; #669508] | ||||
| * lookingGlass: Don't use a signal callback on 'paint' to draw the border | ||||
|   [Jasper; #679464] | ||||
| * Add support for inhibiting automount [Hans; #678597] | ||||
| * Implemented banner support for the login screen [Matthias, Marius; #665346] | ||||
| * boxpointer: Flip side if we would end outside the monitor [Rui; #678164] | ||||
| * boxpointer: Change 'animate' parameter on show/hide to a bitmask | ||||
|   [Rui; #678337] | ||||
| * Add a grayscale effect [Matthias, Jasper, Florian: #676782, #674499] | ||||
| * UserMenu: show "Install Updates & Restart" when appropriate | ||||
|   [Giovanni; #677394, #680080] | ||||
| * messageTray: don't show the message tray when a new notification is shown | ||||
|   [Ana; #677210] | ||||
| * panel: don't break when indicator has no menu [Jean-Philippe; #678694] | ||||
| * appMenu: Disable app menu during startup animations [Florian; #672322] | ||||
| * autorun: Add a notification when unmounting drives [Cosimo; #676125] | ||||
| * st-icon: Fix potential crash involving shadows [Jasper; #679776] | ||||
| * Remove manual garbage collection on tweeners end [Cosimo; #679832] | ||||
| * dash: hide tooltips when overview begins hiding [Stefano; #674241] | ||||
| * Update modal dialog animation for new centered position [Florian; #674499] | ||||
| * calendar: Fix grid lines in RTL locales [Florian; #679879] | ||||
| * Integrate IBus with keyboard indicator [Rui; #641531] | ||||
| * Move ibus status icon under keyboard [Matthias] | ||||
| * gdm: port from libgdmgreeter to libgdm [Ray; #676401] | ||||
| * Misc bug fixes and cleanups [Antoine, Cosimo, Giovanni, Jasper, Rico; | ||||
|   #678978, #672790, #679847, #679944] | ||||
|  | ||||
| Contributors: | ||||
|  Jean-Philippe Braun, Clemens Buchacher, Giovanni Campagna, Cosimo Cecchi, | ||||
|  Matthias Clasen, Hans de Goede, Guillaume Desmottes, Stefano Facchini, | ||||
|  Antoine Jacoutot, Rui Matos, Florian Müllner, Marius Rieder, Ana Risteska, | ||||
|  Jasper St. Pierre, Rico Tzschichholz, Colin Walters, Dan Williams | ||||
|  | ||||
| Translations: | ||||
|  Matej Urbančič [sl], Khaled Hosny [ar], Nguyễn Thái Ngọc Duy [vi], | ||||
|  Nilamdyuti Goswami [as], Alexander Shopov [bg], Ivaylo Valkov [bg], | ||||
|  Daniel Mustieles [es], Kjartan Maraas [nb,nn], Yaron Shahrabani [he], | ||||
|  Nilamdyuti Goswami [as], Chao-Hsiung Liao [zh_HK, zh_TW], Ihar Hrachyshka [be], | ||||
|  Praveen Illa [te] | ||||
|  | ||||
| 3.5.3 | ||||
| ===== | ||||
| * calendar: Adapt to Evolution-Data-Server API changes [Matthew; #677402] | ||||
| * messageTray: Don't show non urgent notifications while in fullscreen | ||||
|   [Adel; #677590] | ||||
| * modalDialog: show dialogs on monitor with the mouse pointer [Tim; #642591] | ||||
| * extensionSystem: Prepare for extension updating system [Jasper; #677586] | ||||
| * appDisplay: Don't show apps in NoDisplay categories in the All view | ||||
|   [Jasper; #658176] | ||||
| * st: Trigger theme updates on resolution changes [Florian; #677975] | ||||
| * Always enable a11y [Bastien; #678095] | ||||
| * telepathyClient: ignore invalidated channels [Guillaume; #677457] | ||||
| * shell-app: Update app menu if necessary [Florian; #676238] | ||||
| * Enable the Screen Reader menu item [Matthias; #663256] | ||||
| * Disable unredirection when a modal operation is active [Giovanni] | ||||
| * Make folks optional [Colin] | ||||
| * Improve mount-operation support [Cosimo] | ||||
|   - Fix exception when showing password entry [#678428] | ||||
|   - Close the password entry on operation abort [#673787] | ||||
|   - autorun: Don't allow autorun for things we mount on startup [#660595] | ||||
|   - Turn passphrase prompt into a dialog [#674962] | ||||
|   - Implement org.Gtk.MountOperationHandler [#678516] | ||||
| * Network menu improvements | ||||
|   - Sort Wifi networks by strength [Giovanni; #658946] | ||||
|   - Prefer wifi/3g over VPN in the panel [Cosimo; #672591] | ||||
| * clock: Switch to using GnomeWallClock [Colin; #657074] | ||||
| * remoteSearch: Allow to reference .desktop file for Title/Icon | ||||
|   [Florian; #678816] | ||||
| * Fix memory leaks [Jasper, Pavel; #678079, #678406, #678737] | ||||
| * Misc fixes [Florian, Giovanni, Guillaume, Jasper, Kjartan, Piotr, Rui; | ||||
|   #658955, #677497, #678396, #678502] | ||||
| * Misc cleanups [Bastien, Florian, Jasper; #677426, #677515, #678096, #678416] | ||||
|  | ||||
| Contributors: | ||||
|  Matthew Barnes, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, | ||||
|  Guillaume Desmottes, Piotr Drąg, Adel Gadllah, Tim L, Kjartan Maraas, | ||||
|  Rui Matos, Florian Müllner, Bastien Nocera, Jasper St. Pierre, Colin Walters | ||||
|  | ||||
| Translations: | ||||
|  Matej Urbančič [sl], Yuri Kozlov [ru], Tom Tryfonidis [el], | ||||
|  Kjartan Maraas [nb], Žygimantas Beručka [lt], Luca Ferretti [it], | ||||
|  Khaled Hosny [ar], Daniel Mustieles [es], Fran Diéguez [gl], A S Alam [pa] | ||||
|  | ||||
| 3.5.2 | ||||
| ===== | ||||
| * main: Move 'toggle-recording' binding into the shell [Florian; #674377] | ||||
| * popupMenu: make sure to break the grab when the slider is not visible | ||||
|   [Stefano; #672713] | ||||
| * st-theme-node-drawing: Don't use GL types [Neil; #672711] | ||||
| * Mirror Evolution calendar settings into our own schema [Owen; #674424] | ||||
| * shell-network-agent: don't crash if a request isn't found [Dan; #674961] | ||||
| * notificationDaemon: Match app based on WM_CLASS [Jasper; #673761] | ||||
| * NetworkMenu: use network-offline while loading [Giovanni; #674426] | ||||
| * lookingGlass: Remove the Errors tab [Jasper; #675104] | ||||
| * searchDisplay: Reset keyboard focus after displaying async results | ||||
|   [Rui; #675078] | ||||
| * gdm: don't fail if fprintd is unavailable [Ray; #675006] | ||||
| * messageTray: Fix scrolling up [Jasper; #661615] | ||||
| * main: Close the recorder instead of pausing it [Rui; #675128] | ||||
| * Accessibility [Alejandro] | ||||
|   - Use the proper label_actor for date menu on top panel [#675307] | ||||
|   - Set the proper role/label_actor for SearchResult.content [#672242] | ||||
|   - do not expose a label text if 'hidden' style class is used [#675341] | ||||
| * Magnifier: Add brightness and contrast functionality [Joseph; #639851] | ||||
| * theme: use a smaller border-radius for top bar [Jakub; #672430] | ||||
| * placeDisplay: use new bookmark file location [Matthias; #675443] | ||||
| * port all synchronous search providers to the async API [Jasper, Rui; #675328] | ||||
| * NetworkAgent: disallow multiple requests for the same connection/setting | ||||
|   [Giovanni; #674961] | ||||
| * userMenu: Update to latest mockups [Florian; #675802] | ||||
| * util: Don't double-fork when spawning from Alt-F2 [Colin; #675789] | ||||
| * messageTray: Make Source usable without subclassing [Jasper; #661236] | ||||
| * panel: Check for appMenu button's reactivity before opening [Florian; #676316] | ||||
| * Fix formatting of bluetooth passkey [Florian; #651251] | ||||
| * notificationDaemon: Filter out file-transfer notifications [Jasper; #676175] | ||||
| * Don't use global.log() [Jasper; #675790] | ||||
| * Fix broken extension loading in some distributions [Owen, Alexandre; #670477] | ||||
| * shell-app: Raise windows in reverse order to preserve the stacking | ||||
|   [Rui; #676371] | ||||
| * Generalize gdm-mode [Florian; #676156] | ||||
| * Switch string formatting to the one inside gjs [Jasper; #675479] | ||||
| * extensionUtils: Support subdirectories in getCurrentExtension | ||||
|   [Jasper; #677001] | ||||
| * panel: Refuse to add (legacy) status icons not part of the session mode | ||||
|   [Florian; #677058] | ||||
| * Add an initial-setup mode [Matthias; #676697] | ||||
| * status/keyboard: Port to the new input sources settings [Rui; #641531] | ||||
| * NetworkMenu: show notifications for failed VPN connections [Giovanni; #676330] | ||||
| * userMenu: Indicate progress on status changes [Florian; #659067] | ||||
| * recorder: Honor "disable-save-to-disk" lockdown key [Rūdolfs; #673630] | ||||
| * searchDisplay: Use the rowLimit we pass to the IconGrid [Christian; #675527] | ||||
| * endSessionDialog: Factor out _updateDescription from _updateContent | ||||
|   [Alejandro; #674210] | ||||
| * Fix empathy's appMenu freezing the shell [Alban; #676447] | ||||
| * Code cleanups [Florian, Giovanni, Jasper; #672807, #672413, #676837, #676850, | ||||
|   #672272] | ||||
| * Misc bug fixes [Alban, Florian, Giovanni, Guillaume, Jasper, Piotr, Rico, | ||||
|   Ron, Rui, Stefano; #659968, #672192, #673177, #673198, #674323, #675301, | ||||
|   #675370, #676347, #676806, #677097] | ||||
|  | ||||
| Contributors: | ||||
|  Alban Browaeys, Giovanni Campagna, Matthias Clasen, Guillaume Desmottes, | ||||
|  Piotr Drąg, Stefano Facchini, Rui Matos, Rūdolfs Mazurs, Florian Müllner, | ||||
|  Alejandro Piñeiro, Neil Roberts, Alexandre Rostovtsev, Joseph Scheuhammer, | ||||
|  Jakub Steiner, Jasper St. Pierre, Ray Strode, Owen Taylor, Rico Tzschichholz, | ||||
|  Colin Walters, Dan Winship, Ron Yorston | ||||
|  | ||||
| Translations: | ||||
|  OKANO Takayoshi [ja], Daniel Mustieles [es], Changwoo Ryu [ko], | ||||
|  Yaron Shahrabani [he], Fran Diéguez [gl], Jonh Wendell [pt_BR], | ||||
|  Kjartan Maraas [nb], Luca Ferretti [it], Tom Tryfonidis [el], | ||||
|  Sandeep Sheshrao Shedmake [mr], Takanori MATSUURA [ja], Dirgita [id], | ||||
|  Mantas Kriaučiūnas [lt], Matej Urbančič [sl], Jiro Matsuzawa [ja] | ||||
|  | ||||
| 3.4.1 | ||||
| ===== | ||||
| * Fix crash that occurred when an icon theme change caused unexpected | ||||
|   | ||||
| @@ -41,7 +41,7 @@ | ||||
|       "It can be used only by extensions.gnome.org" | ||||
| #define PLUGIN_MIME_STRING "application/x-gnome-shell-integration::Gnome Shell Integration Dummy Content-Type"; | ||||
|  | ||||
| #define PLUGIN_API_VERSION 5 | ||||
| #define PLUGIN_API_VERSION 3 | ||||
|  | ||||
| typedef struct { | ||||
|   GDBusProxy *proxy; | ||||
| @@ -163,7 +163,6 @@ NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin) | ||||
|   plugin->newp = NPP_New; | ||||
|   plugin->destroy = NPP_Destroy; | ||||
|   plugin->getvalue = NPP_GetValue; | ||||
|   plugin->setwindow = NPP_SetWindow; | ||||
|  | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
| @@ -225,7 +224,7 @@ NPP_New(NPMIMEType    mimetype, | ||||
|                                                NULL, /* interface info */ | ||||
|                                                "org.gnome.Shell", | ||||
|                                                "/org/gnome/Shell", | ||||
|                                                "org.gnome.Shell.Extensions", | ||||
|                                                "org.gnome.Shell", | ||||
|                                                NULL, /* GCancellable */ | ||||
|                                                &error); | ||||
|   if (!data->proxy) | ||||
| @@ -268,7 +267,6 @@ typedef struct { | ||||
|   NPObject     parent; | ||||
|   NPP          instance; | ||||
|   GDBusProxy  *proxy; | ||||
|   GSettings   *settings; | ||||
|   NPObject    *listener; | ||||
|   NPObject    *restart_listener; | ||||
|   gint         signal_id; | ||||
| @@ -325,9 +323,6 @@ on_shell_appeared (GDBusConnection *connection, | ||||
|     } | ||||
| } | ||||
|  | ||||
| #define SHELL_SCHEMA "org.gnome.shell" | ||||
| #define ENABLED_EXTENSIONS_KEY "enabled-extensions" | ||||
|  | ||||
| static NPObject * | ||||
| plugin_object_allocate (NPP      instance, | ||||
|                         NPClass *klass) | ||||
| @@ -337,7 +332,6 @@ plugin_object_allocate (NPP      instance, | ||||
|  | ||||
|   obj->instance = instance; | ||||
|   obj->proxy = g_object_ref (data->proxy); | ||||
|   obj->settings = g_settings_new (SHELL_SCHEMA); | ||||
|   obj->signal_id = g_signal_connect (obj->proxy, "g-signal", | ||||
|                                      G_CALLBACK (on_shell_signal), obj); | ||||
|  | ||||
| @@ -373,14 +367,39 @@ plugin_object_deallocate (NPObject *npobj) | ||||
|   g_slice_free (PluginObject, obj); | ||||
| } | ||||
|  | ||||
| static NPIdentifier api_version_id; | ||||
| static NPIdentifier shell_version_id; | ||||
| static NPIdentifier get_info_id; | ||||
| static NPIdentifier list_extensions_id; | ||||
| static NPIdentifier enable_extension_id; | ||||
| static NPIdentifier install_extension_id; | ||||
| static NPIdentifier uninstall_extension_id; | ||||
| static NPIdentifier onextension_changed_id; | ||||
| static NPIdentifier onrestart_id; | ||||
| static NPIdentifier get_errors_id; | ||||
| static NPIdentifier launch_extension_prefs_id; | ||||
|  | ||||
| static bool | ||||
| plugin_object_has_method (NPObject     *npobj, | ||||
|                           NPIdentifier  name) | ||||
| { | ||||
|   return (name == get_info_id || | ||||
|           name == list_extensions_id || | ||||
|           name == enable_extension_id || | ||||
|           name == install_extension_id || | ||||
|           name == uninstall_extension_id || | ||||
|           name == get_errors_id || | ||||
|           name == launch_extension_prefs_id); | ||||
| } | ||||
|  | ||||
| static inline gboolean | ||||
| uuid_is_valid (NPString string) | ||||
| uuid_is_valid (const gchar *uuid) | ||||
| { | ||||
|   gsize i; | ||||
|  | ||||
|   for (i = 0; i < string.UTF8Length; i++) | ||||
|   for (i = 0; uuid[i]; i ++) | ||||
|     { | ||||
|       gchar c = string.UTF8Characters[i]; | ||||
|       gchar c = uuid[i]; | ||||
|       if (c < 32 || c >= 127) | ||||
|         return FALSE; | ||||
|  | ||||
| @@ -444,73 +463,8 @@ jsonify_variant (GVariant  *variant, | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| parse_args (const gchar     *format_str, | ||||
|             uint32_t         argc, | ||||
|             const NPVariant *argv, | ||||
|             ...) | ||||
| { | ||||
|   va_list args; | ||||
|   gsize i; | ||||
|   gboolean ret = FALSE; | ||||
|  | ||||
|   if (strlen (format_str) != argc) | ||||
|     return FALSE; | ||||
|  | ||||
|   va_start (args, argv); | ||||
|  | ||||
|   for (i = 0; format_str[i]; i++) | ||||
|     { | ||||
|       gpointer arg_location; | ||||
|       const NPVariant arg = argv[i]; | ||||
|  | ||||
|       arg_location = va_arg (args, gpointer); | ||||
|  | ||||
|       switch (format_str[i]) | ||||
|         { | ||||
|         case 'u': | ||||
|           { | ||||
|             NPString string; | ||||
|  | ||||
|             if (!NPVARIANT_IS_STRING (arg)) | ||||
|               goto out; | ||||
|  | ||||
|             string = NPVARIANT_TO_STRING (arg); | ||||
|  | ||||
|             if (!uuid_is_valid (string)) | ||||
|               goto out; | ||||
|  | ||||
|             *(gchar **) arg_location = g_strndup (string.UTF8Characters, string.UTF8Length); | ||||
|           } | ||||
|           break; | ||||
|  | ||||
|         case 'b': | ||||
|           if (!NPVARIANT_IS_BOOLEAN (arg)) | ||||
|             goto out; | ||||
|  | ||||
|           *(gboolean *) arg_location = NPVARIANT_TO_BOOLEAN (arg); | ||||
|           break; | ||||
|  | ||||
|         case 'o': | ||||
|           if (!NPVARIANT_IS_OBJECT (arg)) | ||||
|             goto out; | ||||
|  | ||||
|           *(NPObject **) arg_location = NPVARIANT_TO_OBJECT (arg); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   ret = TRUE; | ||||
|  | ||||
|  out: | ||||
|   va_end (args); | ||||
|  | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| plugin_list_extensions (PluginObject    *obj, | ||||
|                         uint32_t         argc, | ||||
|                         const NPVariant *args, | ||||
|                         NPVariant       *result) | ||||
| plugin_list_extensions (PluginObject  *obj, | ||||
|                         NPVariant     *result) | ||||
| { | ||||
|   GError *error = NULL; | ||||
|   GVariant *res; | ||||
| @@ -534,159 +488,91 @@ plugin_list_extensions (PluginObject    *obj, | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| plugin_enable_extension (PluginObject    *obj, | ||||
|                          uint32_t         argc, | ||||
|                          const NPVariant *argv, | ||||
|                          NPVariant       *result) | ||||
| plugin_enable_extension (PluginObject *obj, | ||||
|                          NPString      uuid, | ||||
|                          gboolean      enabled) | ||||
| { | ||||
|   gboolean ret; | ||||
|   gchar *uuid; | ||||
|   gboolean enabled; | ||||
|   gsize length; | ||||
|   gchar **uuids; | ||||
|   const gchar **new_uuids; | ||||
|  | ||||
|   if (!parse_args ("ub", argc, argv, &uuid, &enabled)) | ||||
|     return FALSE; | ||||
|  | ||||
|   uuids = g_settings_get_strv (obj->settings, ENABLED_EXTENSIONS_KEY); | ||||
|   length = g_strv_length (uuids); | ||||
|  | ||||
|   if (enabled) | ||||
|   gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length); | ||||
|   if (!uuid_is_valid (uuid_str)) | ||||
|     { | ||||
|       new_uuids = g_new (const gchar *, length + 2); /* New key, NULL */ | ||||
|       memcpy (new_uuids, uuids, length * sizeof (*new_uuids)); | ||||
|       new_uuids[length] = uuid; | ||||
|       new_uuids[length + 1] = NULL; | ||||
|       g_free (uuid_str); | ||||
|       return FALSE; | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       gsize i = 0, j = 0; | ||||
|       new_uuids = g_new (const gchar *, length); | ||||
|       for (i = 0; i < length; i ++) | ||||
|         { | ||||
|           if (g_str_equal (uuids[i], uuid)) | ||||
|             continue; | ||||
|  | ||||
|           new_uuids[j] = uuids[i]; | ||||
|           j++; | ||||
|         } | ||||
|  | ||||
|       new_uuids[j] = NULL; | ||||
|     } | ||||
|  | ||||
|   ret = g_settings_set_strv (obj->settings, | ||||
|                              ENABLED_EXTENSIONS_KEY, | ||||
|                              new_uuids); | ||||
|  | ||||
|   g_strfreev (uuids); | ||||
|   g_free (new_uuids); | ||||
|   g_free (uuid); | ||||
|  | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| typedef struct _AsyncClosure AsyncClosure; | ||||
|  | ||||
| struct _AsyncClosure { | ||||
|   PluginObject *obj; | ||||
|   NPObject *callback; | ||||
|   NPObject *errback; | ||||
| }; | ||||
|  | ||||
| static void | ||||
| install_extension_cb (GObject      *proxy, | ||||
|                       GAsyncResult *async_res, | ||||
|                       gpointer      user_data) | ||||
| { | ||||
|   AsyncClosure *async_closure = (AsyncClosure *) user_data; | ||||
|   GError *error = NULL; | ||||
|   GVariant *res; | ||||
|   NPVariant args[1]; | ||||
|   NPVariant result = { NPVariantType_Void }; | ||||
|   NPObject *callback; | ||||
|  | ||||
|   res = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), async_res, &error); | ||||
|  | ||||
|   if (res == NULL) | ||||
|     { | ||||
|       if (g_dbus_error_is_remote_error (error)) | ||||
|         g_dbus_error_strip_remote_error (error); | ||||
|       STRINGZ_TO_NPVARIANT (error->message, args[0]); | ||||
|       callback = async_closure->errback; | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       char *string_result; | ||||
|       g_variant_get (res, "(&s)", &string_result); | ||||
|       STRINGZ_TO_NPVARIANT (string_result, args[0]); | ||||
|       callback = async_closure->callback; | ||||
|     } | ||||
|  | ||||
|   funcs.invokeDefault (async_closure->obj->instance, | ||||
|                        callback, args, 1, &result); | ||||
|  | ||||
|   funcs.releasevariantvalue (&result); | ||||
|  | ||||
|   funcs.releaseobject (async_closure->callback); | ||||
|   funcs.releaseobject (async_closure->errback); | ||||
|   g_slice_free (AsyncClosure, async_closure); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| plugin_install_extension (PluginObject    *obj, | ||||
|                           uint32_t         argc, | ||||
|                           const NPVariant *argv, | ||||
|                           NPVariant       *result) | ||||
| { | ||||
|   gchar *uuid; | ||||
|   NPObject *callback, *errback; | ||||
|   AsyncClosure *async_closure; | ||||
|  | ||||
|   if (!parse_args ("uoo", argc, argv, &uuid, &callback, &errback)) | ||||
|     return FALSE; | ||||
|  | ||||
|   async_closure = g_slice_new (AsyncClosure); | ||||
|   async_closure->obj = obj; | ||||
|   async_closure->callback = funcs.retainobject (callback); | ||||
|   async_closure->errback = funcs.retainobject (errback); | ||||
|  | ||||
|   g_dbus_proxy_call (obj->proxy, | ||||
|                      "InstallRemoteExtension", | ||||
|                      g_variant_new ("(s)", uuid), | ||||
|                      (enabled ? "EnableExtension" : "DisableExtension"), | ||||
|                      g_variant_new ("(s)", uuid_str), | ||||
|                      G_DBUS_CALL_FLAGS_NONE, | ||||
|                      -1, /* timeout */ | ||||
|                      NULL, /* cancellable */ | ||||
|                      install_extension_cb, | ||||
|                      async_closure); | ||||
|                      NULL, /* callback */ | ||||
|                      NULL /* user_data */); | ||||
|  | ||||
|   g_free (uuid); | ||||
|   g_free (uuid_str); | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| plugin_uninstall_extension (PluginObject    *obj, | ||||
|                             uint32_t         argc, | ||||
|                             const NPVariant *argv, | ||||
|                             NPVariant       *result) | ||||
| plugin_install_extension (PluginObject *obj, | ||||
|                           NPString      uuid, | ||||
|                           NPString      version_tag) | ||||
| { | ||||
|   gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length); | ||||
|   gchar *version_tag_str; | ||||
|  | ||||
|   if (!uuid_is_valid (uuid_str)) | ||||
|     { | ||||
|       g_free (uuid_str); | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   version_tag_str = g_strndup (version_tag.UTF8Characters, | ||||
|                                version_tag.UTF8Length); | ||||
|  | ||||
|   g_dbus_proxy_call (obj->proxy, | ||||
|                      "InstallRemoteExtension", | ||||
|                      g_variant_new ("(ss)", | ||||
|                                     uuid_str, | ||||
|                                     version_tag_str), | ||||
|                      G_DBUS_CALL_FLAGS_NONE, | ||||
|                      -1, /* timeout */ | ||||
|                      NULL, /* cancellable */ | ||||
|                      NULL, /* callback */ | ||||
|                      NULL /* user_data */); | ||||
|  | ||||
|   g_free (uuid_str); | ||||
|   g_free (version_tag_str); | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| plugin_uninstall_extension (PluginObject *obj, | ||||
|                             NPString      uuid, | ||||
|                             NPVariant    *result) | ||||
| { | ||||
|   GError *error = NULL; | ||||
|   GVariant *res; | ||||
|   gchar *uuid; | ||||
|   gchar *uuid_str; | ||||
|  | ||||
|   if (!parse_args ("u", argc, argv, &uuid)) | ||||
|     return FALSE; | ||||
|   uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length); | ||||
|   if (!uuid_is_valid (uuid_str)) | ||||
|     { | ||||
|       g_free (uuid_str); | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   res = g_dbus_proxy_call_sync (obj->proxy, | ||||
|                                 "UninstallExtension", | ||||
|                                 g_variant_new ("(s)", uuid), | ||||
|                                 g_variant_new ("(s)", | ||||
|                                                uuid_str), | ||||
|                                 G_DBUS_CALL_FLAGS_NONE, | ||||
|                                 -1, /* timeout */ | ||||
|                                 NULL, /* cancellable */ | ||||
|                                 &error); | ||||
|  | ||||
|   g_free (uuid); | ||||
|   g_free (uuid_str); | ||||
|  | ||||
|   if (!res) | ||||
|     { | ||||
| @@ -699,27 +585,30 @@ plugin_uninstall_extension (PluginObject    *obj, | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| plugin_get_info (PluginObject    *obj, | ||||
|                  uint32_t         argc, | ||||
|                  const NPVariant *argv, | ||||
|                  NPVariant       *result) | ||||
| plugin_get_info (PluginObject *obj, | ||||
|                  NPString      uuid, | ||||
|                  NPVariant    *result) | ||||
| { | ||||
|   GError *error = NULL; | ||||
|   GVariant *res; | ||||
|   gchar *uuid; | ||||
|   gchar *uuid_str; | ||||
|  | ||||
|   if (!parse_args ("u", argc, argv, &uuid)) | ||||
|     return FALSE; | ||||
|   uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length); | ||||
|   if (!uuid_is_valid (uuid_str)) | ||||
|     { | ||||
|       g_free (uuid_str); | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   res = g_dbus_proxy_call_sync (obj->proxy, | ||||
|                                 "GetExtensionInfo", | ||||
|                                 g_variant_new ("(s)", uuid), | ||||
|                                 g_variant_new ("(s)", uuid_str), | ||||
|                                 G_DBUS_CALL_FLAGS_NONE, | ||||
|                                 -1, /* timeout */ | ||||
|                                 NULL, /* cancellable */ | ||||
|                                 &error); | ||||
|  | ||||
|   g_free (uuid); | ||||
|   g_free (uuid_str); | ||||
|  | ||||
|   if (!res) | ||||
|     { | ||||
| @@ -732,26 +621,31 @@ plugin_get_info (PluginObject    *obj, | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| plugin_get_errors (PluginObject    *obj, | ||||
|                    uint32_t         argc, | ||||
|                    const NPVariant *argv, | ||||
|                    NPVariant       *result) | ||||
| plugin_get_errors (PluginObject *obj, | ||||
|                    NPString      uuid, | ||||
|                    NPVariant    *result) | ||||
| { | ||||
|   GError *error = NULL; | ||||
|   GVariant *res; | ||||
|   gchar *uuid; | ||||
|   gchar *uuid_str; | ||||
|  | ||||
|   if (!parse_args ("u", argc, argv, &uuid)) | ||||
|     return FALSE; | ||||
|   uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length); | ||||
|   if (!uuid_is_valid (uuid_str)) | ||||
|     { | ||||
|       g_free (uuid_str); | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   res = g_dbus_proxy_call_sync (obj->proxy, | ||||
|                                 "GetExtensionErrors", | ||||
|                                 g_variant_new ("(s)", uuid), | ||||
|                                 g_variant_new ("(s)", uuid_str), | ||||
|                                 G_DBUS_CALL_FLAGS_NONE, | ||||
|                                 -1, /* timeout */ | ||||
|                                 NULL, /* cancellable */ | ||||
|                                 &error); | ||||
|  | ||||
|   g_free (uuid_str); | ||||
|  | ||||
|   if (!res) | ||||
|     { | ||||
|       g_warning ("Failed to retrieve errors: %s", error->message); | ||||
| @@ -763,25 +657,29 @@ plugin_get_errors (PluginObject    *obj, | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| plugin_launch_extension_prefs (PluginObject    *obj, | ||||
|                                uint32_t         argc, | ||||
|                                const NPVariant *argv, | ||||
|                                NPVariant       *result) | ||||
| plugin_launch_extension_prefs (PluginObject *obj, | ||||
|                                NPString      uuid, | ||||
|                                NPVariant    *result) | ||||
| { | ||||
|   gchar *uuid; | ||||
|   gchar *uuid_str; | ||||
|  | ||||
|   if (!parse_args ("u", argc, argv, &uuid)) | ||||
|     return FALSE; | ||||
|   uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length); | ||||
|   if (!uuid_is_valid (uuid_str)) | ||||
|     { | ||||
|       g_free (uuid_str); | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   g_dbus_proxy_call (obj->proxy, | ||||
|                      "LaunchExtensionPrefs", | ||||
|                      g_variant_new ("(s)", uuid), | ||||
|                      g_variant_new ("(s)", uuid_str), | ||||
|                      G_DBUS_CALL_FLAGS_NONE, | ||||
|                      -1, /* timeout */ | ||||
|                      NULL, /* cancellable */ | ||||
|                      NULL, /* callback */ | ||||
|                      NULL /* user_data */); | ||||
|  | ||||
|   g_free (uuid_str); | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| @@ -835,40 +733,10 @@ plugin_get_shell_version (PluginObject  *obj, | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| #define METHODS                                 \ | ||||
|   METHOD (list_extensions)                      \ | ||||
|   METHOD (get_info)                             \ | ||||
|   METHOD (enable_extension)                     \ | ||||
|   METHOD (install_extension)                    \ | ||||
|   METHOD (uninstall_extension)                  \ | ||||
|   METHOD (get_errors)                           \ | ||||
|   METHOD (launch_extension_prefs)               \ | ||||
|   /* */ | ||||
|  | ||||
| #define METHOD(x)                               \ | ||||
|   static NPIdentifier x##_id; | ||||
| METHODS | ||||
| #undef METHOD | ||||
|  | ||||
| static NPIdentifier api_version_id; | ||||
| static NPIdentifier shell_version_id; | ||||
| static NPIdentifier onextension_changed_id; | ||||
| static NPIdentifier onrestart_id; | ||||
|  | ||||
| static bool | ||||
| plugin_object_has_method (NPObject     *npobj, | ||||
|                           NPIdentifier  name) | ||||
| { | ||||
| #define METHOD(x) (name == (x##_id)) || | ||||
|   /* expands to (name == list_extensions_id) || FALSE; */ | ||||
|   return METHODS FALSE; | ||||
| #undef METHOD | ||||
| } | ||||
|  | ||||
| static bool | ||||
| plugin_object_invoke (NPObject        *npobj, | ||||
|                       NPIdentifier     name, | ||||
|                       const NPVariant *argv, | ||||
|                       const NPVariant *args, | ||||
|                       uint32_t         argc, | ||||
|                       NPVariant       *result) | ||||
| { | ||||
| @@ -880,13 +748,61 @@ plugin_object_invoke (NPObject        *npobj, | ||||
|  | ||||
|   VOID_TO_NPVARIANT (*result); | ||||
|  | ||||
| #define METHOD(x)                                       \ | ||||
|   if (name == x##_id)                                   \ | ||||
|     return plugin_##x (obj, argc, argv, result); | ||||
| METHODS | ||||
| #undef METHOD | ||||
|   if (!plugin_object_has_method (npobj, name)) | ||||
|     return FALSE; | ||||
|  | ||||
|   return FALSE; | ||||
|   if (name == list_extensions_id) | ||||
|     return plugin_list_extensions (obj, result); | ||||
|   else if (name == get_info_id) | ||||
|     { | ||||
|       if (!NPVARIANT_IS_STRING(args[0])) return FALSE; | ||||
|  | ||||
|       return plugin_get_info (obj, NPVARIANT_TO_STRING(args[0]), result); | ||||
|     } | ||||
|   else if (name == enable_extension_id) | ||||
|     { | ||||
|       if (!NPVARIANT_IS_STRING(args[0])) return FALSE; | ||||
|       if (!NPVARIANT_IS_BOOLEAN(args[1])) return FALSE; | ||||
|  | ||||
|       return plugin_enable_extension (obj, | ||||
|                                       NPVARIANT_TO_STRING(args[0]), | ||||
|                                       NPVARIANT_TO_BOOLEAN(args[1])); | ||||
|     } | ||||
|   else if (name == install_extension_id) | ||||
|     { | ||||
|       if (!NPVARIANT_IS_STRING(args[0])) return FALSE; | ||||
|       if (!NPVARIANT_IS_STRING(args[1])) return FALSE; | ||||
|  | ||||
|       return plugin_install_extension (obj, | ||||
|                                        NPVARIANT_TO_STRING(args[0]), | ||||
|                                        NPVARIANT_TO_STRING(args[1])); | ||||
|     } | ||||
|   else if (name == uninstall_extension_id) | ||||
|     { | ||||
|       if (!NPVARIANT_IS_STRING(args[0])) return FALSE; | ||||
|  | ||||
|       return plugin_uninstall_extension (obj, | ||||
|                                          NPVARIANT_TO_STRING(args[0]), | ||||
|                                          result); | ||||
|     } | ||||
|   else if (name == get_errors_id) | ||||
|     { | ||||
|       if (!NPVARIANT_IS_STRING(args[0])) return FALSE; | ||||
|  | ||||
|       return plugin_get_errors (obj, | ||||
|                                 NPVARIANT_TO_STRING(args[0]), | ||||
|                                 result); | ||||
|     } | ||||
|   else if (name == launch_extension_prefs_id) | ||||
|     { | ||||
|       if (!NPVARIANT_IS_STRING(args[0])) return FALSE; | ||||
|  | ||||
|       return plugin_launch_extension_prefs (obj, | ||||
|                                             NPVARIANT_TO_STRING(args[0]), | ||||
|                                             result); | ||||
|     } | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static bool | ||||
| @@ -1030,12 +946,3 @@ NPP_GetValue(NPP          instance, | ||||
|  | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| /* Opera tries to call NPP_SetWindow without checking the | ||||
|  * NULL pointer beforehand. */ | ||||
| NPError | ||||
| NPP_SetWindow(NPP          instance, | ||||
|               NPWindow    *window) | ||||
| { | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|   | ||||
							
								
								
									
										64
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						| @@ -1,5 +1,5 @@ | ||||
| AC_PREREQ(2.63) | ||||
| AC_INIT([gnome-shell],[3.6.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) | ||||
| AC_INIT([gnome-shell],[3.4.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) | ||||
|  | ||||
| AC_CONFIG_HEADERS([config.h]) | ||||
| AC_CONFIG_SRCDIR([src/shell-global.c]) | ||||
| @@ -44,15 +44,15 @@ AC_SUBST(PYTHON) | ||||
|  | ||||
| # We need at least this, since gst_plugin_register_static() was added | ||||
| # in 0.10.16, but nothing older than 0.10.21 has been tested. | ||||
| GSTREAMER_MIN_VERSION=0.11.92 | ||||
| GSTREAMER_MIN_VERSION=0.10.16 | ||||
|  | ||||
| recorder_modules= | ||||
| build_recorder=false | ||||
| AC_MSG_CHECKING([for GStreamer (needed for recording functionality)]) | ||||
| if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then | ||||
| if $PKG_CONFIG --exists gstreamer-0.10 '>=' $GSTREAMER_MIN_VERSION ; then | ||||
|    AC_MSG_RESULT(yes) | ||||
|    build_recorder=true | ||||
|    recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11" | ||||
|    recorder_modules="gstreamer-0.10 gstreamer-base-0.10 x11" | ||||
|    PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl) | ||||
| else | ||||
|    AC_MSG_RESULT(no) | ||||
| @@ -60,32 +60,30 @@ 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.6.1 | ||||
| GJS_MIN_VERSION=1.29.18 | ||||
| MUTTER_MIN_VERSION=3.4.1 | ||||
| FOLKS_MIN_VERSION=0.5.2 | ||||
| GTK_MIN_VERSION=3.3.9 | ||||
| GIO_MIN_VERSION=2.31.6 | ||||
| LIBECAL_MIN_VERSION=3.5.3 | ||||
| LIBEDATASERVER_MIN_VERSION=3.5.3 | ||||
| LIBEDATASERVERUI_MIN_VERSION=3.5.3 | ||||
| LIBECAL_MIN_VERSION=2.32.0 | ||||
| LIBEDATASERVER_MIN_VERSION=1.2.0 | ||||
| LIBEDATASERVERUI_MIN_VERSION=2.91.6 | ||||
| TELEPATHY_GLIB_MIN_VERSION=0.17.5 | ||||
| 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.5.1 | ||||
| GNOME_MENUS_REQUIRED_VERSION=3.5.3 | ||||
|  | ||||
| # Collect more than 20 libraries for a prize! | ||||
| PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION | ||||
| 			       libxml-2.0 | ||||
|                                gtk+-3.0 >= $GTK_MIN_VERSION | ||||
|                                atk-bridge-2.0 | ||||
|                                folks >= $FOLKS_MIN_VERSION | ||||
|                                libmutter >= $MUTTER_MIN_VERSION | ||||
|                                gjs-internals-1.0 >= $GJS_MIN_VERSION | ||||
| 			       libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION | ||||
|                                $recorder_modules | ||||
| 			       libgnome-menu-3.0 $recorder_modules | ||||
|                                gdk-x11-3.0 libsoup-2.4 | ||||
|                                gl | ||||
| 			       clutter-x11-1.0 >= $CLUTTER_MIN_VERSION | ||||
| @@ -97,8 +95,7 @@ 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 | ||||
|                                gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION) | ||||
|                                gcr-3 >= $GCR_MIN_VERSION) | ||||
|  | ||||
| PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0) | ||||
|  | ||||
| @@ -106,8 +103,9 @@ 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]) | ||||
| GJS_VERSION=`$PKG_CONFIG --modversion gjs-internals-1.0` | ||||
| AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to]) | ||||
| AC_SUBST([GJS_VERSION], ["$GJS_VERSION"]) | ||||
|  | ||||
| GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION]) | ||||
|  | ||||
| @@ -123,7 +121,7 @@ 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.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) | ||||
| PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 0.1.7) | ||||
|  | ||||
| AC_MSG_CHECKING([for bluetooth support]) | ||||
| PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0], | ||||
| @@ -239,6 +237,31 @@ AC_ARG_ENABLE(jhbuild-wrapper-script, | ||||
|   AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no) | ||||
| AM_CONDITIONAL(USE_JHBUILD_WRAPPER_SCRIPT, test "x$enable_jhbuild_wrapper_script" = xyes) | ||||
|  | ||||
| AC_MSG_CHECKING([location of system Certificate Authority list]) | ||||
| AC_ARG_WITH(ca-certificates, | ||||
|             [AC_HELP_STRING([--with-ca-certificates=@<:@path@:>@], | ||||
|                             [path to system Certificate Authority list])]) | ||||
|  | ||||
| if test "$with_ca_certificates" = "no"; then | ||||
|     AC_MSG_RESULT([disabled]) | ||||
| else | ||||
|     if test -z "$with_ca_certificates"; then | ||||
|         for f in /etc/pki/tls/certs/ca-bundle.crt \ | ||||
|                  /etc/ssl/certs/ca-certificates.crt; do | ||||
|              if test -f "$f"; then | ||||
|                 with_ca_certificates="$f" | ||||
|              fi | ||||
|         done | ||||
|         if test -z "$with_ca_certificates"; then | ||||
|             AC_MSG_ERROR([could not find. Use --with-ca-certificates=path to set, or --without-ca-certificates to disable]) | ||||
|         fi | ||||
|    fi | ||||
|  | ||||
|    AC_MSG_RESULT($with_ca_certificates) | ||||
|    AC_DEFINE_UNQUOTED(SHELL_SYSTEM_CA_FILE, ["$with_ca_certificates"], [The system TLS CA list]) | ||||
| fi | ||||
| AC_SUBST(SHELL_SYSTEM_CA_FILE,["$with_ca_certificates"]) | ||||
|  | ||||
| BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}" | ||||
| AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to]) | ||||
|  | ||||
| @@ -252,7 +275,6 @@ AC_CONFIG_FILES([ | ||||
|   docs/reference/st/Makefile | ||||
|   docs/reference/st/st-docs.sgml | ||||
|   js/Makefile | ||||
|   src/calendar-server/evolution-calendar.desktop.in | ||||
|   src/Makefile | ||||
|   browser-plugin/Makefile | ||||
|   tests/Makefile | ||||
|   | ||||
| @@ -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,12 +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"/> | ||||
|  | ||||
| </KeyListEntries> | ||||
|  | ||||
| @@ -8,7 +8,14 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop | ||||
| 	    -e "s|@VERSION[@]|$(VERSION)|" \ | ||||
| 	    $< > $@ || rm $@ | ||||
|  | ||||
| @INTLTOOL_DESKTOP_RULE@ | ||||
| # Placeholder until we add intltool | ||||
| %.desktop:%.desktop.in | ||||
| 	$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@ | ||||
|  | ||||
| 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 | ||||
| @@ -29,39 +36,26 @@ dist_theme_DATA =				\ | ||||
| 	theme/dash-placeholder.svg		\ | ||||
| 	theme/filter-selected-ltr.svg		\ | ||||
| 	theme/filter-selected-rtl.svg		\ | ||||
| 	theme/gdm.css				\ | ||||
| 	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	\ | ||||
| 	theme/panel-button-highlight-wide.svg	\ | ||||
| 	theme/process-working.svg		\ | ||||
| 	theme/running-indicator.svg		\ | ||||
| 	theme/scroll-hhandle.svg		\ | ||||
| 	theme/scroll-vhandle.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 | ||||
| 	theme/ws-switch-arrow-up.svg		\ | ||||
| 	theme/ws-switch-arrow-down.svg | ||||
|  | ||||
| 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) | ||||
|  | ||||
| gsettings_SCHEMAS = org.gnome.shell.gschema.xml | ||||
| gsettings_SCHEMAS = org.gnome.shell.gschema.xml org.gnome.shell.evolution.calendar.gschema.xml | ||||
|  | ||||
| @INTLTOOL_XML_NOMERGE_RULE@ | ||||
|  | ||||
| %.gschema.xml.in: %.gschema.xml.in.in Makefile | ||||
| 	$(AM_V_GEN) sed -e 's|@GETTEXT_PACKAGE[@]|$(GETTEXT_PACKAGE)|g' \ | ||||
| 	$< > $@ || rm $@ | ||||
|  | ||||
| @GSETTINGS_RULES@ | ||||
|  | ||||
| # We need to compile schemas at make time | ||||
| @@ -74,21 +68,25 @@ all-local: gschemas.compiled | ||||
| convertdir = $(datadir)/GConf/gsettings | ||||
| convert_DATA = gnome-shell-overrides.convert | ||||
|  | ||||
| shadersdir = $(pkgdatadir)/shaders | ||||
| shaders_DATA = \ | ||||
| 	shaders/dim-window.glsl | ||||
|  | ||||
|  | ||||
| EXTRA_DIST =						\ | ||||
| 	gnome-shell.desktop.in.in			\ | ||||
| 	gnome-shell-extension-prefs.desktop.in.in	\ | ||||
| 	$(introspection_DATA)				\ | ||||
| 	$(menu_DATA)					\ | ||||
| 	$(shaders_DATA)					\ | ||||
| 	$(convert_DATA)					\ | ||||
| 	$(keys_in_files)				\ | ||||
| 	org.gnome.shell.gschema.xml.in.in | ||||
| 	org.gnome.shell.evolution.calendar.gschema.xml.in	\ | ||||
| 	org.gnome.shell.gschema.xml.in | ||||
|  | ||||
| CLEANFILES =						\ | ||||
| 	gnome-shell.desktop.in				\ | ||||
| 	gnome-shell-extension-prefs.in			\ | ||||
| 	$(desktop_DATA)					\ | ||||
| 	$(keys_DATA)					\ | ||||
| 	$(gsettings_SCHEMAS)				\ | ||||
| 	gschemas.compiled				\ | ||||
| 	org.gnome.shell.gschema.valid			\ | ||||
| 	org.gnome.shell.gschema.xml.in | ||||
| 	gschemas.compiled | ||||
|  | ||||
|   | ||||
							
								
								
									
										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
									
								
							
							
						
						| @@ -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> | ||||
|   | ||||
							
								
								
									
										21
									
								
								data/org.gnome.shell.evolution.calendar.gschema.xml.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,21 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!-- NOTE: This schema is a GNOME 3.4 workaround - it uses the same path | ||||
|      as org.gnome.evolution.calendar, but avoids us requiring Evolution | ||||
|      be installed. In GNOME 3.6 the selected state will become a flag | ||||
|      on the calendar. Because the translations are in Evolution, | ||||
|      this is untranslated and in POTFILES.skip. | ||||
|   --> | ||||
| <schemalist> | ||||
|   <schema path="/org/gnome/evolution/calendar/" id="org.gnome.shell.evolution.calendar" gettext-domain="evolution"> | ||||
|     <key type="as" name="selected-calendars"> | ||||
|       <default>[]</default> | ||||
|       <summary>List of selected calendars</summary> | ||||
|       <description>List of calendars to load</description> | ||||
|     </key> | ||||
|     <key type="as" name="selected-tasks"> | ||||
|       <default>[]</default> | ||||
|       <summary>List of selected task lists</summary> | ||||
|       <description>List of task lists to load</description> | ||||
|     </key> | ||||
|   </schema> | ||||
| </schemalist> | ||||
| @@ -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,6 +61,7 @@ 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> | ||||
|     <child name="clock" schema="org.gnome.shell.clock"/> | ||||
|     <child name="calendar" schema="org.gnome.shell.calendar"/> | ||||
|     <child name="recorder" schema="org.gnome.shell.recorder"/> | ||||
|     <child name="keybindings" schema="org.gnome.shell.keybindings"/> | ||||
| @@ -83,13 +88,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary> | ||||
|         Keybinding to open the application menu. | ||||
|       </_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="toggle-recording" type="as"> | ||||
|       <default><![CDATA[['<Control><Shift><Alt>r']]]></default> | ||||
|       <_summary>Keybinding to toggle the screen recorder</_summary> | ||||
| @@ -110,6 +108,24 @@ value here is from the GsmPresenceStatus enumeration.</_summary> | ||||
|     </key> | ||||
|   </schema> | ||||
| 
 | ||||
|   <schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/" | ||||
|           gettext-domain="@GETTEXT_PACKAGE@"> | ||||
|     <key name="show-seconds" type="b"> | ||||
|       <default>false</default> | ||||
|       <_summary>Show time with seconds</_summary> | ||||
|       <_description> | ||||
|         If true, display seconds in time. | ||||
|       </_description> | ||||
|     </key> | ||||
|     <key name="show-date" type="b"> | ||||
|       <default>false</default> | ||||
|       <_summary>Show date in clock</_summary> | ||||
|       <_description> | ||||
|         If true, display date in the clock, in addition to time. | ||||
|       </_description> | ||||
|     </key> | ||||
|   </schema> | ||||
| 
 | ||||
|   <schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/" | ||||
|           gettext-domain="@GETTEXT_PACKAGE@"> | ||||
|     <key name="framerate" type="i"> | ||||
| @@ -132,7 +148,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> | ||||
| @@ -190,13 +206,5 @@ value here is from the GsmPresenceStatus enumeration.</_summary> | ||||
|         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> | ||||
|       <description> | ||||
|         This key overrides the key in org.gnome.mutter when running GNOME Shell. | ||||
|       </description> | ||||
|     </key> | ||||
|   </schema> | ||||
| </schemalist> | ||||
							
								
								
									
										27
									
								
								data/shaders/dim-window.glsl
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,27 @@ | ||||
| #version 110 | ||||
| uniform sampler2D tex; | ||||
| uniform float fraction; | ||||
| uniform float height; | ||||
| const float c = -0.2; | ||||
| const float border_max_height = 60.0; | ||||
|  | ||||
| mat4 contrast = mat4 (1.0 + c, 0.0, 0.0, 0.0, | ||||
|                       0.0, 1.0 + c, 0.0, 0.0, | ||||
|                       0.0, 0.0, 1.0 + c, 0.0, | ||||
|                       0.0, 0.0, 0.0, 1.0); | ||||
| vec4 off = vec4(0.633, 0.633, 0.633, 0); | ||||
| void main() | ||||
| { | ||||
|   vec4 color = texture2D(tex, cogl_tex_coord_in[0].xy); | ||||
|   float y = height * cogl_tex_coord_in[0].y; | ||||
|  | ||||
|   // To reduce contrast, blend with a mid gray | ||||
|   cogl_color_out = color * contrast - off * c * color.a; | ||||
|  | ||||
|   // We only fully dim at a distance of BORDER_MAX_HEIGHT from the top and | ||||
|   // when the fraction is 1.0. For other locations and fractions we linearly | ||||
|   // interpolate back to the original undimmed color, so the top of the window | ||||
|   // is at full color. | ||||
|   cogl_color_out = color + (cogl_color_out - color) * max(min(y / border_max_height, 1.0), 0.0); | ||||
|   cogl_color_out = color + (cogl_color_out - color) * fraction; | ||||
| } | ||||
| @@ -10,11 +10,11 @@ | ||||
|    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="29" | ||||
|    height="29" | ||||
|    width="28" | ||||
|    height="25" | ||||
|    id="svg10621" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.48.2 r9819" | ||||
|    inkscape:version="0.48.1 r9760" | ||||
|    sodipodi:docname="calendar-today.svg"> | ||||
|   <defs | ||||
|      id="defs10623"> | ||||
| @@ -118,17 +118,6 @@ | ||||
|        fx="51" | ||||
|        fy="30" | ||||
|        r="42" /> | ||||
|     <radialGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient34508-1-3" | ||||
|        id="radialGradient3113" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)" | ||||
|        cx="51" | ||||
|        cy="30" | ||||
|        fx="51" | ||||
|        fy="30" | ||||
|        r="42" /> | ||||
|   </defs> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
| @@ -138,29 +127,20 @@ | ||||
|      inkscape:pageopacity="0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="15.839192" | ||||
|      inkscape:cx="20.652108" | ||||
|      inkscape:cy="11.839084" | ||||
|      inkscape:cx="8.3750933" | ||||
|      inkscape:cy="8.0837211" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="layer1" | ||||
|      showgrid="true" | ||||
|      showgrid="false" | ||||
|      fit-margin-top="0" | ||||
|      fit-margin-left="0" | ||||
|      fit-margin-right="0" | ||||
|      fit-margin-bottom="0" | ||||
|      inkscape:window-width="1280" | ||||
|      inkscape:window-height="741" | ||||
|      inkscape:window-width="1440" | ||||
|      inkscape:window-height="843" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-maximized="1" | ||||
|      borderlayer="true"> | ||||
|     <inkscape:grid | ||||
|        type="xygrid" | ||||
|        id="grid3109" | ||||
|        empspacing="5" | ||||
|        visible="true" | ||||
|        enabled="true" | ||||
|        snapvisiblegridlinesonly="true" /> | ||||
|   </sodipodi:namedview> | ||||
|      inkscape:window-y="26" | ||||
|      inkscape:window-maximized="1" /> | ||||
|   <metadata | ||||
|      id="metadata10626"> | ||||
|     <rdf:RDF> | ||||
| @@ -177,28 +157,31 @@ | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1" | ||||
|      transform="translate(-469.08263,-532.99307)"> | ||||
|     <path | ||||
|        sodipodi:type="arc" | ||||
|        style="opacity:0.4625;color:#000000;fill:url(#radialGradient3113);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" | ||||
|        id="path34506-3" | ||||
|        sodipodi:cx="51" | ||||
|        sodipodi:cy="30" | ||||
|        sodipodi:rx="42" | ||||
|        sodipodi:ry="16" | ||||
|        d="M 9,29.999999 A 42,16 0 0 1 93,30 l -42,0 z" | ||||
|        sodipodi:start="3.1415927" | ||||
|        sodipodi:end="6.2831853" | ||||
|        transform="matrix(0.43692393,0,0,1.3783114,461.29951,517.6437)" | ||||
|        inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png" | ||||
|        inkscape:export-xdpi="90" | ||||
|        inkscape:export-ydpi="90" /> | ||||
|     <rect | ||||
|        style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none" | ||||
|        id="rect2996" | ||||
|        width="31" | ||||
|        height="3" | ||||
|        x="468.08264" | ||||
|        y="558.99304" /> | ||||
|      transform="translate(-469.08263,-536.99307)"> | ||||
|     <g | ||||
|        id="g3003"> | ||||
|       <path | ||||
|          inkscape:export-ydpi="90" | ||||
|          inkscape:export-xdpi="90" | ||||
|          inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png" | ||||
|          transform="matrix(0.43692393,0,0,1.3783114,460.60467,517.48289)" | ||||
|          sodipodi:end="6.2831853" | ||||
|          sodipodi:start="3.1415927" | ||||
|          d="M 9,29.999999 C 9.0000011,21.163443 27.804042,14 51.000002,14 74.195961,14 93,21.163444 93,30 l -42,0 z" | ||||
|          sodipodi:ry="16" | ||||
|          sodipodi:rx="42" | ||||
|          sodipodi:cy="30" | ||||
|          sodipodi:cx="51" | ||||
|          id="path34506-3" | ||||
|          style="opacity:0.4625;color:#000000;fill:url(#radialGradient2997);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" | ||||
|          sodipodi:type="arc" /> | ||||
|       <rect | ||||
|          y="558.85046" | ||||
|          x="468.96878" | ||||
|          height="3.1425927" | ||||
|          width="28.149134" | ||||
|          id="rect2996" | ||||
|          style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none" /> | ||||
|     </g> | ||||
|   </g> | ||||
| </svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 5.7 KiB | 
							
								
								
									
										180
									
								
								data/theme/gdm.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,180 @@ | ||||
| /* Copyright 2011, Red Hat, Inc. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms and conditions of the GNU Lesser General Public License, | ||||
|  * version 2.1, as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope it will be useful, but WITHOUT ANY | ||||
|  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||||
|  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with this program; if not, write to the Free Software Foundation, | ||||
|  * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  */ | ||||
|  | ||||
| /* Login Dialog */ | ||||
|  | ||||
| .login-dialog-title { | ||||
|     font-size: 14pt; | ||||
|     font-weight: bold; | ||||
|     color: #666666; | ||||
|     padding-bottom: 2em; | ||||
| } | ||||
|  | ||||
| .login-dialog { | ||||
|     border-radius: 16px; | ||||
|     min-height: 150px; | ||||
|     max-height: 700px; | ||||
|     min-width: 350px; | ||||
| } | ||||
|  | ||||
| .login-dialog-prompt-fingerprint-message { | ||||
|     font-size: 10.5pt; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list-view { | ||||
|     -st-vfade-offset: 1em; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list { | ||||
|     spacing: 12px; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list-item { | ||||
|     color: #666666; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list-item:ltr { | ||||
|     padding-right: 1em; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list-item:rtl { | ||||
|     padding-left: 1em; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list-item .login-dialog-user-list-item-name { | ||||
|     font-size: 20pt; | ||||
|     padding-left: 1em; | ||||
|     color: #666666; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list-item:hover .login-dialog-user-list-item-name { | ||||
|     color: white; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list-item:focus .login-dialog-user-list-item-name { | ||||
|     color: white; | ||||
|     text-shadow: black 0px 2px 2px; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list-item-vertical-layout { | ||||
|     spacing: 2px; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list-item .login-dialog-user-list-item-focus-bin { | ||||
|     background-color: rgba(0,0,0,0.0); | ||||
|     height: 2px; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list-item:focus .login-dialog-user-list-item-focus-bin { | ||||
|     background-color: #666666; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list-item-icon { | ||||
|     border: 2px solid #8b8b8b; | ||||
|     border-radius: 8px; | ||||
|     width: 64px; | ||||
|     height: 64px; | ||||
| } | ||||
|  | ||||
| .login-dialog-not-listed-button { | ||||
|     padding-top: 2em; | ||||
| } | ||||
| .login-dialog-not-listed-label { | ||||
|     font-size: 14pt; | ||||
|     font-weight: bold; | ||||
|     color: #666666; | ||||
| } | ||||
|  | ||||
| .login-dialog-not-listed-button:hover .login-dialog-not-listed-label { | ||||
|     color: white; | ||||
| } | ||||
|  | ||||
| .login-dialog-prompt-layout { | ||||
|     padding-bottom: 32px; | ||||
| } | ||||
| .login-dialog-prompt-label { | ||||
|     color: white; | ||||
|     font-size: 20pt; | ||||
| } | ||||
|  | ||||
| .login-dialog-prompt-entry { | ||||
|     padding: 4px; | ||||
|     border-radius: 4px; | ||||
|     border: 2px solid #5656cc; | ||||
|     color: black; | ||||
|     background-color: white; | ||||
|     caret-color: black; | ||||
|     caret-size: 1px; | ||||
|     width: 15em; | ||||
| } | ||||
|  | ||||
| .login-dialog-prompt-entry .capslock-warning { | ||||
|     icon-size: 16px; | ||||
|     warning-color: #999; | ||||
| } | ||||
|  | ||||
| .login-dialog-prompt-entry:insensitive { | ||||
|     color: rgba(0,0,0,0.7); | ||||
|     border: 2px solid #565656; | ||||
| } | ||||
|  | ||||
| .login-dialog-session-list { | ||||
|     color: #ffffff; | ||||
|     font-size: 10.5pt; | ||||
| } | ||||
|  | ||||
| .login-dialog-session-list-button { | ||||
|     padding: 4px; | ||||
| } | ||||
|  | ||||
| .login-dialog-session-list-button:focus { | ||||
|     background-color: #4c4c4c; | ||||
| } | ||||
|  | ||||
| .login-dialog-session-list-button:active { | ||||
|     background-color: #4c4c4c; | ||||
| } | ||||
|  | ||||
| .login-dialog-session-list-button:hover { | ||||
|     font-weight: bold; | ||||
| } | ||||
|  | ||||
| .login-dialog-session-list-scroll-view { | ||||
|     background-gradient-start: rgba(80,80,80,0.3); | ||||
|     background-gradient-end: rgba(80,80,80,0.7); | ||||
|     background-gradient-direction: vertical; | ||||
|     box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9); | ||||
|     border-radius: 8px; | ||||
|     border: 1px solid rgba(80,80,80,1.0); | ||||
|     padding: .5em; | ||||
| } | ||||
|  | ||||
| .login-dialog-session-list-item:focus { | ||||
|     background-color: #666666; | ||||
| } | ||||
|  | ||||
| .login-dialog-session-list-triangle { | ||||
|     padding-right: .5em; | ||||
| } | ||||
|  | ||||
| .login-dialog-session-list-item-box { | ||||
|     spacing: .25em; | ||||
| } | ||||
|  | ||||
| .login-dialog-session-list-item-dot { | ||||
|     width: .75em; | ||||
|     height: .75em; | ||||
| } | ||||
| @@ -1,130 +0,0 @@ | ||||
| <?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="300" | ||||
|    height="80" | ||||
|    id="svg7355" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.48.2 r9819" | ||||
|    sodipodi:docname="logged-in-indicator.svg"> | ||||
|   <metadata | ||||
|      id="metadata4175"> | ||||
|     <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="#2c1cff" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1" | ||||
|      objecttolerance="10" | ||||
|      gridtolerance="10" | ||||
|      guidetolerance="10" | ||||
|      inkscape:pageopacity="1" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:window-width="1440" | ||||
|      inkscape:window-height="843" | ||||
|      id="namedview4173" | ||||
|      showgrid="false" | ||||
|      inkscape:zoom="2.8760889" | ||||
|      inkscape:cx="106.00403" | ||||
|      inkscape:cy="80.68078" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-maximized="1" | ||||
|      inkscape:current-layer="g30864" /> | ||||
|   <defs | ||||
|      id="defs7357"> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient36429" | ||||
|        id="radialGradient7461" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="matrix(2.5919312,0,0,0.57582113,-20.687059,48.400487)" | ||||
|        cx="47.428951" | ||||
|        cy="167.16817" | ||||
|        fx="47.428951" | ||||
|        fy="167.16817" | ||||
|        r="37" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient36429"> | ||||
|       <stop | ||||
|          id="stop36431" | ||||
|          offset="0" | ||||
|          style="stop-color:#ffffff;stop-opacity:1;" /> | ||||
|       <stop | ||||
|          id="stop36433" | ||||
|          offset="1" | ||||
|          style="stop-color:#ffffff;stop-opacity:0;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient36471" | ||||
|        id="radialGradient7463" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="matrix(1.1891549,0,0,0.55513246,-9.281289,36.12653)" | ||||
|        cx="49.067139" | ||||
|        cy="242.50381" | ||||
|        fx="49.067139" | ||||
|        fy="242.50381" | ||||
|        r="37.00671" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient36471"> | ||||
|       <stop | ||||
|          id="stop36473" | ||||
|          offset="0" | ||||
|          style="stop-color:#ffffff;stop-opacity:1;" /> | ||||
|       <stop | ||||
|          id="stop36475" | ||||
|          offset="1" | ||||
|          style="stop-color:#ffffff;stop-opacity:0;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        r="37.00671" | ||||
|        fy="242.50381" | ||||
|        fx="49.067139" | ||||
|        cy="242.50381" | ||||
|        cx="49.067139" | ||||
|        gradientTransform="matrix(3.4218418,0,0,0.03365337,-61.309005,138.5071)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient7488" | ||||
|        xlink:href="#linearGradient36471" /> | ||||
|   </defs> | ||||
|   <g | ||||
|      id="layer1" | ||||
|      transform="matrix(1.6213276,0,0,1.6213276,-431.6347,-272.5745)"> | ||||
|     <g | ||||
|        style="display:inline" | ||||
|        id="g30864" | ||||
|        transform="translate(255.223,70.118091)"> | ||||
|       <rect | ||||
|          ry="3.4593496" | ||||
|          rx="8.8641119" | ||||
|          y="76.159348" | ||||
|          x="12.596948" | ||||
|          height="71.116341" | ||||
|          width="182.22595" | ||||
|          id="rect14000" | ||||
|          style="opacity:0.371875;fill:url(#radialGradient7461);fill-opacity:1;stroke:none" /> | ||||
|       <path | ||||
|          id="rect34520" | ||||
|          d="m 194.80022,146.83551 -182.559919,0" | ||||
|          style="opacity:0.35;fill:none;stroke:url(#radialGradient7488);stroke-width:0.61184424;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" | ||||
|          connector-curvature="0" | ||||
|          inkscape:connector-curvature="0" | ||||
|          sodipodi:nodetypes="cc" /> | ||||
|     </g> | ||||
|   </g> | ||||
| </svg> | ||||
| Before Width: | Height: | Size: 3.8 KiB | 
| Before Width: | Height: | Size: 25 KiB | 
| Before Width: | Height: | Size: 78 KiB | 
							
								
								
									
										64
									
								
								data/theme/scroll-hhandle.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,64 @@ | ||||
| <?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="10" | ||||
|    height="4" | ||||
|    id="svg2" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.47 r22583" | ||||
|    sodipodi:docname="scroll-hhandle.svg"> | ||||
|   <defs | ||||
|      id="defs4"> | ||||
|   </defs> | ||||
|   <metadata | ||||
|      id="metadata7"> | ||||
|     <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 | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1"> | ||||
|     <rect | ||||
|        style="fill:#323232;fill-opacity:1;fill-rule:evenodd;stroke:none" | ||||
|        id="rect3592" | ||||
|        width="2" | ||||
|        height="4" | ||||
|        x="0" | ||||
|        y="0" | ||||
|        rx="0" | ||||
|        ry="0" /> | ||||
|     <use | ||||
|        x="0" | ||||
|        y="0" | ||||
|        xlink:href="#rect3592" | ||||
|        id="use2825" | ||||
|        transform="translate(8,0)" | ||||
|        width="10" | ||||
|        height="4" /> | ||||
|     <use | ||||
|        x="0" | ||||
|        y="0" | ||||
|        xlink:href="#use2825" | ||||
|        id="use2827" | ||||
|        transform="translate(-4,0)" | ||||
|        width="10" | ||||
|        height="4" /> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 1.6 KiB | 
							
								
								
									
										62
									
								
								data/theme/scroll-vhandle.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,62 @@ | ||||
| <?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="4" | ||||
|    height="10" | ||||
|    id="svg2" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.47 r22583" | ||||
|    sodipodi:docname="scroll-hhandle.svg"> | ||||
|   <metadata | ||||
|      id="metadata7"> | ||||
|     <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></dc:title> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   <g | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1"> | ||||
|     <rect | ||||
|        style="fill:#323232;fill-opacity:1;fill-rule:evenodd;stroke:none" | ||||
|        id="rect3592" | ||||
|        width="2" | ||||
|        height="4" | ||||
|        x="0" | ||||
|        y="-4" | ||||
|        rx="0" | ||||
|        ry="0" | ||||
|        transform="matrix(0,1,-1,0,0,0)" /> | ||||
|     <use | ||||
|        x="0" | ||||
|        y="0" | ||||
|        xlink:href="#rect3592" | ||||
|        id="use3705" | ||||
|        transform="translate(0,4)" | ||||
|        width="4" | ||||
|        height="10" /> | ||||
|     <use | ||||
|        x="0" | ||||
|        y="0" | ||||
|        xlink:href="#use3705" | ||||
|        id="use3707" | ||||
|        transform="translate(0,4)" | ||||
|        width="4" | ||||
|        height="10" /> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 1.6 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 | 
| Before Width: | Height: | Size: 850 B | 
							
								
								
									
										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 | 
| Before Width: | Height: | Size: 841 B | 
							
								
								
									
										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 | 
| @@ -68,10 +68,6 @@ IGNORE_HFILES=					\ | ||||
| 	gactionobserver.h			\ | ||||
| 	shell-recorder-src.h | ||||
|  | ||||
| if !BUILD_RECORDER | ||||
| IGNORE_HFILES += shell-recorder.h | ||||
| endif | ||||
|  | ||||
| # Images to copy into HTML directory. | ||||
| # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png | ||||
| HTML_IMAGES= | ||||
|   | ||||
| @@ -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 | ||||
| @@ -7,7 +6,9 @@ misc/config.js: misc/config.js.in Makefile | ||||
| 	[ -d $(@D) ] || $(mkdir_p) $(@D) ; \ | ||||
| 	sed -e "s|[@]PACKAGE_NAME@|$(PACKAGE_NAME)|g" \ | ||||
| 	    -e "s|[@]PACKAGE_VERSION@|$(PACKAGE_VERSION)|g" \ | ||||
| 	    -e "s|[@]GJS_VERSION@|$(GJS_VERSION)|g" \ | ||||
| 	    -e "s|[@]HAVE_BLUETOOTH@|$(HAVE_BLUETOOTH)|g" \ | ||||
| 	    -e "s|[@]SHELL_SYSTEM_CA_FILE@|$(SHELL_SYSTEM_CA_FILE)|g" \ | ||||
| 	    -e "s|[@]GETTEXT_PACKAGE@|$(GETTEXT_PACKAGE)|g" \ | ||||
| 	    -e "s|[@]datadir@|$(datadir)|g" \ | ||||
| 	    -e "s|[@]libexecdir@|$(libexecdir)|g" \ | ||||
| @@ -18,58 +19,62 @@ 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/util.js		\ | ||||
| 	gdm/systemd.js	 	\ | ||||
| 	extensionPrefs/main.js	\ | ||||
| 	misc/config.js		\ | ||||
| 	misc/extensionUtils.js	\ | ||||
| 	misc/fileUtils.js	\ | ||||
| 	misc/format.js		\ | ||||
| 	misc/gnomeSession.js	\ | ||||
| 	misc/history.js		\ | ||||
| 	misc/jsParse.js		\ | ||||
| 	misc/loginManager.js	\ | ||||
| 	misc/modemManager.js	\ | ||||
| 	misc/params.js		\ | ||||
| 	misc/screenSaver.js     \ | ||||
| 	misc/util.js		\ | ||||
| 	perf/core.js		\ | ||||
| 	ui/altTab.js		\ | ||||
| 	ui/appDisplay.js	\ | ||||
| 	ui/appFavorites.js	\ | ||||
| 	ui/automountManager.js  \ | ||||
| 	ui/autorunManager.js    \ | ||||
| 	ui/boxpointer.js	\ | ||||
| 	ui/calendar.js		\ | ||||
| 	ui/checkBox.js		\ | ||||
| 	ui/contactDisplay.js \ | ||||
| 	ui/ctrlAltTab.js	\ | ||||
| 	ui/dash.js		\ | ||||
| 	ui/dateMenu.js		\ | ||||
| 	ui/dnd.js		\ | ||||
| 	ui/endSessionDialog.js	\ | ||||
| 	ui/extensionSystem.js	\ | ||||
| 	ui/extensionDownloader.js \ | ||||
| 	ui/environment.js	\ | ||||
| 	ui/extensionSystem.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/link.js		\ | ||||
| 	ui/lookingGlass.js	\ | ||||
| 	ui/magnifier.js		\ | ||||
| 	ui/magnifierDBus.js	\ | ||||
| 	ui/main.js		\ | ||||
| 	ui/messageTray.js	\ | ||||
| 	ui/modalDialog.js	\ | ||||
| 	ui/sessionMode.js	\ | ||||
| 	ui/networkAgent.js	\ | ||||
| 	ui/shellEntry.js	\ | ||||
| 	ui/shellMountOperation.js \ | ||||
| 	ui/notificationDaemon.js \ | ||||
| 	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		\ | ||||
| @@ -78,13 +83,14 @@ nobase_dist_js_DATA = 	\ | ||||
| 	ui/search.js		\ | ||||
| 	ui/searchDisplay.js	\ | ||||
| 	ui/shellDBus.js		\ | ||||
| 	ui/statusIconDispatcher.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,13 +102,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/networkAgent.js		\ | ||||
| 	ui/components/polkitAgent.js		\ | ||||
| 	ui/components/recorder.js		\ | ||||
| 	ui/components/telepathyClient.js	\ | ||||
| 	ui/components/keyring.js		\ | ||||
| 	$(NULL) | ||||
| 	ui/xdndHandler.js | ||||
|   | ||||
| @@ -6,14 +6,15 @@ const GObject = imports.gi.GObject; | ||||
| const Gio = imports.gi.Gio; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Pango = imports.gi.Pango; | ||||
| const Format = imports.format; | ||||
|  | ||||
| const _ = Gettext.gettext; | ||||
|  | ||||
| const Config = imports.misc.config; | ||||
| const Format = imports.misc.format; | ||||
| const ExtensionUtils = imports.misc.extensionUtils; | ||||
|  | ||||
| const GnomeShellIface = <interface name="org.gnome.Shell.Extensions"> | ||||
|  | ||||
| const GnomeShellIface = <interface name="org.gnome.Shell"> | ||||
| <signal name="ExtensionStatusChanged"> | ||||
|     <arg type="s" name="uuid"/> | ||||
|     <arg type="i" name="state"/> | ||||
| @@ -161,7 +162,7 @@ const Application = new Lang.Class({ | ||||
|         vbox.add(toolbar); | ||||
|         let toolitem; | ||||
|  | ||||
|         let label = new Gtk.Label({ label: '<b>' + _("Extension") + '</b>', | ||||
|         let label = new Gtk.Label({ label: _("<b>Extension</b>"), | ||||
|                                     use_markup: true }); | ||||
|         toolitem = new Gtk.ToolItem({ child: label }); | ||||
|         toolbar.add(toolitem); | ||||
| @@ -174,7 +175,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 }); | ||||
| @@ -201,17 +202,23 @@ const Application = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _scanExtensions: function() { | ||||
|         let finder = new ExtensionUtils.ExtensionFinder(); | ||||
|         finder.connect('extension-found', Lang.bind(this, this._extensionFound)); | ||||
|         finder.scanExtensions(); | ||||
|     }, | ||||
|         ExtensionUtils.scanExtensions(Lang.bind(this, function(uuid, dir, type) { | ||||
|             if (ExtensionUtils.extensions[uuid] !== undefined) | ||||
|                 return; | ||||
|  | ||||
|     _extensionFound: function(signals, extension) { | ||||
|         let iter = this._model.append(); | ||||
|         this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]); | ||||
|         this._extensionIters[extension.uuid] = iter; | ||||
|     }, | ||||
|             let extension; | ||||
|             try { | ||||
|                 extension = ExtensionUtils.createExtensionObject(uuid, dir, type); | ||||
|             } catch(e) { | ||||
|                 global.logError('' + e); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             let iter = this._model.append(); | ||||
|             this._model.set(iter, [0, 1], [uuid, extension.metadata.name]); | ||||
|             this._extensionIters[uuid] = iter; | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     _onActivate: function() { | ||||
|         this._window.present(); | ||||
| @@ -250,7 +257,7 @@ function initEnvironment() { | ||||
|         }, | ||||
|  | ||||
|         logError: function(s) { | ||||
|             log('ERROR: ' + s); | ||||
|             global.log('ERROR: ' + s); | ||||
|         }, | ||||
|  | ||||
|         userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell']) | ||||
| @@ -261,6 +268,7 @@ function initEnvironment() { | ||||
|  | ||||
| function main(argv) { | ||||
|     initEnvironment(); | ||||
|     ExtensionUtils.init(); | ||||
|  | ||||
|     Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR); | ||||
|     Gettext.textdomain(Config.GETTEXT_PACKAGE); | ||||
|   | ||||
							
								
								
									
										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,30 +25,88 @@ 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; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
| const Gdm = imports.gi.Gdm; | ||||
| const GdmGreeter = imports.gi.GdmGreeter; | ||||
|  | ||||
| const Batch = imports.gdm.batch; | ||||
| const Fprint = imports.gdm.fingerprint; | ||||
| const GdmUtil = imports.gdm.util; | ||||
| const Lightbox = imports.ui.lightbox; | ||||
| const Main = imports.ui.main; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
| const Tweener = imports.ui.tweener; | ||||
| const UserMenu = imports.ui.userMenu; | ||||
|  | ||||
| const PASSWORD_SERVICE_NAME = 'gdm-password'; | ||||
| const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint'; | ||||
| const _FADE_ANIMATION_TIME = 0.16; | ||||
| const _RESIZE_ANIMATION_TIME = 0.25; | ||||
| const _SCROLL_ANIMATION_TIME = 0.5; | ||||
| const _SCROLL_ANIMATION_TIME = 2.0; | ||||
| const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0; | ||||
| const _LOGO_ICON_NAME_SIZE = 48; | ||||
|  | ||||
| const _LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen'; | ||||
| const _FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication'; | ||||
|  | ||||
| const _LOGO_KEY = 'logo'; | ||||
|  | ||||
| let _loginDialog = null; | ||||
|  | ||||
| function _fadeInActor(actor) { | ||||
|     let hold = new Batch.Hold(); | ||||
|  | ||||
|     if (actor.opacity == 255 && actor.visible) | ||||
|         return null; | ||||
|  | ||||
|     actor.show(); | ||||
|     let [minHeight, naturalHeight] = actor.get_preferred_height(-1); | ||||
|  | ||||
|     actor.opacity = 0; | ||||
|     actor.set_height(0); | ||||
|     Tweener.addTween(actor, | ||||
|                      { opacity: 255, | ||||
|                        height: naturalHeight, | ||||
|                        time: _FADE_ANIMATION_TIME, | ||||
|                        transition: 'easeOutQuad', | ||||
|                        onComplete: function() { | ||||
|                            actor.set_height(-1); | ||||
|                            hold.release(); | ||||
|                        }, | ||||
|                        onCompleteScope: this | ||||
|                      }); | ||||
|     return hold; | ||||
| } | ||||
|  | ||||
| function _fadeOutActor(actor) { | ||||
|     let hold = new Batch.Hold(); | ||||
|  | ||||
|     if (!actor.visible) { | ||||
|         actor.opacity = 0; | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     if (actor.opacity == 0) { | ||||
|         actor.hide(); | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     Tweener.addTween(actor, | ||||
|                      { opacity: 0, | ||||
|                        height: 0, | ||||
|                        time: _FADE_ANIMATION_TIME, | ||||
|                        transition: 'easeOutQuad', | ||||
|                        onComplete: function() { | ||||
|                            actor.hide(); | ||||
|                            actor.set_height(-1); | ||||
|                            hold.release(); | ||||
|                        }, | ||||
|                        onCompleteScope: this | ||||
|                      }); | ||||
|     return hold; | ||||
| } | ||||
|  | ||||
| function _smoothlyResizeActor(actor, width, height) { | ||||
|     let finalWidth; | ||||
|     let finalHeight; | ||||
| @@ -90,72 +148,121 @@ const UserListItem = new Lang.Class({ | ||||
|         this._userChangedId = this.user.connect('changed', | ||||
|                                                  Lang.bind(this, this._onUserChanged)); | ||||
|  | ||||
|         let layout = new St.BoxLayout({ vertical: false }); | ||||
|         this._verticalBox = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-vertical-layout', | ||||
|                                                vertical: true }); | ||||
|  | ||||
|         this.actor = new St.Button({ style_class: 'login-dialog-user-list-item', | ||||
|                                      can_focus: true, | ||||
|                                      child: layout, | ||||
|                                      child: this._verticalBox, | ||||
|                                      reactive: true, | ||||
|                                      x_align: St.Align.START, | ||||
|                                      x_fill: true }); | ||||
|         let layout = new St.BoxLayout({ vertical: false }); | ||||
|  | ||||
|         this._userAvatar = new UserMenu.UserAvatarWidget(this.user, | ||||
|                                                          { styleClass: 'login-dialog-user-list-item-icon' }); | ||||
|         layout.add(this._userAvatar.actor); | ||||
|         this._verticalBox.add(layout, | ||||
|                               { y_fill: true, | ||||
|                                 x_fill: true, | ||||
|                                 expand: true }); | ||||
|  | ||||
|         this._focusBin = new St.Bin({ style_class: 'login-dialog-user-list-item-focus-bin' }); | ||||
|         this._verticalBox.add(this._focusBin, | ||||
|                               { x_fill: false, | ||||
|                                 x_align: St.Align.MIDDLE, | ||||
|                                 y_fill: false, | ||||
|                                 expand: true }); | ||||
|  | ||||
|         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 }); | ||||
|         layout.add(textLayout, | ||||
|                    { y_fill: false, | ||||
|                      y_align: St.Align.MIDDLE, | ||||
|                      expand: true }); | ||||
|  | ||||
|         this._nameLabel = new St.Label({ style_class: 'login-dialog-user-list-item-name' }); | ||||
|         this.actor.label_actor = this._nameLabel; | ||||
|         textLayout.add(this._nameLabel, | ||||
|                        { y_fill: false, | ||||
|                          y_align: St.Align.MIDDLE, | ||||
|                          expand: true }); | ||||
|         this._nameLabel = new St.Label({ text:        this.user.get_real_name(), | ||||
|                                          style_class: 'login-dialog-user-list-item-name' }); | ||||
|         textLayout.add(this._nameLabel); | ||||
|  | ||||
|         this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator', | ||||
|                                                  scale_x: 0 }); | ||||
|         textLayout.add(this._timedLoginIndicator, | ||||
|                        { x_fill: true, | ||||
|                          x_align: St.Align.MIDDLE, | ||||
|                          y_fill: false, | ||||
|                          y_align: St.Align.END }); | ||||
|         this._updateIcon(); | ||||
|  | ||||
|         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._updateLoggedIn(); | ||||
|         this._updateIcon(); | ||||
|     }, | ||||
|  | ||||
|     syncStyleClasses: function() { | ||||
|         this._updateLoggedIn(); | ||||
|     _setIconFromFile: function(iconFile, styleClass) { | ||||
|         if (styleClass) | ||||
|             this._iconBin.set_style_class_name(styleClass); | ||||
|         this._iconBin.set_style(null); | ||||
|  | ||||
|         if (global.stage.get_key_focus() == this.actor) | ||||
|             this.actor.add_style_pseudo_class('focus'); | ||||
|         else | ||||
|             this.actor.remove_style_pseudo_class('focus'); | ||||
|         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(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _updateLoggedIn: function() { | ||||
|         if (this.user.is_logged_in()) | ||||
|             this.actor.add_style_pseudo_class('logged-in'); | ||||
|     _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.actor.remove_style_pseudo_class('logged-in'); | ||||
|             this._setIconFromName('avatar-default', 'login-dialog-user-list-item-icon'); | ||||
|     }, | ||||
|  | ||||
|     _onClicked: function() { | ||||
|         this.emit('activate'); | ||||
|     }, | ||||
|  | ||||
|     showTimedLoginIndicator: function(time) { | ||||
|     fadeOutName: function() { | ||||
|         return _fadeOutActor(this._nameLabel); | ||||
|     }, | ||||
|  | ||||
|     fadeInName: function() { | ||||
|         return _fadeInActor(this._nameLabel); | ||||
|     }, | ||||
|  | ||||
|     showFocusAnimation: function(time) { | ||||
|         let hold = new Batch.Hold(); | ||||
|  | ||||
|         this.hideTimedLoginIndicator(); | ||||
|         Tweener.addTween(this._timedLoginIndicator, | ||||
|                          { scale_x: 1., | ||||
|         let node = this.actor.get_theme_node(); | ||||
|         let padding = node.get_horizontal_padding(); | ||||
|  | ||||
|         let box = this._verticalBox.get_allocation_box(); | ||||
|  | ||||
|         Tweener.removeTweens(this._focusBin); | ||||
|         this._focusBin.width = 0; | ||||
|         Tweener.addTween(this._focusBin, | ||||
|                          { width: (box.x2 - box.x1 - padding), | ||||
|                            time: time, | ||||
|                            transition: 'linear', | ||||
|                            onComplete: function() { | ||||
| @@ -164,11 +271,6 @@ const UserListItem = new Lang.Class({ | ||||
|                            onCompleteScope: this | ||||
|                          }); | ||||
|         return hold; | ||||
|     }, | ||||
|  | ||||
|     hideTimedLoginIndicator: function() { | ||||
|         Tweener.removeTweens(this._timedLoginIndicator); | ||||
|         this._timedLoginIndicator.scale_x = 0.; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(UserListItem.prototype); | ||||
| @@ -182,10 +284,13 @@ const UserList = new Lang.Class({ | ||||
|                               Gtk.PolicyType.AUTOMATIC); | ||||
|  | ||||
|         this._box = new St.BoxLayout({ vertical: true, | ||||
|                                        style_class: 'login-dialog-user-list', | ||||
|                                        pseudo_class: 'expanded' }); | ||||
|                                        style_class: 'login-dialog-user-list' }); | ||||
|  | ||||
|         this.actor.add_actor(this._box); | ||||
|         this.actor.add_actor(this._box, | ||||
|                              { x_fill: true, | ||||
|                                y_fill: true, | ||||
|                                x_align: St.Align.START, | ||||
|                                y_align: St.Align.MIDDLE }); | ||||
|         this._items = {}; | ||||
|  | ||||
|         this.actor.connect('key-focus-in', Lang.bind(this, this._moveFocusToItems)); | ||||
| @@ -200,18 +305,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); | ||||
|                          return _fadeInActor(item.actor); | ||||
|                      }, | ||||
|  | ||||
|                      function() { | ||||
|                          return item.fadeInName(); | ||||
|                      }]; | ||||
|  | ||||
|         let batch = new Batch.ConsecutiveBatch(this, tasks); | ||||
| @@ -264,27 +367,20 @@ const UserList = new Lang.Class({ | ||||
|         for (let userName in this._items) { | ||||
|             let item = this._items[userName]; | ||||
|  | ||||
|             item.actor.set_hover(false); | ||||
|             item.actor.reactive = false; | ||||
|             item.actor.can_focus = false; | ||||
|             item.syncStyleClasses(); | ||||
|             item._timedLoginIndicator.scale_x = 0.; | ||||
|             item._focusBin.width = 0; | ||||
|             if (item != exception) | ||||
|                 tasks.push(function() { | ||||
|                     return GdmUtil.fadeOutActor(item.actor); | ||||
|                     return _fadeOutActor(item.actor); | ||||
|                 }); | ||||
|         } | ||||
|  | ||||
|         let batch = new Batch.ConsecutiveBatch(this, | ||||
|                                                [function() { | ||||
|                                                     return GdmUtil.fadeOutActor(this.actor.vscroll); | ||||
|                                                     return _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(); | ||||
| @@ -325,10 +421,7 @@ const UserList = new Lang.Class({ | ||||
|  | ||||
|         for (let userName in this._items) { | ||||
|             let item = this._items[userName]; | ||||
|             item.actor.sync_hover(); | ||||
|             item.actor.reactive = true; | ||||
|             item.actor.can_focus = true; | ||||
|             item.syncStyleClasses(); | ||||
|             tasks.push(function() { | ||||
|                 return this._showItem(item); | ||||
|             }); | ||||
| @@ -344,10 +437,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() { | ||||
| @@ -355,7 +444,7 @@ const UserList = new Lang.Class({ | ||||
|                                                 }, | ||||
|  | ||||
|                                                 function() { | ||||
|                                                     return GdmUtil.fadeInActor(this.actor.vscroll); | ||||
|                                                     return _fadeInActor(this.actor.vscroll); | ||||
|                                                 }]); | ||||
|         return batch.run(); | ||||
|     }, | ||||
| @@ -370,7 +459,7 @@ const UserList = new Lang.Class({ | ||||
|         Tweener.addTween (adjustment, | ||||
|                           { value: value, | ||||
|                             time: _SCROLL_ANIMATION_TIME, | ||||
|                             transition: 'easeOutQuad' }); | ||||
|                             transition: 'linear' }); | ||||
|     }, | ||||
|  | ||||
|     jumpToItem: function(item) { | ||||
| @@ -422,6 +511,7 @@ const UserList = new Lang.Class({ | ||||
|                            Lang.bind(this, | ||||
|                                      function() { | ||||
|                                          this.scrollToItem(item); | ||||
|                                          item.showFocusAnimation(0); | ||||
|                                      })); | ||||
|  | ||||
|         this._moveFocusToItems(); | ||||
| @@ -463,7 +553,10 @@ const SessionListItem = new Lang.Class({ | ||||
|  | ||||
|         this._box = new St.BoxLayout({ style_class: 'login-dialog-session-list-item-box' }); | ||||
|  | ||||
|         this.actor.add_actor(this._box); | ||||
|         this.actor.add_actor(this._box, | ||||
|                              { expand: true, | ||||
|                                x_fill: true, | ||||
|                                y_fill: true }); | ||||
|         this.actor.connect('clicked', Lang.bind(this, this._onClicked)); | ||||
|  | ||||
|         this._dot = new St.DrawingArea({ style_class: 'login-dialog-session-list-item-dot' }); | ||||
| @@ -473,9 +566,11 @@ 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); | ||||
|         this._box.add_actor(label, | ||||
|                             { expand: true, | ||||
|                               x_fill: true, | ||||
|                               y_fill: true }); | ||||
|     }, | ||||
|  | ||||
|     setShowDot: function(show) { | ||||
| @@ -519,7 +614,10 @@ const SessionList = new Lang.Class({ | ||||
|                                        x_fill: true, | ||||
|                                        y_fill: true }); | ||||
|         let box = new St.BoxLayout(); | ||||
|         this._button.add_actor(box); | ||||
|         this._button.add_actor(box, | ||||
|                                { x_fill: true, | ||||
|                                  y_fill: true, | ||||
|                                  expand: true }); | ||||
|  | ||||
|         this._triangle = new St.Label({ style_class: 'login-dialog-session-list-triangle', | ||||
|                                         text: '\u25B8' }); | ||||
| @@ -527,18 +625,30 @@ const SessionList = new Lang.Class({ | ||||
|  | ||||
|         let label = new St.Label({ style_class: 'login-dialog-session-list-label', | ||||
|                                    text: _("Session...") }); | ||||
|         box.add_actor(label); | ||||
|         box.add_actor(label, | ||||
|                       { x_fill: true, | ||||
|                         y_fill: true, | ||||
|                         expand: true }); | ||||
|  | ||||
|         this._button.connect('clicked', | ||||
|                              Lang.bind(this, this._onClicked)); | ||||
|         this._box.add_actor(this._button); | ||||
|         this._box.add_actor(this._button, | ||||
|                             { x_fill: true, | ||||
|                               y_fill: true, | ||||
|                               expand: true }); | ||||
|         this._scrollView = new St.ScrollView({ style_class: 'login-dialog-session-list-scroll-view'}); | ||||
|         this._scrollView.set_policy(Gtk.PolicyType.NEVER, | ||||
|                                     Gtk.PolicyType.AUTOMATIC); | ||||
|         this._box.add_actor(this._scrollView); | ||||
|         this._box.add_actor(this._scrollView, | ||||
|                             { x_fill: true, | ||||
|                               y_fill: true, | ||||
|                               expand: true }); | ||||
|         this._itemList = new St.BoxLayout({ style_class: 'login-dialog-session-item-list', | ||||
|                                             vertical: true }); | ||||
|         this._scrollView.add_actor(this._itemList); | ||||
|         this._scrollView.add_actor(this._itemList, | ||||
|                                    { x_fill: true, | ||||
|                                      y_fill: true, | ||||
|                                      expand: true }); | ||||
|         this._scrollView.hide(); | ||||
|         this.isOpen = false; | ||||
|         this._populate(); | ||||
| @@ -591,7 +701,7 @@ const SessionList = new Lang.Class({ | ||||
|         this._activeSessionId = null; | ||||
|         this._items = {}; | ||||
|  | ||||
|         let ids = Gdm.get_session_ids(); | ||||
|         let ids = GdmGreeter.get_session_ids(); | ||||
|         ids.sort(); | ||||
|  | ||||
|         if (ids.length <= 1) { | ||||
| @@ -603,10 +713,14 @@ const SessionList = new Lang.Class({ | ||||
|         } | ||||
|  | ||||
|         for (let i = 0; i < ids.length; i++) { | ||||
|             let [sessionName, sessionDescription] = Gdm.get_session_name_and_description(ids[i]); | ||||
|             let [sessionName, sessionDescription] = GdmGreeter.get_session_name_and_description(ids[i]); | ||||
|  | ||||
|             let item = new SessionListItem(ids[i], sessionName); | ||||
|             this._itemList.add_actor(item.actor); | ||||
|             this._itemList.add_actor(item.actor, | ||||
|                               { x_align: St.Align.START, | ||||
|                                 y_align: St.Align.START, | ||||
|                                 x_fill: true, | ||||
|                                 y_fill: true }); | ||||
|             this._items[ids[i]] = item; | ||||
|  | ||||
|             if (!this._activeSessionId) | ||||
| @@ -625,54 +739,48 @@ const LoginDialog = new Lang.Class({ | ||||
|     Name: 'LoginDialog', | ||||
|     Extends: ModalDialog.ModalDialog, | ||||
|  | ||||
|     _init: function(parentActor) { | ||||
|         this.parent({ shellReactive: true, | ||||
|                       styleClass: 'login-dialog', | ||||
|                       parentActor: parentActor, | ||||
|                       shouldFadeIn: false }); | ||||
|     _init: function() { | ||||
|         this.parent({ shellReactive: true, styleClass: 'login-dialog' }); | ||||
|         this.connect('destroy', | ||||
|                      Lang.bind(this, this._onDestroy)); | ||||
|         this.connect('opened', | ||||
|                      Lang.bind(this, this._onOpened)); | ||||
|  | ||||
|         this._userManager = AccountsService.UserManager.get_default() | ||||
|         this._greeterClient = new Gdm.Client(); | ||||
|         this._greeterClient = GdmGreeter.Server.new_for_greeter_sync(null); | ||||
|  | ||||
|         this._greeter = this._greeterClient.get_greeter_sync(null); | ||||
|         this._greeterClient.call_start_conversation_sync(PASSWORD_SERVICE_NAME, null); | ||||
|  | ||||
|         this._greeter.connect('default-session-name-changed', | ||||
|                               Lang.bind(this, this._onDefaultSessionChanged)); | ||||
|         this._greeterClient.connect('reset', | ||||
|                                     Lang.bind(this, this._onReset)); | ||||
|         this._greeterClient.connect('default-session-name-changed', | ||||
|                                     Lang.bind(this, this._onDefaultSessionChanged)); | ||||
|         this._greeterClient.connect('info', | ||||
|                                     Lang.bind(this, this._onInfo)); | ||||
|         this._greeterClient.connect('problem', | ||||
|                                     Lang.bind(this, this._onProblem)); | ||||
|         this._greeterClient.connect('info-query', | ||||
|                                     Lang.bind(this, this._onInfoQuery)); | ||||
|         this._greeterClient.connect('secret-info-query', | ||||
|                                     Lang.bind(this, this._onSecretInfoQuery)); | ||||
|         this._greeterClient.connect('session-opened', | ||||
|                                     Lang.bind(this, this._onSessionOpened)); | ||||
|         this._greeterClient.connect('timed-login-requested', | ||||
|                                     Lang.bind(this, this._onTimedLoginRequested)); | ||||
|         this._greeterClient.connect('conversation-stopped', | ||||
|                                     Lang.bind(this, this._onConversationStopped)); | ||||
|  | ||||
|         this._greeter.connect('session-opened', | ||||
|                               Lang.bind(this, this._onSessionOpened)); | ||||
|         this._greeter.connect('timed-login-requested', | ||||
|                               Lang.bind(this, this._onTimedLoginRequested)); | ||||
|         this._settings = new Gio.Settings({ schema: _LOGIN_SCREEN_SCHEMA }); | ||||
|  | ||||
|         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._onReset)); | ||||
|         this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint)); | ||||
|         this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint)); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA }); | ||||
|  | ||||
|         this._settings.connect('changed::' + GdmUtil.LOGO_KEY, | ||||
|         this._fprintManager = new Fprint.FprintManager(); | ||||
|         this._startFingerprintConversationIfNeeded(); | ||||
|         this._settings.connect('changed::' + _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._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: '' }); | ||||
|         this.contentLayout.add(this._bannerLabel); | ||||
|         this._updateBanner(); | ||||
|  | ||||
|         this._titleLabel = new St.Label({ style_class: 'login-dialog-title', | ||||
|                                           text: C_("title", "Sign In") }); | ||||
|  | ||||
| @@ -680,23 +788,31 @@ const LoginDialog = new Lang.Class({ | ||||
|                               { 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, | ||||
| @@ -709,18 +825,17 @@ const LoginDialog = new Lang.Class({ | ||||
|                               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', | ||||
|                                   Lang.bind(this, function(list, sessionId) { | ||||
|                                                 this._greeter.call_select_session_sync (sessionId, null); | ||||
|                                                 this._greeterClient.call_select_session (sessionId); | ||||
|                                             })); | ||||
|  | ||||
|         this._promptBox.add(this._sessionList.actor, | ||||
| @@ -768,9 +883,25 @@ const LoginDialog = new Lang.Class({ | ||||
|  | ||||
|    }, | ||||
|  | ||||
|    _startFingerprintConversationIfNeeded: function() { | ||||
|         this._haveFingerprintReader = false; | ||||
|  | ||||
|         if (!this._settings.get_boolean(_FINGERPRINT_AUTHENTICATION_KEY)) | ||||
|             return; | ||||
|  | ||||
|         this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, Lang.bind(this, | ||||
|             function(device, error) { | ||||
|                 if (!error && device) | ||||
|                     this._haveFingerprintReader = true; | ||||
|  | ||||
|                 if (this._haveFingerprintReader) | ||||
|                     this._greeterClient.call_start_conversation_sync(FINGERPRINT_SERVICE_NAME, null); | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
|     _updateLogo: function() { | ||||
|         this._logoBox.child = null; | ||||
|         let path = this._settings.get_string(GdmUtil.LOGO_KEY); | ||||
|         let path = this._settings.get_string(_LOGO_KEY); | ||||
|  | ||||
|         if (path) { | ||||
|             let file = Gio.file_new_for_path(path); | ||||
| @@ -782,20 +913,9 @@ const LoginDialog = new Lang.Class({ | ||||
|  | ||||
|     }, | ||||
|  | ||||
|     _updateBanner: function() { | ||||
|         let enabled = this._settings.get_boolean(GdmUtil.BANNER_MESSAGE_KEY); | ||||
|         let text = this._settings.get_string(GdmUtil.BANNER_MESSAGE_TEXT_KEY); | ||||
|  | ||||
|         if (enabled && text) { | ||||
|             this._bannerLabel.set_text(text); | ||||
|             this._fadeInBanner(); | ||||
|         } else { | ||||
|             this._fadeOutBanner(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onReset: function(client, serviceName) { | ||||
|         this._promptMessage.hide(); | ||||
|         this._greeterClient.call_start_conversation_sync(PASSWORD_SERVICE_NAME, null); | ||||
|         this._startFingerprintConversationIfNeeded(); | ||||
|  | ||||
|         let tasks = [this._hidePrompt, | ||||
|  | ||||
| @@ -805,7 +925,7 @@ const LoginDialog = new Lang.Class({ | ||||
|  | ||||
|                      function() { | ||||
|                          this._sessionList.close(); | ||||
|                          this._promptLoginHint.hide(); | ||||
|                          this._promptFingerprintMessage.hide(); | ||||
|                          this._userList.actor.show(); | ||||
|                          this._userList.actor.opacity = 255; | ||||
|                          return this._userList.showItems(); | ||||
| @@ -826,51 +946,61 @@ const LoginDialog = new Lang.Class({ | ||||
|         this._sessionList.setActiveSession(sessionId); | ||||
|     }, | ||||
|  | ||||
|     _showMessage: function(userVerifier, message, styleClass) { | ||||
|         this._promptMessage.text = message; | ||||
|         this._promptMessage.styleClass = styleClass; | ||||
|         GdmUtil.fadeInActor(this._promptMessage); | ||||
|     _onInfo: function(client, serviceName, info) { | ||||
|         // We don't display fingerprint messages, because they | ||||
|         // have words like UPEK in them. Instead we use the messages | ||||
|         // as a cue to display our own message. | ||||
|         if (serviceName == FINGERPRINT_SERVICE_NAME && | ||||
|             this._haveFingerprintReader && | ||||
|             (!this._promptFingerprintMessage.visible || | ||||
|              this._promptFingerprintMessage.opacity != 255)) { | ||||
|  | ||||
|             _fadeInActor(this._promptFingerprintMessage); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (serviceName != PASSWORD_SERVICE_NAME) | ||||
|             return; | ||||
|         Main.notifyError(info); | ||||
|     }, | ||||
|  | ||||
|     _showLoginHint: function(verifier, message) { | ||||
|         this._promptLoginHint.set_text(message) | ||||
|         GdmUtil.fadeInActor(this._promptLoginHint); | ||||
|     _onProblem: function(client, serviceName, problem) { | ||||
|         // we don't want to show auth failed messages to | ||||
|         // users who haven't enrolled their fingerprint. | ||||
|         if (serviceName != PASSWORD_SERVICE_NAME) | ||||
|             return; | ||||
|         Main.notifyError(problem); | ||||
|     }, | ||||
|  | ||||
|     _hideLoginHint: function() { | ||||
|         GdmUtil.fadeOutActor(this._promptLoginHint); | ||||
|         this._promptLoginHint.set_text(''); | ||||
|     }, | ||||
|  | ||||
|     cancel: function() { | ||||
|         this._userVerifier.cancel(); | ||||
|     _onCancel: function(client) { | ||||
|         this._greeterClient.call_cancel_sync(null); | ||||
|     }, | ||||
|  | ||||
|     _fadeInPrompt: function() { | ||||
|         let tasks = [function() { | ||||
|                          return GdmUtil.fadeInActor(this._promptLabel); | ||||
|                          return _fadeInActor(this._promptLabel); | ||||
|                      }, | ||||
|  | ||||
|                      function() { | ||||
|                          return GdmUtil.fadeInActor(this._promptEntry); | ||||
|                          return _fadeInActor(this._promptEntry); | ||||
|                      }, | ||||
|  | ||||
|                      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() { | ||||
|                          return GdmUtil.fadeInActor(this._promptBox); | ||||
|                          return _fadeInActor(this._promptBox); | ||||
|                      }, | ||||
|  | ||||
|                      function() { | ||||
|                          if (this._user && this._user.is_logged_in()) | ||||
|                              return null; | ||||
|  | ||||
|                          return GdmUtil.fadeInActor(this._sessionList.actor); | ||||
|                          return _fadeInActor(this._sessionList.actor); | ||||
|                      }, | ||||
|  | ||||
|                      function() { | ||||
| @@ -885,14 +1015,22 @@ const LoginDialog = new Lang.Class({ | ||||
|     _showPrompt: function() { | ||||
|         let hold = new Batch.Hold(); | ||||
|  | ||||
|         let buttons = [{ action: Lang.bind(this, this.cancel), | ||||
|         let buttons = [{ action: Lang.bind(this, this._onCancel), | ||||
|                          label: _("Cancel"), | ||||
|                          key: Clutter.Escape }, | ||||
|                        { action: Lang.bind(this, function() { | ||||
|                                      hold.release(); | ||||
|                                  }), | ||||
|                          label: C_("button", "Sign In"), | ||||
|                          default: true }]; | ||||
|                          label: C_("button", "Sign In") }]; | ||||
|  | ||||
|         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(); | ||||
| @@ -910,15 +1048,21 @@ const LoginDialog = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _hidePrompt: function() { | ||||
|         if (this._promptEntryActivateCallbackId) { | ||||
|             this._promptEntry.clutter_text.disconnect(this._promptEntryActivateCallbackId); | ||||
|             this._promptEntryActivateCallbackId = null; | ||||
|         } | ||||
|  | ||||
|         this.setButtons([]); | ||||
|  | ||||
|         let tasks = [function() { | ||||
|                          return GdmUtil.fadeOutActor(this._promptBox); | ||||
|                          return _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(''); | ||||
|                      }]; | ||||
|  | ||||
| @@ -927,26 +1071,43 @@ const LoginDialog = new Lang.Class({ | ||||
|         return batch.run(); | ||||
|     }, | ||||
|  | ||||
|     _askQuestion: function(verifier, serviceName, question, passwordChar) { | ||||
|     _askQuestion: function(serviceName, question) { | ||||
|         this._promptLabel.set_text(question); | ||||
|  | ||||
|         this._promptEntry.set_text(''); | ||||
|         this._promptEntry.clutter_text.set_password_char(passwordChar); | ||||
|  | ||||
|         let tasks = [this._showPrompt, | ||||
|  | ||||
|                      function() { | ||||
|                          let _text = this._promptEntry.get_text(); | ||||
|                          this._promptEntry.reactive = false; | ||||
|                          this._userVerifier.answerQuery(serviceName, _text); | ||||
|                          this._promptEntry.add_style_pseudo_class('insensitive'); | ||||
|                          this._greeterClient.call_answer_query_sync(serviceName, _text, null); | ||||
|                      }]; | ||||
|  | ||||
|         let batch = new Batch.ConsecutiveBatch(this, tasks); | ||||
|         return batch.run(); | ||||
|     }, | ||||
|     _onInfoQuery: function(client, serviceName, question) { | ||||
|         // We only expect questions to come from the main auth service | ||||
|         if (serviceName != PASSWORD_SERVICE_NAME) | ||||
|             return; | ||||
|  | ||||
|         this._promptEntry.set_text(''); | ||||
|         this._promptEntry.clutter_text.set_password_char(''); | ||||
|         this._askQuestion(serviceName, question); | ||||
|     }, | ||||
|  | ||||
|     _onSecretInfoQuery: function(client, serviceName, secretQuestion) { | ||||
|         // We only expect secret requests to come from the main auth service | ||||
|         if (serviceName != PASSWORD_SERVICE_NAME) | ||||
|             return; | ||||
|  | ||||
|         this._promptEntry.set_text(''); | ||||
|         this._promptEntry.clutter_text.set_password_char('\u25cf'); | ||||
|         this._askQuestion(serviceName, secretQuestion); | ||||
|     }, | ||||
|  | ||||
|     _onSessionOpened: function(client, serviceName) { | ||||
|         this._greeter.call_start_session_when_ready_sync(serviceName, true, null); | ||||
|         this._greeterClient.call_start_session_when_ready_sync(serviceName, true, null); | ||||
|     }, | ||||
|  | ||||
|     _waitForItemForUser: function(userName) { | ||||
| @@ -973,7 +1134,7 @@ const LoginDialog = new Lang.Class({ | ||||
|  | ||||
|     _showTimedLoginAnimation: function() { | ||||
|         this._timedLoginItem.actor.grab_key_focus(); | ||||
|         return this._timedLoginItem.showTimedLoginIndicator(this._timedLoginAnimationTime); | ||||
|         return this._timedLoginItem.showFocusAnimation(this._timedLoginAnimationTime); | ||||
|     }, | ||||
|  | ||||
|     _blockTimedLoginUntilIdle: function() { | ||||
| @@ -1014,6 +1175,7 @@ const LoginDialog = new Lang.Class({ | ||||
|                          // item. | ||||
|                          if (!this.is_loaded) { | ||||
|                              this._userList.jumpToItem(this._timedLoginItem); | ||||
|                              this._timedLoginItem.showFocusAnimation(0); | ||||
|                          } | ||||
|                      }, | ||||
|  | ||||
| @@ -1027,7 +1189,7 @@ const LoginDialog = new Lang.Class({ | ||||
|  | ||||
|                      function() { | ||||
|                          this._timedLoginBatch = null; | ||||
|                          this._greeter.call_begin_auto_login_sync(userName, null); | ||||
|                          this._greeterClient.call_begin_auto_login_sync(userName, null); | ||||
|                      }]; | ||||
|  | ||||
|         this._timedLoginBatch = new Batch.ConsecutiveBatch(this, tasks); | ||||
| @@ -1041,9 +1203,6 @@ const LoginDialog = new Lang.Class({ | ||||
|             this._timedLoginBatch = null; | ||||
|         } | ||||
|  | ||||
|         if (this._timedLoginItem) | ||||
|             this._timedLoginItem.hideTimedLoginIndicator(); | ||||
|  | ||||
|         let userName = this._timedLoginItem.user.get_user_name(); | ||||
|  | ||||
|         if (userName) | ||||
| @@ -1073,6 +1232,17 @@ const LoginDialog = new Lang.Class({ | ||||
|                              })); | ||||
|     }, | ||||
|  | ||||
|     _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._greeterClient.call_cancel_sync(null); | ||||
|         } else if (serviceName == FINGERPRINT_SERVICE_NAME) { | ||||
|             _fadeOutActor(this._promptFingerprintMessage); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onNotListedClicked: function(user) { | ||||
|         let tasks = [function() { | ||||
|                          return this._userList.hideItems(); | ||||
| @@ -1091,10 +1261,7 @@ const LoginDialog = new Lang.Class({ | ||||
|                                                       this._fadeOutLogo]), | ||||
|  | ||||
|                      function() { | ||||
|                          let hold = new Batch.Hold(); | ||||
|  | ||||
|                          this._userVerifier.begin(null, hold); | ||||
|                          return hold; | ||||
|                          this._greeterClient.call_begin_verification_sync(PASSWORD_SERVICE_NAME, null); | ||||
|                      }]; | ||||
|  | ||||
|         let batch = new Batch.ConsecutiveBatch(this, tasks); | ||||
| @@ -1102,47 +1269,30 @@ const LoginDialog = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _fadeInLogo: function() { | ||||
|         return GdmUtil.fadeInActor(this._logoBox); | ||||
|         return _fadeInActor(this._logoBox); | ||||
|     }, | ||||
|  | ||||
|     _fadeOutLogo: function() { | ||||
|         return GdmUtil.fadeOutActor(this._logoBox); | ||||
|     }, | ||||
|  | ||||
|     _fadeInBanner: function() { | ||||
|         return GdmUtil.fadeInActor(this._bannerLabel); | ||||
|     }, | ||||
|  | ||||
|     _fadeOutBanner: function() { | ||||
|         return GdmUtil.fadeOutActor(this._bannerLabel); | ||||
|         return _fadeOutActor(this._logoBox); | ||||
|     }, | ||||
|  | ||||
|     _fadeInTitleLabel: function() { | ||||
|         return GdmUtil.fadeInActor(this._titleLabel); | ||||
|         return _fadeInActor(this._titleLabel); | ||||
|     }, | ||||
|  | ||||
|     _fadeOutTitleLabel: function() { | ||||
|         return GdmUtil.fadeOutActor(this._titleLabel); | ||||
|         return _fadeOutActor(this._titleLabel); | ||||
|     }, | ||||
|  | ||||
|     _fadeInNotListedButton: function() { | ||||
|         return GdmUtil.fadeInActor(this._notListedButton); | ||||
|         return _fadeInActor(this._notListedButton); | ||||
|     }, | ||||
|  | ||||
|     _fadeOutNotListedButton: function() { | ||||
|         return GdmUtil.fadeOutActor(this._notListedButton); | ||||
|     }, | ||||
|  | ||||
|     _beginVerificationForUser: function(userName) { | ||||
|         let hold = new Batch.Hold(); | ||||
|  | ||||
|         this._userVerifier.begin(userName, hold); | ||||
|         return hold; | ||||
|         return _fadeOutActor(this._notListedButton); | ||||
|     }, | ||||
|  | ||||
|     _onUserListActivated: function(activatedItem) { | ||||
|         let userName; | ||||
|  | ||||
|         let tasks = [function() { | ||||
|                          this._userList.actor.reactive = false; | ||||
|                          return this._userList.pinInPlace(); | ||||
| @@ -1156,6 +1306,10 @@ const LoginDialog = new Lang.Class({ | ||||
|                          return this._userList.giveUpWhitespace(); | ||||
|                      }, | ||||
|  | ||||
|                      function() { | ||||
|                          return activatedItem.fadeOutName(); | ||||
|                      }, | ||||
|  | ||||
|                      new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel, | ||||
|                                                       this._fadeOutNotListedButton, | ||||
|                                                       this._fadeOutLogo]), | ||||
| @@ -1165,9 +1319,12 @@ const LoginDialog = new Lang.Class({ | ||||
|                      }, | ||||
|  | ||||
|                      function() { | ||||
|                          userName = activatedItem.user.get_user_name(); | ||||
|                          let userName = activatedItem.user.get_user_name(); | ||||
|                          this._greeterClient.call_begin_verification_for_user_sync(PASSWORD_SERVICE_NAME, | ||||
|                                                                                    userName, null); | ||||
|  | ||||
|                          return this._beginVerificationForUser(userName); | ||||
|                          if (this._haveFingerprintReader) | ||||
|                              this._greeterClient.call_begin_verification_for_user_sync(_FINGERPRINT_SERVICE_NAME, userName, null); | ||||
|                      }]; | ||||
|  | ||||
|         this._user = activatedItem.user; | ||||
| @@ -1211,7 +1368,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 }); | ||||
|   | ||||
| @@ -21,7 +21,8 @@ | ||||
| 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 PanelMenu = imports.ui.panelMenu; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| @@ -31,11 +32,13 @@ 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(); | ||||
|         if (Systemd.haveSystemd()) | ||||
|             this._systemdLoginManager = new Systemd.SystemdLoginManager(); | ||||
|         else | ||||
|             this._consoleKitManager = new ConsoleKit.ConsoleKitManager(); | ||||
|  | ||||
|         this._createSubMenu(); | ||||
|  | ||||
| @@ -62,19 +65,57 @@ const PowerMenuButton = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _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 != '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; | ||||
|                     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 != '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; | ||||
|                     else | ||||
|                         this._haveRestart = false; | ||||
|  | ||||
|                     this._restartItem.actor.visible = this._haveRestart; | ||||
|                     this._updateVisibility(); | ||||
|                 })); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _updateHaveSuspend: function() { | ||||
| @@ -111,13 +152,19 @@ const PowerMenuButton = new Lang.Class({ | ||||
|         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
									
									
									
									
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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; | ||||
| } | ||||
							
								
								
									
										369
									
								
								js/gdm/util.js
									
									
									
									
									
								
							
							
						
						| @@ -1,369 +0,0 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| 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; | ||||
|  | ||||
| const PASSWORD_SERVICE_NAME = 'gdm-password'; | ||||
| const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint'; | ||||
| const FADE_ANIMATION_TIME = 0.16; | ||||
|  | ||||
| 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'; | ||||
|  | ||||
| function fadeInActor(actor) { | ||||
|     if (actor.opacity == 255 && actor.visible) | ||||
|         return null; | ||||
|  | ||||
|     let hold = new Batch.Hold(); | ||||
|     actor.show(); | ||||
|     let [minHeight, naturalHeight] = actor.get_preferred_height(-1); | ||||
|  | ||||
|     actor.opacity = 0; | ||||
|     actor.set_height(0); | ||||
|     Tweener.addTween(actor, | ||||
|                      { opacity: 255, | ||||
|                        height: naturalHeight, | ||||
|                        time: FADE_ANIMATION_TIME, | ||||
|                        transition: 'easeOutQuad', | ||||
|                        onComplete: function() { | ||||
|                            this.set_height(-1); | ||||
|                            hold.release(); | ||||
|                        }, | ||||
|                      }); | ||||
|  | ||||
|     return hold; | ||||
| } | ||||
|  | ||||
| function fadeOutActor(actor) { | ||||
|     if (!actor.visible || actor.opacity == 0) { | ||||
|         actor.opacity = 0; | ||||
|         actor.hide(); | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     let hold = new Batch.Hold(); | ||||
|     Tweener.addTween(actor, | ||||
|                      { opacity: 0, | ||||
|                        height: 0, | ||||
|                        time: FADE_ANIMATION_TIME, | ||||
|                        transition: 'easeOutQuad', | ||||
|                        onComplete: function() { | ||||
|                            this.hide(); | ||||
|                            this.set_height(-1); | ||||
|                            hold.release(); | ||||
|                        }, | ||||
|                      }); | ||||
|     return hold; | ||||
| } | ||||
|  | ||||
| const ShellUserVerifier = new Lang.Class({ | ||||
|     Name: 'ShellUserVerifier', | ||||
|  | ||||
|     _init: function(client, params) { | ||||
|         params = Params.parse(params, { reauthenticationOnly: false }); | ||||
|         this._reauthOnly = params.reauthenticationOnly; | ||||
|  | ||||
|         this._client = client; | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA }); | ||||
|  | ||||
|         this._fprintManager = new Fprint.FprintManager(); | ||||
|         this._realmManager = new Realmd.Manager(); | ||||
|  | ||||
|         this._failCounter = 0; | ||||
|     }, | ||||
|  | ||||
|     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 | ||||
|             this._client.open_reauthentication_channel(userName, this._cancellable, | ||||
|                                                        Lang.bind(this, this._reauthenticationChannelOpened)); | ||||
|         } else { | ||||
|             this._client.get_user_verifier(this._cancellable, Lang.bind(this, this._userVerifierGot)); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     cancel: function() { | ||||
|         if (this._cancellable) | ||||
|             this._cancellable.cancel(); | ||||
|  | ||||
|         if (this._userVerifier) | ||||
|             this._userVerifier.call_cancel_sync(null); | ||||
|     }, | ||||
|  | ||||
|     clear: function() { | ||||
|         if (this._cancellable) { | ||||
|             this._cancellable.cancel(); | ||||
|             this._cancellable = null; | ||||
|         } | ||||
|  | ||||
|         if (this._userVerifier) { | ||||
|             this._userVerifier.run_dispose(); | ||||
|             this._userVerifier = null; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     answerQuery: function(serviceName, answer) { | ||||
|         this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null); | ||||
|     }, | ||||
|  | ||||
|     _checkForFingerprintReader: function() { | ||||
|         this._haveFingerprintReader = false; | ||||
|  | ||||
|         if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY)) | ||||
|             return; | ||||
|  | ||||
|         this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, this._cancellable, Lang.bind(this, | ||||
|             function(device, error) { | ||||
|                 if (!error && device) | ||||
|                     this._haveFingerprintReader = true; | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
|     _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._hold.release(); | ||||
|     }, | ||||
|  | ||||
|     _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._connectSignals(); | ||||
|         this._beginVerification(); | ||||
|         this._hold.release(); | ||||
|     }, | ||||
|  | ||||
|     _connectSignals: function() { | ||||
|         this._userVerifier.connect('info', Lang.bind(this, this._onInfo)); | ||||
|         this._userVerifier.connect('problem', Lang.bind(this, this._onProblem)); | ||||
|         this._userVerifier.connect('info-query', Lang.bind(this, this._onInfoQuery)); | ||||
|         this._userVerifier.connect('secret-info-query', Lang.bind(this, this._onSecretInfoQuery)); | ||||
|         this._userVerifier.connect('conversation-stopped', Lang.bind(this, this._onConversationStopped)); | ||||
|         this._userVerifier.connect('reset', Lang.bind(this, this._onReset)); | ||||
|         this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete)); | ||||
|     }, | ||||
|  | ||||
|     _beginVerification: function() { | ||||
|         this._hold.acquire(); | ||||
|  | ||||
|         if (this._userName) { | ||||
|             this._userVerifier.call_begin_verification_for_user(PASSWORD_SERVICE_NAME, | ||||
|                                                                 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; | ||||
|                 } | ||||
|  | ||||
|                 this._hold.release(); | ||||
|             })); | ||||
|  | ||||
|             if (this._haveFingerprintReader) { | ||||
|                 this._hold.acquire(); | ||||
|  | ||||
|                 this._userVerifier.call_begin_verification_for_user(FINGERPRINT_SERVICE_NAME, | ||||
|                                                                     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; | ||||
|                     } | ||||
|  | ||||
|                     this._hold.release(); | ||||
|                 })); | ||||
|             } | ||||
|         } else { | ||||
|             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; | ||||
|                 } | ||||
|  | ||||
|                 this._hold.release(); | ||||
|             })); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onInfo: function(client, serviceName, info) { | ||||
|         // We don't display fingerprint messages, because they | ||||
|         // have words like UPEK in them. Instead we use the messages | ||||
|         // 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)")); | ||||
|         } else if (serviceName == PASSWORD_SERVICE_NAME) { | ||||
|             this.emit('show-message', info, 'login-dialog-message-info'); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onProblem: function(client, serviceName, problem) { | ||||
|         // we don't want to show auth failed messages to | ||||
|         // 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)); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onInfoQuery: function(client, serviceName, question) { | ||||
|         // We only expect questions to come from the main auth service | ||||
|         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, ''); | ||||
|     }, | ||||
|  | ||||
|     _onSecretInfoQuery: function(client, serviceName, secretQuestion) { | ||||
|         // We only expect secret requests to come from the main auth service | ||||
|         if (serviceName != PASSWORD_SERVICE_NAME) | ||||
|             return; | ||||
|  | ||||
|         this.emit('ask-question', serviceName, secretQuestion, '\u25cf'); | ||||
|     }, | ||||
|  | ||||
|     _onReset: function() { | ||||
|         this.clear(); | ||||
|  | ||||
|         // Clear previous attempts to authenticate | ||||
|         this._failCounter = 0; | ||||
|  | ||||
|         this.emit('reset'); | ||||
|     }, | ||||
|  | ||||
|     _onVerificationComplete: function() { | ||||
|         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. | ||||
|  | ||||
|         let canRetry = retry && this._userName && | ||||
|             this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY); | ||||
|  | ||||
|         if (canRetry) { | ||||
|             this._failCounter++; | ||||
|  | ||||
|             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; | ||||
|         } | ||||
|     }, | ||||
| }); | ||||
| Signals.addSignalMethods(ShellUserVerifier.prototype); | ||||
| @@ -4,8 +4,12 @@ | ||||
| const PACKAGE_NAME = '@PACKAGE_NAME@'; | ||||
| /* The version of this package */ | ||||
| const PACKAGE_VERSION = '@PACKAGE_VERSION@'; | ||||
| /* The version of GJS we're linking to */ | ||||
| const GJS_VERSION = '@GJS_VERSION@'; | ||||
| /* 1 if gnome-bluetooth is available, 0 otherwise */ | ||||
| const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@; | ||||
| /* The system TLS CA list */ | ||||
| const SHELL_SYSTEM_CA_FILE = '@SHELL_SYSTEM_CA_FILE@'; | ||||
| /* gettext package */ | ||||
| const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@'; | ||||
| /* locale dir */ | ||||
|   | ||||
| @@ -3,9 +3,6 @@ | ||||
| // Common utils for the extension system and the extension | ||||
| // preferences tool | ||||
|  | ||||
| const Lang = imports.lang; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gio = imports.gi.Gio; | ||||
| const ShellJS = imports.gi.ShellJS; | ||||
| @@ -17,6 +14,9 @@ const ExtensionType = { | ||||
|     PER_USER: 2 | ||||
| }; | ||||
|  | ||||
| // GFile for user extensions | ||||
| var userExtensionsDir = null; | ||||
|  | ||||
| // Maps uuid -> metadata object | ||||
| const extensions = {}; | ||||
|  | ||||
| @@ -40,18 +40,13 @@ function getCurrentExtension() { | ||||
|         throw new Error('Could not find current extension'); | ||||
|  | ||||
|     let path = match[1]; | ||||
|     let file = Gio.File.new_for_path(path); | ||||
|     let uuid = GLib.path_get_basename(GLib.path_get_dirname(path)); | ||||
|  | ||||
|     // Walk up the directory tree, looking for an extesion with | ||||
|     // the same UUID as a directory name. | ||||
|     while (file != null) { | ||||
|         let extension = extensions[file.get_basename()]; | ||||
|         if (extension !== undefined) | ||||
|             return extension; | ||||
|         file = file.get_parent(); | ||||
|     } | ||||
|     let extension = extensions[uuid]; | ||||
|     if (extension === undefined) | ||||
|         throw new Error('Could not find current extension'); | ||||
|  | ||||
|     throw new Error('Could not find current extension'); | ||||
|     return extension; | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -88,6 +83,9 @@ function isOutOfDate(extension) { | ||||
|     if (!versionCheck(extension.metadata['shell-version'], Config.PACKAGE_VERSION)) | ||||
|         return true; | ||||
|  | ||||
|     if (extension.metadata['js-version'] && !versionCheck(extension.metadata['js-version'], Config.GJS_VERSION)) | ||||
|         return true; | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| @@ -120,6 +118,11 @@ function createExtensionObject(uuid, dir, type) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Encourage people to add this | ||||
|     if (!meta.url) { | ||||
|         global.log('Warning: Missing "url" property in metadata.json'); | ||||
|     } | ||||
|  | ||||
|     if (uuid != meta.uuid) { | ||||
|         throw new Error('uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"'); | ||||
|     } | ||||
| @@ -147,56 +150,45 @@ function installImporter(extension) { | ||||
|     _extension = null; | ||||
| } | ||||
|  | ||||
| const ExtensionFinder = new Lang.Class({ | ||||
|     Name: 'ExtensionFinder', | ||||
|  | ||||
|     _scanExtensionsInDirectory: function(dir, type) { | ||||
|         let fileEnum; | ||||
|         let file, info; | ||||
|         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'); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         while ((info = fileEnum.next_file(null)) != null) { | ||||
|             let fileType = info.get_file_type(); | ||||
|             if (fileType != Gio.FileType.DIRECTORY) | ||||
|                 continue; | ||||
|             let uuid = info.get_name(); | ||||
|             let extensionDir = dir.get_child(uuid); | ||||
|  | ||||
|             let existing = extensions[uuid]; | ||||
|             if (existing) { | ||||
|                 log('Extension %s already installed in %s. %s will not be loaded'.format(uuid, existing.path, extensionDir.get_path())); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             let extension; | ||||
|             try { | ||||
|                 extension = createExtensionObject(uuid, extensionDir, type); | ||||
|             } catch(e) { | ||||
|                 logError(e, 'Could not load extension %s'.format(uuid)); | ||||
|                 continue; | ||||
|             } | ||||
|             this.emit('extension-found', extension); | ||||
|         } | ||||
|         fileEnum.close(null); | ||||
|     }, | ||||
|  | ||||
|     scanExtensions: function() { | ||||
|         let userExtensionsDir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions'])); | ||||
|         this._scanExtensionsInDirectory(userExtensionsDir, ExtensionType.PER_USER); | ||||
|  | ||||
|         let systemDataDirs = GLib.get_system_data_dirs(); | ||||
|         for (let i = 0; i < systemDataDirs.length; i++) { | ||||
|             let dirPath = GLib.build_filenamev([systemDataDirs[i], 'gnome-shell', 'extensions']); | ||||
|             let dir = Gio.file_new_for_path(dirPath); | ||||
|             if (dir.query_exists(null)) | ||||
|                 this._scanExtensionsInDirectory(dir, ExtensionType.SYSTEM); | ||||
|         } | ||||
| function init() { | ||||
|     let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']); | ||||
|     userExtensionsDir = Gio.file_new_for_path(userExtensionsPath); | ||||
|     try { | ||||
|         if (!userExtensionsDir.query_exists(null)) | ||||
|             userExtensionsDir.make_directory_with_parents(null); | ||||
|     } catch (e) { | ||||
|         global.logError('' + e); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(ExtensionFinder.prototype); | ||||
| } | ||||
|  | ||||
| function scanExtensionsInDirectory(callback, dir, type) { | ||||
|     let fileEnum; | ||||
|     let file, info; | ||||
|     try { | ||||
|         fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null); | ||||
|     } catch(e) { | ||||
|         global.logError('' + e); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     while ((info = fileEnum.next_file(null)) != null) { | ||||
|         let fileType = info.get_file_type(); | ||||
|         if (fileType != Gio.FileType.DIRECTORY) | ||||
|             continue; | ||||
|         let uuid = info.get_name(); | ||||
|         let extensionDir = dir.get_child(uuid); | ||||
|         callback(uuid, extensionDir, type); | ||||
|     } | ||||
|     fileEnum.close(null); | ||||
| } | ||||
|  | ||||
| function scanExtensions(callback) { | ||||
|     let systemDataDirs = GLib.get_system_data_dirs(); | ||||
|     scanExtensionsInDirectory(callback, userExtensionsDir, ExtensionType.PER_USER); | ||||
|     for (let i = 0; i < systemDataDirs.length; i++) { | ||||
|         let dirPath = GLib.build_filenamev([systemDataDirs[i], 'gnome-shell', 'extensions']); | ||||
|         let dir = Gio.file_new_for_path(dirPath); | ||||
|         if (dir.query_exists(null)) | ||||
|             scanExtensionsInDirectory(callback, dir, ExtensionType.SYSTEM); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -28,7 +28,7 @@ function deleteGFile(file) { | ||||
|     return file['delete'](null); | ||||
| } | ||||
|  | ||||
| function recursivelyDeleteDir(dir, deleteParent) { | ||||
| function recursivelyDeleteDir(dir) { | ||||
|     let children = dir.enumerate_children('standard::name,standard::type', | ||||
|                                           Gio.FileQueryInfoFlags.NONE, null); | ||||
|  | ||||
| @@ -39,29 +39,8 @@ function recursivelyDeleteDir(dir, deleteParent) { | ||||
|         if (type == Gio.FileType.REGULAR) | ||||
|             deleteGFile(child); | ||||
|         else if (type == Gio.FileType.DIRECTORY) | ||||
|             recursivelyDeleteDir(child, true); | ||||
|             recursivelyDeleteDir(child); | ||||
|     } | ||||
|  | ||||
|     if (deleteParent) | ||||
|         deleteGFile(dir); | ||||
| } | ||||
|  | ||||
| function recursivelyMoveDir(srcDir, destDir) { | ||||
|     let children = srcDir.enumerate_children('standard::name,standard::type', | ||||
|                                              Gio.FileQueryInfoFlags.NONE, null); | ||||
|  | ||||
|     if (!destDir.query_exists(null)) | ||||
|         destDir.make_directory_with_parents(null); | ||||
|  | ||||
|     let info, child; | ||||
|     while ((info = children.next_file(null)) != null) { | ||||
|         let type = info.get_file_type(); | ||||
|         let srcChild = srcDir.get_child(info.get_name()); | ||||
|         let destChild = destDir.get_child(info.get_name()); | ||||
|         log([srcChild.get_path(), destChild.get_path()]); | ||||
|         if (type == Gio.FileType.REGULAR) | ||||
|             srcChild.move(destChild, Gio.FileCopyFlags.NONE, null, null); | ||||
|         else if (type == Gio.FileType.DIRECTORY) | ||||
|             recursivelyMoveDir(srcChild, destChild); | ||||
|     } | ||||
|     deleteGFile(dir); | ||||
| } | ||||
|   | ||||
							
								
								
									
										71
									
								
								js/misc/format.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,71 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const ShellJS = imports.gi.ShellJS; | ||||
|  | ||||
| /* | ||||
|  * This function is intended to extend the String object and provide | ||||
|  * an String.format API for string formatting. | ||||
|  * It has to be set up using String.prototype.format = Format.format; | ||||
|  * Usage: | ||||
|  * "somestring %s %d".format('hello', 5); | ||||
|  * It supports %s, %d, %x and %f, for %f it also support precisions like | ||||
|  * "%.2f".format(1.526). All specifiers can be prefixed with a minimum | ||||
|  * field width, e.g. "%5s".format("foo"). Unless the width is prefixed | ||||
|  * with '0', the formatted string will be padded with spaces. | ||||
|  */ | ||||
|  | ||||
| function format() { | ||||
|     let str = this; | ||||
|     let i = 0; | ||||
|     let args = arguments; | ||||
|  | ||||
|     return str.replace(/%(I+)?([0-9]+)?(?:\.([0-9]+))?(.)/g, function (str, flagsGroup, widthGroup, precisionGroup, genericGroup) { | ||||
|  | ||||
|                     if (precisionGroup != '' && genericGroup != 'f') | ||||
|                         throw new Error("Precision can only be specified for 'f'"); | ||||
|  | ||||
|                     let hasAlternativeIntFlag = (flagsGroup.indexOf('I') != -1); | ||||
|  | ||||
|                     if (hasAlternativeIntFlag && genericGroup != 'd') | ||||
|                         throw new Error("Alternative output digits can only be specfied for 'd'"); | ||||
|  | ||||
|                     let fillChar = (widthGroup[0] == '0') ? '0' : ' '; | ||||
|                     let width = parseInt(widthGroup, 10) || 0; | ||||
|  | ||||
|                     function fillWidth(s, c, w) { | ||||
|                         let fill = ''; | ||||
|                         for (let i = 0; i < w; i++) | ||||
|                             fill += c; | ||||
|                         return fill.substr(s.length) + s; | ||||
|                     } | ||||
|  | ||||
|                     let s = ''; | ||||
|                     switch (genericGroup) { | ||||
|                         case '%': | ||||
|                             return '%'; | ||||
|                             break; | ||||
|                         case 's': | ||||
|                             s = args[i++].toString(); | ||||
|                             break; | ||||
|                         case 'd': | ||||
|                             let intV = parseInt(args[i++]); | ||||
|                             if (hasAlternativeIntFlag) | ||||
|                                 s = ShellJS.format_int_alternative_output(intV); | ||||
|                             else | ||||
|                                 s = intV.toString(); | ||||
|                             break; | ||||
|                         case 'x': | ||||
|                             s = parseInt(args[i++]).toString(16); | ||||
|                             break; | ||||
|                         case 'f': | ||||
|                             if (precisionGroup == '') | ||||
|                                 s = parseFloat(args[i++]).toString(); | ||||
|                             else | ||||
|                                 s = parseFloat(args[i++]).toFixed(parseInt(precisionGroup)); | ||||
|                             break; | ||||
|                         default: | ||||
|                             throw new Error('Unsupported conversion character %' + genericGroup); | ||||
|                     } | ||||
|                     return fillWidth(s, fillChar, width); | ||||
|                 }); | ||||
| } | ||||
| @@ -50,20 +50,9 @@ const SessionManagerIface = <interface name="org.gnome.SessionManager"> | ||||
|     <arg type="u" direction="in" /> | ||||
| </method> | ||||
| <method name="Shutdown" /> | ||||
| <method name="Reboot" /> | ||||
| <method name="CanShutdown"> | ||||
|     <arg type="b" direction="out" /> | ||||
| </method> | ||||
| <method name="IsInhibited"> | ||||
|     <arg type="u" direction="in" /> | ||||
|     <arg type="b" direction="out" /> | ||||
| </method> | ||||
| <signal name="InhibitorAdded"> | ||||
|     <arg type="o" direction="out"/> | ||||
| </signal> | ||||
| <signal name="InhibitorRemoved"> | ||||
|     <arg type="o" direction="out"/> | ||||
| </signal> | ||||
| </interface>; | ||||
|  | ||||
| var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface); | ||||
|   | ||||
| @@ -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,197 +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 Shell = imports.gi.Shell; | ||||
|  | ||||
| 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 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'); | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|     powerOff: function() { | ||||
|         this._proxy.PowerOffRemote(true); | ||||
|     }, | ||||
|  | ||||
|     reboot: function() { | ||||
|         this._proxy.RebootRemote(true); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const LoginManagerConsoleKit = new Lang.Class({ | ||||
|     Name: 'LoginManagerConsoleKit', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._proxy = new ConsoleKitManager(Gio.DBus.system, | ||||
|                                             'org.freedesktop.ConsoleKit', | ||||
|                                             '/org/freedesktop/ConsoleKit/Manager'); | ||||
|     }, | ||||
|  | ||||
|     // 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]); | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|     powerOff: function() { | ||||
|         this._proxy.StopRemote(); | ||||
|     }, | ||||
|  | ||||
|     reboot: function() { | ||||
|         this._proxy.RestartRemote(); | ||||
|     } | ||||
|              | ||||
| }); | ||||
							
								
								
									
										48
									
								
								js/misc/screenSaver.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,48 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Gio = imports.gi.Gio; | ||||
| const Lang = imports.lang; | ||||
|  | ||||
| const ScreenSaverIface = <interface name="org.gnome.ScreenSaver"> | ||||
| <method name="GetActive"> | ||||
|     <arg type="b" direction="out" /> | ||||
| </method> | ||||
| <method name="Lock" /> | ||||
| <method name="SetActive"> | ||||
|     <arg type="b" direction="in" /> | ||||
| </method> | ||||
| <signal name="ActiveChanged"> | ||||
|     <arg type="b" direction="out" /> | ||||
| </signal> | ||||
| </interface>; | ||||
|  | ||||
| const ScreenSaverInfo = Gio.DBusInterfaceInfo.new_for_xml(ScreenSaverIface); | ||||
|  | ||||
| function ScreenSaverProxy() { | ||||
|     var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session, | ||||
| 				   g_interface_name: ScreenSaverInfo.name, | ||||
| 				   g_interface_info: ScreenSaverInfo, | ||||
| 				   g_name: 'org.gnome.ScreenSaver', | ||||
| 				   g_object_path: '/org/gnome/ScreenSaver', | ||||
|                                    g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START | | ||||
|                                              Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) }); | ||||
|     self.init(null); | ||||
|     self.screenSaverActive = false; | ||||
|  | ||||
|     self.connectSignal('ActiveChanged', function(proxy, senderName, [isActive]) { | ||||
|         self.screenSaverActive = isActive; | ||||
|     }); | ||||
|     self.connect('notify::g-name-owner', function() { | ||||
|         if (self.g_name_owner) { | ||||
|             self.GetActiveRemote(function(result, excp) { | ||||
|                 if (result) { | ||||
|                     let [isActive] = result; | ||||
|                     self.screenSaverActive = isActive; | ||||
|                 } | ||||
|             }); | ||||
|         } else | ||||
|             self.screenSaverActive = false; | ||||
|     }); | ||||
|  | ||||
|     return self; | ||||
| } | ||||
| @@ -1,6 +1,9 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Gdk = imports.gi.Gdk; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Shell = imports.gi.Shell; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
|  | ||||
| @@ -84,24 +87,20 @@ 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)) { | ||||
|             throw new GLib.SpawnError({ code: GLib.SpawnError.NOENT, | ||||
|                                         message: _("Command not found") }); | ||||
|         } else if (err instanceof GLib.Error) { | ||||
|         if (err.code == GLib.SpawnError.G_SPAWN_ERROR_NOENT) { | ||||
|             err.message = _("Command not found"); | ||||
|         } else { | ||||
|             // The exception from gjs contains an error string like: | ||||
|             //   Error invoking GLib.spawn_command_line_async: Failed to | ||||
|             //   execute child process "foo" (No such file or directory) | ||||
|             // We are only interested in the part in the parentheses. (And | ||||
|             // we can't pattern match the text, since it gets localized.) | ||||
|             let message = err.message.replace(/.*\((.+)\)/, '$1'); | ||||
|             throw new (err.constructor)({ code: err.code, | ||||
|                                           message: message }); | ||||
|         } else { | ||||
|             throw err; | ||||
|             err.message = err.message.replace(/.*\((.+)\)/, '$1'); | ||||
|         } | ||||
|  | ||||
|         throw err; | ||||
|     } | ||||
|     // Dummy child watch; we don't want to double-fork internally | ||||
|     // because then we lose the parent-child relationship, which | ||||
| @@ -150,7 +149,7 @@ function killall(processName) { | ||||
|         // whatever... | ||||
|  | ||||
|         let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )']; | ||||
|         GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null); | ||||
|         GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null, null); | ||||
|         // It might be useful to return success/failure, but we'd need | ||||
|         // a wrapper around WIFEXITED and WEXITSTATUS. Since none of | ||||
|         // the current callers care, we don't bother. | ||||
|   | ||||
| @@ -1,7 +1,5 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const System = imports.system; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const Scripting = imports.ui.scripting; | ||||
|  | ||||
| @@ -101,7 +99,7 @@ function run() { | ||||
|         Main.overview.hide(); | ||||
|         yield Scripting.waitLeisure(); | ||||
|  | ||||
|         System.gc(); | ||||
|         global.gc(); | ||||
|         yield Scripting.sleep(1000); | ||||
|         Scripting.collectStatistics(); | ||||
|         Scripting.scriptEvent('afterShowHide'); | ||||
| @@ -115,10 +113,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(); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -22,7 +22,6 @@ const Search = imports.ui.search; | ||||
| const Tweener = imports.ui.tweener; | ||||
| const Workspace = imports.ui.workspace; | ||||
| const Params = imports.misc.params; | ||||
| const Util = imports.misc.util; | ||||
|  | ||||
| const MAX_APPLICATION_WORK_MILLIS = 75; | ||||
| const MENU_POPUP_TIMEOUT = 600; | ||||
| @@ -37,7 +36,6 @@ const AlphabeticalView = new Lang.Class({ | ||||
|  | ||||
|         this._pendingAppLaterId = 0; | ||||
|         this._appIcons = {}; // desktop file id | ||||
|         this._allApps = []; | ||||
|  | ||||
|         let box = new St.BoxLayout({ vertical: true }); | ||||
|         box.add(this._grid.actor, { y_align: St.Align.START, expand: true }); | ||||
| @@ -62,22 +60,16 @@ const AlphabeticalView = new Lang.Class({ | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
|     removeAll: function() { | ||||
|     _removeAll: function() { | ||||
|         this._grid.removeAll(); | ||||
|         this._appIcons = {}; | ||||
|         this._allApps = []; | ||||
|     }, | ||||
|  | ||||
|     addApp: function(app) { | ||||
|     _addApp: function(app) { | ||||
|         var id = app.get_id(); | ||||
|         if (this._appIcons[id] !== undefined) | ||||
|             return; | ||||
|  | ||||
|         let appIcon = new AppWellIcon(app); | ||||
|         let pos = Util.insertSorted(this._allApps, app, function(a, b) { | ||||
|             return a.compare_by_name(b); | ||||
|         }); | ||||
|         this._grid.addItem(appIcon.actor, pos); | ||||
|  | ||||
|         this._grid.addItem(appIcon.actor); | ||||
|         appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible)); | ||||
|  | ||||
|         this._appIcons[id] = appIcon; | ||||
| @@ -128,6 +120,14 @@ const AlphabeticalView = new Lang.Class({ | ||||
|                 icon.actor.visible = true; | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     setAppList: function(apps) { | ||||
|         this._removeAll(); | ||||
|         for (var i = 0; i < apps.length; i++) { | ||||
|             var app = apps[i]; | ||||
|             this._addApp(app); | ||||
|          } | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -147,6 +147,7 @@ const ViewByCategories = new Lang.Class({ | ||||
|         // (used only before the actor is mapped the first time) | ||||
|         this._currentCategory = -2; | ||||
|         this._categories = []; | ||||
|         this._apps = null; | ||||
|  | ||||
|         this._categoryBox = new St.BoxLayout({ vertical: true, | ||||
|                                                reactive: true, | ||||
| @@ -203,19 +204,16 @@ const ViewByCategories = new Lang.Class({ | ||||
|             if (nextType == GMenu.TreeItemType.ENTRY) { | ||||
|                 var entry = iter.get_entry(); | ||||
|                 var app = this._appSystem.lookup_app_by_tree_entry(entry); | ||||
|                 if (!entry.get_app_info().get_nodisplay()) { | ||||
|                     this._view.addApp(app); | ||||
|                 if (!entry.get_app_info().get_nodisplay()) | ||||
|                     appList.push(app); | ||||
|                 } | ||||
|             } else if (nextType == GMenu.TreeItemType.DIRECTORY) { | ||||
|                 var itemDir = iter.get_directory(); | ||||
|                 if (!itemDir.get_is_nodisplay()) | ||||
|                     this._loadCategory(itemDir, appList); | ||||
|                 if (!dir.get_is_nodisplay()) | ||||
|                     this._loadCategory(iter.get_directory(), appList); | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _addCategory: function(name, index, dir) { | ||||
|     _addCategory: function(name, index, dir, allApps) { | ||||
|         let button = new St.Button({ label: GLib.markup_escape_text (name, -1), | ||||
|                                      style_class: 'app-filter', | ||||
|                                      x_align: St.Align.START, | ||||
| @@ -227,6 +225,7 @@ const ViewByCategories = new Lang.Class({ | ||||
|  | ||||
|         var apps; | ||||
|         if (dir == null) { | ||||
|             apps = allApps; | ||||
|             this._allCategoryButton = button; | ||||
|         } else { | ||||
|             apps = []; | ||||
| @@ -240,7 +239,6 @@ const ViewByCategories = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _removeAll: function() { | ||||
|         this._view.removeAll(); | ||||
|         this._categories = []; | ||||
|         this._categoryBox.destroy_all_children(); | ||||
|     }, | ||||
| @@ -248,8 +246,13 @@ const ViewByCategories = new Lang.Class({ | ||||
|     refresh: function() { | ||||
|         this._removeAll(); | ||||
|  | ||||
|         var allApps = Shell.AppSystem.get_default().get_all(); | ||||
|         allApps.sort(function(a, b) { | ||||
|             return a.compare_by_name(b); | ||||
|         }); | ||||
|  | ||||
|         /* Translators: Filter to display all applications */ | ||||
|         this._addCategory(_("All"), -1, null); | ||||
|         this._addCategory(_("All"), -1, null, allApps); | ||||
|  | ||||
|         var tree = this._appSystem.get_tree(); | ||||
|         var root = tree.get_root_directory(); | ||||
| @@ -267,6 +270,7 @@ const ViewByCategories = new Lang.Class({ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         this._view.setAppList(allApps); | ||||
|         this._selectCategory(-1); | ||||
|  | ||||
|         if (this._focusDummy) { | ||||
|   | ||||
							
								
								
									
										290
									
								
								js/ui/automountManager.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,290 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Lang = imports.lang; | ||||
| 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 Main = imports.ui.main; | ||||
| const ShellMountOperation = imports.ui.shellMountOperation; | ||||
| const ScreenSaver = imports.misc.screenSaver; | ||||
|  | ||||
| // GSettings keys | ||||
| const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling'; | ||||
| 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', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA }); | ||||
|         this._volumeQueue = []; | ||||
|  | ||||
|         if (!haveSystemd()) | ||||
|             this.ckListener = new ConsoleKitManager(); | ||||
|  | ||||
|         this._ssProxy = new ScreenSaver.ScreenSaverProxy(); | ||||
|         this._ssProxy.connectSignal('ActiveChanged', | ||||
|                                     Lang.bind(this, this._screenSaverActiveChanged)); | ||||
|  | ||||
|         this._volumeMonitor = Gio.VolumeMonitor.get(); | ||||
|  | ||||
|         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)); | ||||
|  | ||||
|         Mainloop.idle_add(Lang.bind(this, this._startupMountAll)); | ||||
|     }, | ||||
|  | ||||
|     _screenSaverActiveChanged: function(object, senderName, [isActive]) { | ||||
|         if (!isActive) { | ||||
|             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) { | ||||
|             this._checkAndMountVolume(volume, { checkSession: false, | ||||
|                                                 useMountOp: false }); | ||||
|         })); | ||||
|  | ||||
|         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.isSessionActive()) | ||||
|             return; | ||||
|  | ||||
|         if (this._ssProxy.screenSaverActive) | ||||
|             return; | ||||
|  | ||||
|         global.play_theme_sound(0, 'device-added-media'); | ||||
|     }, | ||||
|  | ||||
|     _onDriveDisconnected: function() { | ||||
|         // if we're not in the current ConsoleKit session, | ||||
|         // or screensaver is active, don't play sounds | ||||
|         if (!this.isSessionActive()) | ||||
|             return; | ||||
|  | ||||
|         if (this._ssProxy.screenSaverActive) | ||||
|             return; | ||||
|  | ||||
|         global.play_theme_sound(0, 'device-removed-media');         | ||||
|     }, | ||||
|  | ||||
|     _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.isSessionActive()) | ||||
|             return; | ||||
|  | ||||
|         // we force stop/eject in this case, so we don't have to pass a | ||||
|         // mount operation object | ||||
|         if (drive.can_stop()) { | ||||
|             drive.stop | ||||
|                 (Gio.MountUnmountFlags.FORCE, null, null, | ||||
|                  Lang.bind(this, function(drive, res) { | ||||
|                      try { | ||||
|                          drive.stop_finish(res); | ||||
|                      } catch (e) { | ||||
|                          log("Unable to stop the drive after drive-eject-button " + e.toString()); | ||||
|                      } | ||||
|                  })); | ||||
|         } else if (drive.can_eject()) { | ||||
|             drive.eject_with_operation  | ||||
|                 (Gio.MountUnmountFlags.FORCE, null, null, | ||||
|                  Lang.bind(this, function(drive, res) { | ||||
|                      try { | ||||
|                          drive.eject_with_operation_finish(res); | ||||
|                      } catch (e) { | ||||
|                          log("Unable to eject the drive after drive-eject-button " + e.toString()); | ||||
|                      } | ||||
|                  })); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onVolumeAdded: function(monitor, volume) { | ||||
|         this._checkAndMountVolume(volume); | ||||
|     }, | ||||
|  | ||||
|     _checkAndMountVolume: function(volume, params) { | ||||
|         params = Params.parse(params, { checkSession: true, | ||||
|                                         useMountOp: true }); | ||||
|  | ||||
|         if (params.checkSession) { | ||||
|             // if we're not in the current ConsoleKit session, | ||||
|             // don't attempt automount | ||||
|             if (!this.isSessionActive()) | ||||
|                 return; | ||||
|  | ||||
|             if (this._ssProxy.screenSaverActive) { | ||||
|                 if (this._volumeQueue.indexOf(volume) == -1) | ||||
|                     this._volumeQueue.push(volume); | ||||
|  | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Volume is already mounted, don't bother. | ||||
|         if (volume.get_mount()) | ||||
|             return; | ||||
|  | ||||
|         if (!this._settings.get_boolean(SETTING_ENABLE_AUTOMOUNT) || | ||||
|             !volume.should_automount() || | ||||
|             !volume.can_mount()) { | ||||
|             // allow the autorun to run anyway; this can happen if the | ||||
|             // mount gets added programmatically later, even if  | ||||
|             // should_automount() or can_mount() are false, like for | ||||
|             // blank optical media. | ||||
|             this._allowAutorun(volume); | ||||
|             this._allowAutorunExpire(volume); | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (params.useMountOp) { | ||||
|             let operation = new ShellMountOperation.ShellMountOperation(volume); | ||||
|             this._mountVolume(volume, operation.mountOp); | ||||
|         } else { | ||||
|             this._mountVolume(volume, null); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _mountVolume: function(volume, operation) { | ||||
|         this._allowAutorun(volume); | ||||
|         volume.mount(0, operation, null, | ||||
|                      Lang.bind(this, this._onVolumeMounted)); | ||||
|     }, | ||||
|  | ||||
|     _onVolumeMounted: function(volume, res) { | ||||
|         this._allowAutorunExpire(volume); | ||||
|  | ||||
|         try { | ||||
|             volume.mount_finish(res); | ||||
|         } catch (e) { | ||||
|             let string = e.toString(); | ||||
|  | ||||
|             // FIXME: needs proper error code handling instead of this | ||||
|             // See https://bugzilla.gnome.org/show_bug.cgi?id=591480 | ||||
|             if (string.indexOf('No key available with this passphrase') != -1) | ||||
|                 this._reaskPassword(volume); | ||||
|             else | ||||
|                 log('Unable to mount volume ' + volume.get_name() + ': ' + string); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onVolumeRemoved: function(monitor, volume) { | ||||
|         this._volumeQueue =  | ||||
|             this._volumeQueue.filter(function(element) { | ||||
|                 return (element != volume); | ||||
|             }); | ||||
|     }, | ||||
|  | ||||
|     _reaskPassword: function(volume) { | ||||
|         let operation = new ShellMountOperation.ShellMountOperation(volume, { reaskPassword: true }); | ||||
|         this._mountVolume(volume, operation.mountOp);         | ||||
|     }, | ||||
|  | ||||
|     _allowAutorun: function(volume) { | ||||
|         volume.allowAutorun = true; | ||||
|     }, | ||||
|  | ||||
|     _allowAutorunExpire: function(volume) { | ||||
|         Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() { | ||||
|             volume.allowAutorun = false; | ||||
|             return false; | ||||
|         }); | ||||
|     } | ||||
| }); | ||||
| @@ -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; | ||||
| @@ -24,14 +23,12 @@ const AutorunSetting = { | ||||
| }; | ||||
| 
 | ||||
| // misc utils
 | ||||
| function shouldAutorunMount(mount, forTransient) { | ||||
| function ignoreAutorunForMount(mount) { | ||||
|     let root = mount.get_root(); | ||||
|     let volume = mount.get_volume(); | ||||
| 
 | ||||
|     if (!volume || (!volume.allowAutorun && forTransient)) | ||||
|         return false; | ||||
| 
 | ||||
|     if (!root.is_native() || isMountRootHidden(root)) | ||||
|     if ((root.is_native() && !isMountRootHidden(root)) || | ||||
|         (volume && volume.allowAutorun && volume.should_automount())) | ||||
|         return false; | ||||
| 
 | ||||
|     return true; | ||||
| @@ -44,19 +41,6 @@ function isMountRootHidden(root) { | ||||
|     return (path.indexOf('/.') != -1); | ||||
| } | ||||
| 
 | ||||
| function isMountNonLocal(mount) { | ||||
|     // If the mount doesn't have an associated volume, that means it could
 | ||||
|     // be a remote filesystem. For certain kinds of local filesystems,
 | ||||
|     // like digital cameras and music players, there's no associated
 | ||||
|     // gvfs volume, so err on the side of caution and assume it's a local
 | ||||
|     // filesystem to allow the prompt.
 | ||||
|     let volume = mount.get_volume(); | ||||
|     if (volume == null) | ||||
|         return false; | ||||
| 
 | ||||
|     return (volume.get_identifier("class") == "network"); | ||||
| } | ||||
| 
 | ||||
| function startAppForMount(app, mount) { | ||||
|     let files = []; | ||||
|     let root = mount.get_root(); | ||||
| @@ -96,21 +80,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) { | ||||
| @@ -164,69 +140,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) { | ||||
| @@ -262,9 +224,11 @@ const AutorunManager = new Lang.Class({ | ||||
|         try { | ||||
|             mount.unmount_with_operation_finish(res); | ||||
|         } catch (e) { | ||||
|             if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED)) | ||||
|                 log('Unable to eject the mount ' + mount.get_name()  | ||||
|                     + ': ' + e.toString()); | ||||
|             // FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
 | ||||
|             // but we can't access the error code from JS.
 | ||||
|             // See https://bugzilla.gnome.org/show_bug.cgi?id=591480
 | ||||
|             log('Unable to eject the mount ' + mount.get_name()  | ||||
|                 + ': ' + e.toString()); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
| @@ -272,9 +236,11 @@ const AutorunManager = new Lang.Class({ | ||||
|         try { | ||||
|             source.eject_with_operation_finish(res); | ||||
|         } catch (e) { | ||||
|             if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED)) | ||||
|                 log('Unable to eject the drive ' + source.get_name() | ||||
|                     + ': ' + e.toString()); | ||||
|             // FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
 | ||||
|             // but we can't access the error code from JS.
 | ||||
|             // See https://bugzilla.gnome.org/show_bug.cgi?id=591480
 | ||||
|             log('Unable to eject the drive ' + source.get_name()  | ||||
|                 + ': ' + e.toString()); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
| @@ -282,9 +248,11 @@ const AutorunManager = new Lang.Class({ | ||||
|         try { | ||||
|             drive.stop_finish(res); | ||||
|         } catch (e) { | ||||
|             if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED)) | ||||
|                 log('Unable to stop the drive ' + drive.get_name()  | ||||
|                     + ': ' + e.toString()); | ||||
|             // FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
 | ||||
|             // but we can't access the error code from JS.
 | ||||
|             // See https://bugzilla.gnome.org/show_bug.cgi?id=591480
 | ||||
|             log('Unable to stop the drive ' + drive.get_name()  | ||||
|                 + ': ' + e.toString()); | ||||
|         } | ||||
|     }, | ||||
| }); | ||||
| @@ -293,22 +261,16 @@ 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) { | ||||
|         if (!shouldAutorunMount(mount, false)) | ||||
|         if (ignoreAutorunForMount(mount)) | ||||
|             return; | ||||
| 
 | ||||
|         let filtered = this._mounts.filter(function (element) { | ||||
| @@ -354,7 +316,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
 | ||||
| @@ -362,7 +324,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, | ||||
| @@ -410,7 +371,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 = | ||||
| @@ -425,7 +386,7 @@ const AutorunResidentNotification = new Lang.Class({ | ||||
|         })); | ||||
| 
 | ||||
|         ejectButton.connect('clicked', Lang.bind(this, function() { | ||||
|             this._manager.ejectMount(mount); | ||||
|             Main.autorunManager.ejectMount(mount); | ||||
|         })); | ||||
| 
 | ||||
|         return item; | ||||
| @@ -435,8 +396,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 }); | ||||
|     }, | ||||
| @@ -479,7 +439,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) { | ||||
| @@ -488,7 +448,7 @@ const AutorunTransientDispatcher = new Lang.Class({ | ||||
|             return; | ||||
| 
 | ||||
|         // if the mount doesn't want to be autorun, return
 | ||||
|         if (!shouldAutorunMount(mount, true)) | ||||
|         if (ignoreAutorunForMount(mount)) | ||||
|             return; | ||||
| 
 | ||||
|         let setting = this._getAutorunSettingForType(contentTypes[0]); | ||||
| @@ -532,23 +492,23 @@ 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); | ||||
|         this._setSummaryIcon(this.createNotificationIcon()); | ||||
| 
 | ||||
|         // add ourselves as a source, and popup the notification
 | ||||
|         Main.messageTray.add(this); | ||||
|         this.notify(this._notification); | ||||
|     }, | ||||
| 
 | ||||
|     createIcon: function(size) { | ||||
|     createNotificationIcon: function() { | ||||
|         return new St.Icon({ gicon: this.mount.get_icon(), | ||||
|                              icon_size: size }); | ||||
|                              icon_size: this.ICON_SIZE }); | ||||
|     } | ||||
| }); | ||||
| 
 | ||||
| @@ -556,10 +516,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); | ||||
| @@ -611,7 +570,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); | ||||
| 
 | ||||
| @@ -628,11 +587,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; | ||||
| @@ -9,13 +9,6 @@ const Shell = imports.gi.Shell; | ||||
| const Main = imports.ui.main; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const PopupAnimation = { | ||||
|     NONE:  0, | ||||
|     SLIDE: 1 << 0, | ||||
|     FADE:  1 << 1, | ||||
|     FULL:  ~0, | ||||
| }; | ||||
|  | ||||
| const POPUP_ANIMATION_TIME = 0.15; | ||||
|  | ||||
| /** | ||||
| @@ -25,10 +18,7 @@ const POPUP_ANIMATION_TIME = 0.15; | ||||
|  * | ||||
|  * An actor which displays a triangle "arrow" pointing to a given | ||||
|  * side.  The .bin property is a container in which content can be | ||||
|  * placed.  The arrow position may be controlled via | ||||
|  * setArrowOrigin(). The arrow side might be temporarily flipped | ||||
|  * depending on the box size and source position to keep the box | ||||
|  * totally inside the monitor if possible. | ||||
|  * placed.  The arrow position may be controlled via setArrowOrigin(). | ||||
|  * | ||||
|  */ | ||||
| const BoxPointer = new Lang.Class({ | ||||
| @@ -36,7 +26,6 @@ const BoxPointer = new Lang.Class({ | ||||
|  | ||||
|     _init: function(arrowSide, binProperties) { | ||||
|         this._arrowSide = arrowSide; | ||||
|         this._userArrowSide = arrowSide; | ||||
|         this._arrowOrigin = 0; | ||||
|         this.actor = new St.Bin({ x_fill: true, | ||||
|                                   y_fill: true }); | ||||
| @@ -76,16 +65,11 @@ const BoxPointer = new Lang.Class({ | ||||
|     show: function(animate, onComplete) { | ||||
|         let themeNode = this.actor.get_theme_node(); | ||||
|         let rise = themeNode.get_length('-arrow-rise'); | ||||
|         let animationTime = (animate & PopupAnimation.FULL) ? POPUP_ANIMATION_TIME : 0; | ||||
|  | ||||
|         if (animate & PopupAnimation.FADE) | ||||
|             this.opacity = 0; | ||||
|         else | ||||
|             this.opacity = 255; | ||||
|  | ||||
|         this.opacity = 0; | ||||
|         this.actor.show(); | ||||
|  | ||||
|         if (animate & PopupAnimation.SLIDE) { | ||||
|         if (animate) { | ||||
|             switch (this._arrowSide) { | ||||
|                 case St.Side.TOP: | ||||
|                     this.yOffset = -rise; | ||||
| @@ -111,7 +95,7 @@ const BoxPointer = new Lang.Class({ | ||||
|                                      if (onComplete) | ||||
|                                          onComplete(); | ||||
|                                  }), | ||||
|                                  time: animationTime }); | ||||
|                                  time: POPUP_ANIMATION_TIME }); | ||||
|     }, | ||||
|  | ||||
|     hide: function(animate, onComplete) { | ||||
| @@ -119,10 +103,8 @@ const BoxPointer = new Lang.Class({ | ||||
|         let yOffset = 0; | ||||
|         let themeNode = this.actor.get_theme_node(); | ||||
|         let rise = themeNode.get_length('-arrow-rise'); | ||||
|         let fade = (animate & PopupAnimation.FADE); | ||||
|         let animationTime = (animate & PopupAnimation.FULL) ? POPUP_ANIMATION_TIME : 0; | ||||
|  | ||||
|         if (animate & PopupAnimation.SLIDE) { | ||||
|         if (animate) { | ||||
|             switch (this._arrowSide) { | ||||
|                 case St.Side.TOP: | ||||
|                     yOffset = rise; | ||||
| @@ -141,14 +123,13 @@ const BoxPointer = new Lang.Class({ | ||||
|  | ||||
|         this._muteInput(); | ||||
|  | ||||
|         Tweener.addTween(this, { opacity: fade ? 0 : 255, | ||||
|         Tweener.addTween(this, { opacity: 0, | ||||
|                                  xOffset: xOffset, | ||||
|                                  yOffset: yOffset, | ||||
|                                  transition: 'linear', | ||||
|                                  time: animationTime, | ||||
|                                  time: POPUP_ANIMATION_TIME, | ||||
|                                  onComplete: Lang.bind(this, function () { | ||||
|                                      this.actor.hide(); | ||||
|                                      this.opacity = 0; | ||||
|                                      this.xOffset = 0; | ||||
|                                      this.yOffset = 0; | ||||
|                                      if (onComplete) | ||||
| @@ -218,27 +199,8 @@ const BoxPointer = new Lang.Class({ | ||||
|         } | ||||
|         this.bin.allocate(childBox, flags); | ||||
|  | ||||
|         if (this._sourceActor && this._sourceActor.mapped) { | ||||
|         if (this._sourceActor && this._sourceActor.mapped) | ||||
|             this._reposition(this._sourceActor, this._arrowAlignment); | ||||
|  | ||||
|             if (this._shouldFlip()) { | ||||
|                 switch (this._arrowSide) { | ||||
|                 case St.Side.TOP: | ||||
|                     this._arrowSide = St.Side.BOTTOM; | ||||
|                     break; | ||||
|                 case St.Side.BOTTOM: | ||||
|                     this._arrowSide = St.Side.TOP; | ||||
|                     break; | ||||
|                 case St.Side.LEFT: | ||||
|                     this._arrowSide = St.Side.RIGHT; | ||||
|                     break; | ||||
|                 case St.Side.RIGHT: | ||||
|                     this._arrowSide = St.Side.LEFT; | ||||
|                     break; | ||||
|                 } | ||||
|                 this._reposition(this._sourceActor, this._arrowAlignment); | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _drawBorder: function(area) { | ||||
| @@ -252,6 +214,7 @@ const BoxPointer = new Lang.Class({ | ||||
|         let halfBorder = borderWidth / 2; | ||||
|         let halfBase = Math.floor(base/2); | ||||
|  | ||||
|         let borderColor = themeNode.get_color('-arrow-border-color'); | ||||
|         let backgroundColor = themeNode.get_color('-arrow-background-color'); | ||||
|  | ||||
|         let [width, height] = area.get_surface_size(); | ||||
| @@ -262,6 +225,7 @@ const BoxPointer = new Lang.Class({ | ||||
|             boxWidth -= rise; | ||||
|         } | ||||
|         let cr = area.get_context(); | ||||
|         Clutter.cairo_set_source_color(cr, borderColor); | ||||
|  | ||||
|         // Translate so that box goes from 0,0 to boxWidth,boxHeight, | ||||
|         // with the arrow poking out of that | ||||
| @@ -274,51 +238,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); | ||||
| @@ -326,20 +253,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); | ||||
| @@ -347,20 +273,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); | ||||
| @@ -368,20 +293,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); | ||||
| @@ -389,26 +313,20 @@ 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(); | ||||
|  | ||||
|         if (borderWidth > 0) { | ||||
|             let borderColor = themeNode.get_color('-arrow-border-color'); | ||||
|             Clutter.cairo_set_source_color(cr, borderColor); | ||||
|             cr.setLineWidth(borderWidth); | ||||
|             cr.stroke(); | ||||
|         } | ||||
|         Clutter.cairo_set_source_color(cr, borderColor); | ||||
|         cr.setLineWidth(borderWidth); | ||||
|         cr.stroke(); | ||||
|     }, | ||||
|  | ||||
|     setPosition: function(sourceActor, alignment) { | ||||
|         this._arrowSide = this._userArrowSide; | ||||
|  | ||||
|         // We need to show it now to force an allocation, | ||||
|         // so that we can query the correct size. | ||||
|         this.actor.show(); | ||||
| @@ -425,7 +343,11 @@ const BoxPointer = new Lang.Class({ | ||||
|         if (!this._sourceActor) | ||||
|             return; | ||||
|  | ||||
|         this.setPosition(this._sourceActor, this._arrowAlignment); | ||||
|         // We need to show it now to force an allocation, | ||||
|         // so that we can query the correct size. | ||||
|         this.actor.show(); | ||||
|  | ||||
|         this._reposition(this._sourceActor, this._arrowAlignment); | ||||
|     }, | ||||
|  | ||||
|     _reposition: function(sourceActor, alignment) { | ||||
| @@ -446,9 +368,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; | ||||
|  | ||||
| @@ -467,66 +390,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) { | ||||
| @@ -560,39 +446,6 @@ const BoxPointer = new Lang.Class({ | ||||
|                                     -(this._yPosition + this._yOffset)); | ||||
|     }, | ||||
|  | ||||
|     _shouldFlip: function() { | ||||
|         let sourceAllocation = Shell.util_get_transformed_allocation(this._sourceActor); | ||||
|         let boxAllocation = Shell.util_get_transformed_allocation(this.actor); | ||||
|         let boxWidth = boxAllocation.x2 - boxAllocation.x1; | ||||
|         let boxHeight = boxAllocation.y2 - boxAllocation.y1; | ||||
|         let monitor = Main.layoutManager.findMonitorForActor(this.actor); | ||||
|  | ||||
|         switch (this._arrowSide) { | ||||
|         case St.Side.TOP: | ||||
|             if (boxAllocation.y2 > monitor.y + monitor.height && | ||||
|                 boxHeight < sourceAllocation.y1 - monitor.y) | ||||
|                 return true; | ||||
|             break; | ||||
|         case St.Side.BOTTOM: | ||||
|             if (boxAllocation.y1 < monitor.y && | ||||
|                 boxHeight < monitor.y + monitor.height - sourceAllocation.y2) | ||||
|                 return true; | ||||
|             break; | ||||
|         case St.Side.LEFT: | ||||
|             if (boxAllocation.x2 > monitor.x + monitor.width && | ||||
|                 boxWidth < sourceAllocation.x1 - monitor.x) | ||||
|                 return true; | ||||
|             break; | ||||
|         case St.Side.RIGHT: | ||||
|             if (boxAllocation.x1 < monitor.x && | ||||
|                 boxWidth < monitor.x + monitor.width - sourceAllocation.x2) | ||||
|                 return true; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     set xOffset(offset) { | ||||
|         this._xOffset = offset; | ||||
|         this._shiftActor(); | ||||
|   | ||||
| @@ -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; | ||||
| @@ -339,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; | ||||
| @@ -379,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)) { | ||||
| @@ -453,7 +448,7 @@ const Calendar = new Lang.Class({ | ||||
|         } | ||||
|  | ||||
|         // All the children after this are days, and get removed when we update the calendar | ||||
|         this._firstDayIndex = this.actor.get_n_children(); | ||||
|         this._firstDayIndex = this.actor.get_children().length; | ||||
|     }, | ||||
|  | ||||
|     _onStyleChange: function(actor, event) { | ||||
| @@ -556,7 +551,6 @@ const Calendar = new Lang.Class({ | ||||
|         let row = 2; | ||||
|         while (true) { | ||||
|             let button = new St.Button({ label: iter.getDate().toString() }); | ||||
|             let rtl = button.get_text_direction() == Clutter.TextDirection.RTL; | ||||
|  | ||||
|             if (!this._eventSource) | ||||
|                 button.reactive = false; | ||||
| @@ -577,10 +571,7 @@ const Calendar = new Lang.Class({ | ||||
|             // Hack used in lieu of border-collapse - see gnome-shell.css | ||||
|             if (row == 2) | ||||
|                 styleClass = 'calendar-day-top ' + styleClass; | ||||
|  | ||||
|             let leftMost = rtl ? iter.getDay() == (this._weekStart + 6) % 7 | ||||
|                                : iter.getDay() == this._weekStart; | ||||
|             if (leftMost) | ||||
|             if (iter.getDay() == this._weekStart) | ||||
|                 styleClass = 'calendar-day-left ' + styleClass; | ||||
|  | ||||
|             if (_sameDay(now, iter)) | ||||
| @@ -627,25 +618,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) { | ||||
| @@ -731,15 +713,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 | ||||
| @@ -747,7 +727,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,241 +0,0 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Lang = imports.lang; | ||||
| 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 Main = imports.ui.main; | ||||
| const ShellMountOperation = imports.ui.shellMountOperation; | ||||
|  | ||||
| const GNOME_SESSION_AUTOMOUNT_INHIBIT = 16; | ||||
|  | ||||
| // GSettings keys | ||||
| const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling'; | ||||
| const SETTING_ENABLE_AUTOMOUNT = 'automount'; | ||||
|  | ||||
| const AUTORUN_EXPIRE_TIMEOUT_SECS = 10; | ||||
|  | ||||
| const AutomountManager = new Lang.Class({ | ||||
|     Name: 'AutomountManager', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA }); | ||||
|         this._volumeQueue = []; | ||||
|         this._session = new GnomeSession.SessionManager(); | ||||
|         this._session.connectSignal('InhibitorAdded', | ||||
|                                     Lang.bind(this, this._InhibitorsChanged)); | ||||
|         this._session.connectSignal('InhibitorRemoved', | ||||
|                                     Lang.bind(this, this._InhibitorsChanged)); | ||||
|         this._inhibited = false; | ||||
|  | ||||
|         this._loginManager = LoginManager.getLoginManager(); | ||||
|         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._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; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _InhibitorsChanged: function(object, senderName, [inhibtor]) { | ||||
|         this._session.IsInhibitedRemote(GNOME_SESSION_AUTOMOUNT_INHIBIT, | ||||
|             Lang.bind(this, | ||||
|                 function(result, error) { | ||||
|                     if (!error) { | ||||
|                         this._inhibited = result[0]; | ||||
|                     } | ||||
|                 })); | ||||
|     }, | ||||
|  | ||||
|     _startupMountAll: function() { | ||||
|         let volumes = this._volumeMonitor.get_volumes(); | ||||
|         volumes.forEach(Lang.bind(this, function(volume) { | ||||
|             this._checkAndMountVolume(volume, { checkSession: false, | ||||
|                                                 useMountOp: false, | ||||
|                                                 allowAutorun: false }); | ||||
|         })); | ||||
|  | ||||
|         this._mountAllId = 0; | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     _onDriveConnected: function() { | ||||
|         // if we're not in the current ConsoleKit session, | ||||
|         // or screensaver is active, don't play sounds | ||||
|         if (!this._loginManager.sessionActive) | ||||
|             return; | ||||
|  | ||||
|         global.play_theme_sound(0, 'device-added-media'); | ||||
|     }, | ||||
|  | ||||
|     _onDriveDisconnected: function() { | ||||
|         // if we're not in the current ConsoleKit session, | ||||
|         // or screensaver is active, don't play sounds | ||||
|         if (!this._loginManager.sessionActive) | ||||
|             return; | ||||
|  | ||||
|         global.play_theme_sound(0, 'device-removed-media');         | ||||
|     }, | ||||
|  | ||||
|     _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) | ||||
|             return; | ||||
|  | ||||
|         // we force stop/eject in this case, so we don't have to pass a | ||||
|         // mount operation object | ||||
|         if (drive.can_stop()) { | ||||
|             drive.stop | ||||
|                 (Gio.MountUnmountFlags.FORCE, null, null, | ||||
|                  Lang.bind(this, function(drive, res) { | ||||
|                      try { | ||||
|                          drive.stop_finish(res); | ||||
|                      } catch (e) { | ||||
|                          log("Unable to stop the drive after drive-eject-button " + e.toString()); | ||||
|                      } | ||||
|                  })); | ||||
|         } else if (drive.can_eject()) { | ||||
|             drive.eject_with_operation  | ||||
|                 (Gio.MountUnmountFlags.FORCE, null, null, | ||||
|                  Lang.bind(this, function(drive, res) { | ||||
|                      try { | ||||
|                          drive.eject_with_operation_finish(res); | ||||
|                      } catch (e) { | ||||
|                          log("Unable to eject the drive after drive-eject-button " + e.toString()); | ||||
|                      } | ||||
|                  })); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onVolumeAdded: function(monitor, volume) { | ||||
|         this._checkAndMountVolume(volume); | ||||
|     }, | ||||
|  | ||||
|     _checkAndMountVolume: function(volume, params) { | ||||
|         params = Params.parse(params, { checkSession: true, | ||||
|                                         useMountOp: true, | ||||
|                                         allowAutorun: true }); | ||||
|  | ||||
|         if (params.checkSession) { | ||||
|             // if we're not in the current ConsoleKit session, | ||||
|             // don't attempt automount | ||||
|             if (!this._loginManager.sessionActive) | ||||
|                 return; | ||||
|         } | ||||
|  | ||||
|         if (this._inhibited) | ||||
|             return; | ||||
|  | ||||
|         // Volume is already mounted, don't bother. | ||||
|         if (volume.get_mount()) | ||||
|             return; | ||||
|  | ||||
|         if (!this._settings.get_boolean(SETTING_ENABLE_AUTOMOUNT) || | ||||
|             !volume.should_automount() || | ||||
|             !volume.can_mount()) { | ||||
|             // allow the autorun to run anyway; this can happen if the | ||||
|             // mount gets added programmatically later, even if  | ||||
|             // should_automount() or can_mount() are false, like for | ||||
|             // blank optical media. | ||||
|             this._allowAutorun(volume); | ||||
|             this._allowAutorunExpire(volume); | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (params.useMountOp) { | ||||
|             let operation = new ShellMountOperation.ShellMountOperation(volume); | ||||
|             this._mountVolume(volume, operation, params.allowAutorun); | ||||
|         } else { | ||||
|             this._mountVolume(volume, null, params.allowAutorun); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _mountVolume: function(volume, operation, allowAutorun) { | ||||
|         if (allowAutorun) | ||||
|             this._allowAutorun(volume); | ||||
|  | ||||
|         let mountOp = operation ? operation.mountOp : null; | ||||
|         volume._operation = operation; | ||||
|  | ||||
|         volume.mount(0, mountOp, null, | ||||
|                      Lang.bind(this, this._onVolumeMounted)); | ||||
|     }, | ||||
|  | ||||
|     _onVolumeMounted: function(volume, res) { | ||||
|         this._allowAutorunExpire(volume); | ||||
|  | ||||
|         try { | ||||
|             volume.mount_finish(res); | ||||
|             this._closeOperation(volume); | ||||
|         } catch (e) { | ||||
|             // FIXME: we will always get G_IO_ERROR_FAILED from the gvfs udisks | ||||
|             // backend in this case, see  | ||||
|             // https://bugs.freedesktop.org/show_bug.cgi?id=51271 | ||||
|             if (e.message.indexOf('No key available with this passphrase') != -1) { | ||||
|                 this._reaskPassword(volume); | ||||
|             } else { | ||||
|                 if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED)) | ||||
|                     log('Unable to mount volume ' + volume.get_name() + ': ' + e.toString()); | ||||
|  | ||||
|                 this._closeOperation(volume); | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onVolumeRemoved: function(monitor, volume) { | ||||
|         this._volumeQueue =  | ||||
|             this._volumeQueue.filter(function(element) { | ||||
|                 return (element != volume); | ||||
|             }); | ||||
|     }, | ||||
|  | ||||
|     _reaskPassword: function(volume) { | ||||
|         let existingDialog = volume._operation ? volume._operation.borrowDialog() : null; | ||||
|         let operation =  | ||||
|             new ShellMountOperation.ShellMountOperation(volume, | ||||
|                                                         { existingDialog: existingDialog }); | ||||
|         this._mountVolume(volume, operation); | ||||
|     }, | ||||
|  | ||||
|     _closeOperation: function(volume) { | ||||
|         if (volume._operation) | ||||
|             volume._operation.close(); | ||||
|     }, | ||||
|  | ||||
|     _allowAutorun: function(volume) { | ||||
|         volume.allowAutorun = true; | ||||
|     }, | ||||
|  | ||||
|     _allowAutorunExpire: function(volume) { | ||||
|         Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() { | ||||
|             volume.allowAutorun = false; | ||||
|             return false; | ||||
|         }); | ||||
|     } | ||||
| }); | ||||
| const Component = AutomountManager; | ||||
| @@ -1,58 +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_filename(_("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(); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const Component = Recorder; | ||||
							
								
								
									
										196
									
								
								js/ui/contactDisplay.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,196 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Folks = imports.gi.Folks | ||||
| const Lang = imports.lang; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
| const Atk = imports.gi.Atk; | ||||
|  | ||||
| const Util = imports.misc.util; | ||||
| const IconGrid = imports.ui.iconGrid; | ||||
| const Search = imports.ui.search; | ||||
| const SearchDisplay = imports.ui.searchDisplay; | ||||
|  | ||||
| const MAX_SEARCH_RESULTS_ROWS = 1; | ||||
| const ICON_SIZE = 81; | ||||
|  | ||||
| function launchContact(id) { | ||||
|     Util.spawn(['gnome-contacts', '-i', id]); | ||||
| } | ||||
|  | ||||
|  | ||||
| /* This class represents a shown contact search result in the overview */ | ||||
| const Contact = new Lang.Class({ | ||||
|     Name: 'Contact', | ||||
|  | ||||
|     _init: function(id) { | ||||
|         this._contactSys = Shell.ContactSystem.get_default(); | ||||
|         this.individual = this._contactSys.get_individual(id); | ||||
|  | ||||
|         this.actor = new St.Bin({ style_class: 'contact', | ||||
|                                   reactive: true, | ||||
|                                   can_focus: true, | ||||
|                                   track_hover: true, | ||||
|                                   accessible_role: Atk.Role.PUSH_BUTTON }); | ||||
|  | ||||
|         let content = new St.BoxLayout( { style_class: 'contact-content', | ||||
|                                           vertical: false }); | ||||
|         this.actor.set_child(content); | ||||
|  | ||||
|         let icon = new St.Icon({ icon_type: St.IconType.FULLCOLOR, | ||||
|                                  icon_size: ICON_SIZE, | ||||
|                                  style_class: 'contact-icon' }); | ||||
|         if (this.individual.avatar != null) | ||||
|             icon.gicon = this.individual.avatar; | ||||
|         else | ||||
|             icon.icon_name = 'avatar-default'; | ||||
|  | ||||
|         content.add(icon, { x_fill: true, | ||||
|                             y_fill: false, | ||||
|                             x_align: St.Align.START, | ||||
|                             y_align: St.Align.MIDDLE }); | ||||
|  | ||||
|         let details = new St.BoxLayout({ style_class: 'contact-details', | ||||
|                                          vertical: true }); | ||||
|         content.add(details, { x_fill: true, | ||||
|                                y_fill: false, | ||||
|                                x_align: St.Align.START, | ||||
|                                y_align: St.Align.MIDDLE }); | ||||
|  | ||||
|         let email = this._contactSys.get_email_for_display(this.individual); | ||||
|         let aliasText = this.individual.alias     || | ||||
|                         this.individual.full_name || | ||||
|                         this.individual.nickname  || | ||||
|                         email                     || | ||||
|                         _("Unknown"); | ||||
|         let aliasLabel = new St.Label({ text: aliasText, | ||||
|                                         style_class: 'contact-details-alias' }); | ||||
|         details.add(aliasLabel, { x_fill: true, | ||||
|                                   y_fill: false, | ||||
|                                   x_align: St.Align.START, | ||||
|                                   y_align: St.Align.START }); | ||||
|  | ||||
|         this.actor.label_actor = aliasLabel; | ||||
|  | ||||
|         let presence = this._createPresence(this.individual.presence_type); | ||||
|         details.add(presence, { x_fill: false, | ||||
|                                 y_fill: true, | ||||
|                                 x_align: St.Align.START, | ||||
|                                 y_align: St.Align.END }); | ||||
|     }, | ||||
|  | ||||
|     _createPresence: function(presence) { | ||||
|         let text; | ||||
|         let iconName; | ||||
|  | ||||
|         switch(presence) { | ||||
|           case Folks.PresenceType.AVAILABLE: | ||||
|             text = _("Available"); | ||||
|             iconName = 'user-available'; | ||||
|             break; | ||||
|           case Folks.PresenceType.AWAY: | ||||
|           case Folks.PresenceType.EXTENDED_AWAY: | ||||
|             text = _("Away"); | ||||
|             iconName = 'user-away'; | ||||
|             break; | ||||
|           case Folks.PresenceType.BUSY: | ||||
|             text = _("Busy"); | ||||
|             iconName = 'user-busy'; | ||||
|             break; | ||||
|           case Folks.PresenceType.OFFLINE: | ||||
|             text = _("Offline"); | ||||
|             iconName = 'user-offline'; | ||||
|             break; | ||||
|           default: | ||||
|             text = ''; | ||||
|             iconName = null; | ||||
|           } | ||||
|  | ||||
|         let box = new St.BoxLayout({ vertical: false, | ||||
|                                      style_class: 'contact-details-status' }); | ||||
|  | ||||
|         if (iconName) { | ||||
|             let icon = new St.Icon({ icon_name: iconName, | ||||
|                                      icon_type: St.IconType.FULLCOLOR, | ||||
|                                      icon_size: 16, | ||||
|                                      style_class: 'contact-details-status-icon' }); | ||||
|             box.add(icon, { x_fill: true, | ||||
|                             y_fill: false, | ||||
|                             x_align: St.Align.START, | ||||
|                             y_align: St.Align.START }); | ||||
|         } | ||||
|  | ||||
|         let label = new St.Label({ text: text }); | ||||
|  | ||||
|         box.add(label, { x_fill: true, | ||||
|                          y_fill: false, | ||||
|                          x_align: St.Align.END, | ||||
|                          y_align: St.Align.START }); | ||||
|  | ||||
|         return box; | ||||
|     }, | ||||
|  | ||||
|     createIcon: function(size) { | ||||
|         let tc = St.TextureCache.get_default(); | ||||
|         let icon = this.individual.avatar; | ||||
|  | ||||
|         if (icon != null) { | ||||
|             return tc.load_gicon(null, icon, size); | ||||
|         } else { | ||||
|             return tc.load_icon_name(null, 'avatar-default', St.IconType.FULLCOLOR, size); | ||||
|         } | ||||
|     }, | ||||
| }); | ||||
|  | ||||
|  | ||||
| /* Searches for and returns contacts */ | ||||
| const ContactSearchProvider = new Lang.Class({ | ||||
|     Name: 'ContactSearchProvider', | ||||
|     Extends: Search.SearchProvider, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent(_("CONTACTS")); | ||||
|         this._contactSys = Shell.ContactSystem.get_default(); | ||||
|     }, | ||||
|  | ||||
|     getResultMetas: function(ids, callback) { | ||||
|         let metas = []; | ||||
|         for (let i = 0; i < ids.length; i++) { | ||||
|             let contact = new Contact(ids[i]); | ||||
|             metas.push({ 'id': ids[i], | ||||
|                          'name': contact.alias, | ||||
|                          'createIcon': function(size) { | ||||
|                              return contact.createIcon(size); | ||||
|                          } | ||||
|                        }); | ||||
|         } | ||||
|         callback(metas); | ||||
|     }, | ||||
|  | ||||
|     getInitialResultSet: function(terms) { | ||||
|         this.searchSystem.pushResults(this, this._contactSys.initial_search(terms)); | ||||
|     }, | ||||
|  | ||||
|     getSubsearchResultSet: function(previousResults, terms) { | ||||
|         this.searchSystem.pushResults(this, this._contactSys.subsearch(previousResults, terms)); | ||||
|     }, | ||||
|  | ||||
|     createResultActor: function(resultMeta, terms) { | ||||
|         let contact = new Contact(resultMeta.id); | ||||
|         return contact.actor; | ||||
|     }, | ||||
|  | ||||
|     createResultContainerActor: function() { | ||||
|         let grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS, | ||||
|                                              xAlign: St.Align.START }); | ||||
|         grid.actor.style_class = 'contact-grid'; | ||||
|  | ||||
|         let actor = new SearchDisplay.GridSearchResults(this, grid); | ||||
|         return actor; | ||||
|     }, | ||||
|  | ||||
|     activateResult: function(id, params) { | ||||
|         launchContact(id); | ||||
|     } | ||||
| }); | ||||
| @@ -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 } ); | ||||
|   | ||||
							
								
								
									
										246
									
								
								js/ui/dash.js
									
									
									
									
									
								
							
							
						
						| @@ -21,18 +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) { | ||||
|         let appSystem = Shell.AppSystem.get_default(); | ||||
|         return appSystem.lookup_app(source.getId()); | ||||
|     } 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({ | ||||
| @@ -48,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; | ||||
| @@ -107,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(); | ||||
|  | ||||
| @@ -141,10 +124,16 @@ const DashItemContainer = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     setLabelText: function(text) { | ||||
|         this._labelText = 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 () { | ||||
|         this.label.opacity = 255; | ||||
|         Tweener.addTween(this.label, | ||||
|                          { opacity: 0, | ||||
|                            time: DASH_ITEM_LABEL_HIDE_TIME, | ||||
| @@ -238,77 +227,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.toggleButton.label_actor = this.label; | ||||
|         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(); | ||||
|  | ||||
| @@ -332,39 +296,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', | ||||
|  | ||||
| @@ -376,25 +307,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) | ||||
| @@ -404,6 +327,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)); | ||||
| @@ -446,25 +370,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; | ||||
|     }, | ||||
| @@ -480,17 +433,6 @@ 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 display = new AppDisplay.AppWellIcon(app, | ||||
|                                                  { setSizeManually: true, | ||||
| @@ -510,14 +452,18 @@ const Dash = new Lang.Class({ | ||||
|         item.setLabelText(app.get_name()); | ||||
|         // Override default AppWellIcon label_actor | ||||
|         display.actor.label_actor = item.label; | ||||
|         display.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) | ||||
|                                })); | ||||
|         return item; | ||||
|     }, | ||||
|  | ||||
|     _onHover: function (item) { | ||||
|         if (item.child.get_hover() && !item.child._delegate.isMenuUp) { | ||||
|     _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, | ||||
| @@ -528,7 +474,7 @@ const Dash = new Lang.Class({ | ||||
|                     })); | ||||
|                 if (this._resetHoverTimeoutId > 0) { | ||||
|                     Mainloop.source_remove(this._resetHoverTimeoutId); | ||||
|                     this._resetHoverTimeoutId = 0; | ||||
|                 this._resetHoverTimeoutId = 0; | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
| @@ -558,12 +504,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 }); | ||||
| @@ -589,6 +541,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; | ||||
| @@ -777,7 +730,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()) | ||||
| @@ -858,7 +815,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()) { | ||||
|   | ||||
| @@ -2,7 +2,6 @@ | ||||
|  | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GnomeDesktop = imports.gi.GnomeDesktop; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Cairo = imports.cairo; | ||||
| @@ -17,6 +16,14 @@ const Main = imports.ui.main; | ||||
| const PanelMenu = imports.ui.panelMenu; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| const Calendar = imports.ui.calendar; | ||||
| const UPowerGlib = imports.gi.UPowerGlib; | ||||
|  | ||||
| // in org.gnome.desktop.interface | ||||
| const CLOCK_FORMAT_KEY        = 'clock-format'; | ||||
|  | ||||
| // in org.gnome.shell.clock | ||||
| const CLOCK_SHOW_DATE_KEY     = 'show-date'; | ||||
| const CLOCK_SHOW_SECONDS_KEY  = 'show-seconds'; | ||||
|  | ||||
| function _onVertSepRepaint (area) | ||||
| { | ||||
| @@ -38,7 +45,9 @@ const DateMenuButton = new Lang.Class({ | ||||
|     Name: 'DateMenuButton', | ||||
|     Extends: PanelMenu.Button, | ||||
|  | ||||
|     _init: function() { | ||||
|     _init: function(params) { | ||||
|         params = Params.parse(params, { showEvents: true }); | ||||
|  | ||||
|         let item; | ||||
|         let hbox; | ||||
|         let vbox; | ||||
| @@ -53,8 +62,8 @@ const DateMenuButton = new Lang.Class({ | ||||
|         // role ATK_ROLE_MENU like other elements of the panel. | ||||
|         this.actor.accessible_role = Atk.Role.LABEL; | ||||
|  | ||||
|         this._clockDisplay = new St.Label(); | ||||
|         this.actor.add_actor(this._clockDisplay); | ||||
|         this._clock = new St.Label(); | ||||
|         this.actor.add_actor(this._clock); | ||||
|  | ||||
|         hbox = new St.BoxLayout({name: 'calendarArea' }); | ||||
|         this.menu.addActor(hbox); | ||||
| @@ -66,12 +75,20 @@ const DateMenuButton = new Lang.Class({ | ||||
|  | ||||
|         // Date | ||||
|         this._date = new St.Label(); | ||||
|         this.actor.label_actor = this._clockDisplay; | ||||
|         this.actor.label_actor = this._clock; | ||||
|         this._date.style_class = 'datemenu-date-label'; | ||||
|         vbox.add(this._date); | ||||
|  | ||||
|         this._eventList = new Calendar.EventsList(); | ||||
|         this._calendar = new Calendar.Calendar(); | ||||
|         if (params.showEvents) { | ||||
|             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 +106,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 (params.showEvents) { | ||||
|             // 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) { | ||||
| @@ -143,75 +157,79 @@ const DateMenuButton = new Lang.Class({ | ||||
|  | ||||
|         // Done with hbox for calendar and event list | ||||
|  | ||||
|         this._clock = new GnomeDesktop.WallClock(); | ||||
|         this._clock.connect('notify::clock', Lang.bind(this, this._updateClockAndDate)); | ||||
|         // Track changes to clock settings | ||||
|         this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' }); | ||||
|         this._clockSettings = new Gio.Settings({ schema: 'org.gnome.shell.clock' }); | ||||
|         this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate)); | ||||
|         this._clockSettings.connect('changed', Lang.bind(this, this._updateClockAndDate)); | ||||
|  | ||||
|         // https://bugzilla.gnome.org/show_bug.cgi?id=655129 | ||||
|         this._upClient = new UPowerGlib.Client(); | ||||
|         this._upClient.connect('notify-resume', Lang.bind(this, this._updateClockAndDate)); | ||||
|  | ||||
|         // Start the clock | ||||
|         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() { | ||||
|         this._clockDisplay.set_text(this._clock.clock); | ||||
|         let format = this._desktopSettings.get_string(CLOCK_FORMAT_KEY); | ||||
|         let showDate = this._clockSettings.get_boolean(CLOCK_SHOW_DATE_KEY); | ||||
|         let showSeconds = this._clockSettings.get_boolean(CLOCK_SHOW_SECONDS_KEY); | ||||
|  | ||||
|         let clockFormat; | ||||
|         let dateFormat; | ||||
|  | ||||
|         switch (format) { | ||||
|             case '24h': | ||||
|                 if (showDate) | ||||
|                     /* Translators: This is the time format with date used | ||||
|                        in 24-hour mode. */ | ||||
|                     clockFormat = showSeconds ? _("%a %b %e, %R:%S") | ||||
|                                               : _("%a %b %e, %R"); | ||||
|                 else | ||||
|                     /* Translators: This is the time format without date used | ||||
|                        in 24-hour mode. */ | ||||
|                     clockFormat = showSeconds ? _("%a %R:%S") | ||||
|                                               : _("%a %R"); | ||||
|                 break; | ||||
|             case '12h': | ||||
|             default: | ||||
|                 if (showDate) | ||||
|                     /* Translators: This is a time format with date used | ||||
|                        for AM/PM. */ | ||||
|                     clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p") | ||||
|                                               : _("%a %b %e, %l:%M %p"); | ||||
|                 else | ||||
|                     /* Translators: This is a time format without date used | ||||
|                        for AM/PM. */ | ||||
|                     clockFormat = showSeconds ? _("%a %l:%M:%S %p") | ||||
|                                               : _("%a %l:%M %p"); | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         let displayDate = new Date(); | ||||
|  | ||||
|         this._clock.set_text(displayDate.toLocaleFormat(clockFormat)); | ||||
|  | ||||
|         /* Translators: This is the date format to use when the calendar popup is | ||||
|          * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM"). | ||||
|          */ | ||||
|         let dateFormat = _("%A %B %e, %Y"); | ||||
|         let displayDate = new Date(); | ||||
|         dateFormat = _("%A %B %e, %Y"); | ||||
|         this._date.set_text(displayDate.toLocaleFormat(dateFormat)); | ||||
|  | ||||
|         Mainloop.timeout_add_seconds(1, Lang.bind(this, this._updateClockAndDate)); | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     _onOpenCalendarActivate: function() { | ||||
|         this.menu.close(); | ||||
|         let tool = this._calendarSettings.get_string('exec'); | ||||
|         if (tool.length == 0 || tool.substr(0, 9) == 'evolution') { | ||||
|         let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' }); | ||||
|         let tool = calendarSettings.get_string('exec'); | ||||
|         if (tool.length == 0 || tool == 'evolution') { | ||||
|             // TODO: pass the selected day | ||||
|             let app = Shell.AppSystem.get_default().lookup_app('evolution-calendar.desktop'); | ||||
|             app.activate(); | ||||
|             Util.spawn(['evolution', '-c', 'calendar']); | ||||
|         } 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
									
									
									
									
									
								
							
							
						
						| @@ -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; | ||||
|   | ||||
| @@ -31,10 +31,10 @@ const St = imports.gi.St; | ||||
| const Shell = imports.gi.Shell; | ||||
|  | ||||
| const GnomeSession = imports.misc.gnomeSession; | ||||
| const Lightbox = imports.ui.lightbox; | ||||
| 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 +90,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 +105,7 @@ const restartDialogContent = { | ||||
|     endDescription: _("Restarting the system."), | ||||
|     confirmButtons: [{ signal: 'ConfirmedReboot', | ||||
|                        label:  C_("button", "Restart") }], | ||||
|     iconName: 'system-shutdown-symbolic', | ||||
|     iconName: 'system-shutdown', | ||||
|     iconStyleClass: 'end-session-dialog-shutdown-icon' | ||||
| }; | ||||
|  | ||||
| @@ -161,7 +161,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 }); | ||||
| @@ -281,17 +280,21 @@ const EndSessionDialog = new Lang.Class({ | ||||
|         scrollView.hide(); | ||||
|  | ||||
|         this._applicationList = new St.BoxLayout({ vertical: true }); | ||||
|         scrollView.add_actor(this._applicationList); | ||||
|         scrollView.add_actor(this._applicationList, | ||||
|                              { x_fill:  true, | ||||
|                                y_fill:  true, | ||||
|                                x_align: St.Align.START, | ||||
|                                y_align: St.Align.MIDDLE }); | ||||
|  | ||||
|         this._applicationList.connect('actor-added', | ||||
|                                       Lang.bind(this, function() { | ||||
|                                           if (this._applicationList.get_n_children() == 1) | ||||
|                                           if (this._applicationList.get_children().length == 1) | ||||
|                                               scrollView.show(); | ||||
|                                       })); | ||||
|  | ||||
|         this._applicationList.connect('actor-removed', | ||||
|                                       Lang.bind(this, function() { | ||||
|                                           if (this._applicationList.get_n_children() == 0) | ||||
|                                           if (this._applicationList.get_children().length == 0) | ||||
|                                               scrollView.hide(); | ||||
|                                       })); | ||||
|  | ||||
| @@ -304,7 +307,42 @@ const EndSessionDialog = new Lang.Class({ | ||||
|         this._user.disconnect(this._userChangedId); | ||||
|     }, | ||||
|  | ||||
|     _updateDescription: function() { | ||||
|     _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(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _updateContent: function() { | ||||
|         if (this.state != ModalDialog.State.OPENING && | ||||
|             this.state != ModalDialog.State.OPENED) | ||||
|             return; | ||||
| @@ -314,6 +352,17 @@ const EndSessionDialog = new Lang.Class({ | ||||
|         let subject = dialogContent.subject; | ||||
|         let description; | ||||
|  | ||||
|         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); | ||||
|         } | ||||
|  | ||||
|         if (this._inhibitors.length > 0) { | ||||
|             this._stopTimer(); | ||||
|             description = dialogContent.inhibitedDescription; | ||||
| @@ -346,27 +395,6 @@ const EndSessionDialog = new Lang.Class({ | ||||
|         _setLabelText(this._descriptionLabel, description); | ||||
|     }, | ||||
|  | ||||
|     _updateContent: function() { | ||||
|         if (this.state != ModalDialog.State.OPENING && | ||||
|             this.state != ModalDialog.State.OPENED) | ||||
|             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(); | ||||
|         } | ||||
|  | ||||
|         this._updateDescription(); | ||||
|     }, | ||||
|  | ||||
|     _updateButtons: function() { | ||||
|         let dialogContent = DialogContent[this._type]; | ||||
|         let buttons = [{ action: Lang.bind(this, this.cancel), | ||||
| @@ -413,7 +441,7 @@ const EndSessionDialog = new Lang.Class({ | ||||
|                          { _secondsLeft: 0, | ||||
|                            time: this._secondsLeft, | ||||
|                            transition: 'linear', | ||||
|                            onUpdate: Lang.bind(this, this._updateDescription), | ||||
|                            onUpdate: Lang.bind(this, this._updateContent), | ||||
|                            onComplete: Lang.bind(this, function() { | ||||
|                                            let dialogContent = DialogContent[this._type]; | ||||
|                                            let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1]; | ||||
|   | ||||
| @@ -90,7 +90,7 @@ function init() { | ||||
|     } | ||||
|  | ||||
|     // OK, now things are initialized enough that we can import shell JS | ||||
|     const Format = imports.format; | ||||
|     const Format = imports.misc.format; | ||||
|     const Tweener = imports.ui.tweener; | ||||
|  | ||||
|     Tweener.init(); | ||||
|   | ||||
| @@ -1,270 +0,0 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Lang = imports.lang; | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gio = imports.gi.Gio; | ||||
| const Soup = imports.gi.Soup; | ||||
| const St = imports.gi.St; | ||||
| const Shell = imports.gi.Shell; | ||||
|  | ||||
| const Config = imports.misc.config; | ||||
| const ExtensionUtils = imports.misc.extensionUtils; | ||||
| const ExtensionSystem = imports.ui.extensionSystem; | ||||
| const FileUtils = imports.misc.fileUtils; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
|  | ||||
| const _signals = ExtensionSystem._signals; | ||||
|  | ||||
| const REPOSITORY_URL_BASE = 'https://extensions.gnome.org'; | ||||
| const REPOSITORY_URL_DOWNLOAD = REPOSITORY_URL_BASE + '/download-extension/%s.shell-extension.zip'; | ||||
| const REPOSITORY_URL_INFO     = REPOSITORY_URL_BASE + '/extension-info/'; | ||||
| const REPOSITORY_URL_UPDATE   = REPOSITORY_URL_BASE + '/update-info/'; | ||||
|  | ||||
| let _httpSession; | ||||
|  | ||||
| function installExtension(uuid, invocation) { | ||||
|     let params = { uuid: uuid, | ||||
|                    shell_version: Config.PACKAGE_VERSION }; | ||||
|  | ||||
|     let message = Soup.form_request_new_from_hash('GET', REPOSITORY_URL_INFO, params); | ||||
|  | ||||
|     _httpSession.queue_message(message, function(session, message) { | ||||
|         if (message.status_code != Soup.KnownStatusCode.OK) { | ||||
|             ExtensionSystem.logExtensionError(uuid, 'downloading info: ' + message.status_code); | ||||
|             invocation.return_dbus_error('org.gnome.Shell.DownloadInfoError', message.status_code.toString()); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let info; | ||||
|         try { | ||||
|             info = JSON.parse(message.response_body.data); | ||||
|         } catch (e) { | ||||
|             ExtensionSystem.logExtensionError(uuid, 'parsing info: ' + e); | ||||
|             invocation.return_dbus_error('org.gnome.Shell.ParseInfoError', e.toString()); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let dialog = new InstallExtensionDialog(uuid, info, invocation); | ||||
|         dialog.open(global.get_current_time()); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function uninstallExtension(uuid) { | ||||
|     let extension = ExtensionUtils.extensions[uuid]; | ||||
|     if (!extension) | ||||
|         return false; | ||||
|  | ||||
|     // Don't try to uninstall system extensions | ||||
|     if (extension.type != ExtensionUtils.ExtensionType.PER_USER) | ||||
|         return false; | ||||
|  | ||||
|     if (!ExtensionSystem.unloadExtension(extension)) | ||||
|         return false; | ||||
|  | ||||
|     FileUtils.recursivelyDeleteDir(extension.dir, true); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| function gotExtensionZipFile(session, message, uuid, dir, callback, errback) { | ||||
|     if (message.status_code != Soup.KnownStatusCode.OK) { | ||||
|         errback('DownloadExtensionError', message.status_code); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     try { | ||||
|         if (!dir.query_exists(null)) | ||||
|             dir.make_directory_with_parents(null); | ||||
|     } catch (e) { | ||||
|         errback('CreateExtensionDirectoryError', e); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     let [file, stream] = Gio.File.new_tmp('XXXXXX.shell-extension.zip'); | ||||
|     let contents = message.response_body.flatten().get_as_bytes(); | ||||
|     stream.output_stream.write_bytes(contents, null); | ||||
|     stream.close(null); | ||||
|     let [success, pid] = GLib.spawn_async(null, | ||||
|                                           ['unzip', '-uod', dir.get_path(), '--', file.get_path()], | ||||
|                                           null, | ||||
|                                           GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD, | ||||
|                                           null); | ||||
|  | ||||
|     if (!success) { | ||||
|         errback('ExtractExtensionError'); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) { | ||||
|         GLib.spawn_close_pid(pid); | ||||
|  | ||||
|         if (status != 0) | ||||
|             errback('ExtractExtensionError'); | ||||
|         else | ||||
|             callback(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function updateExtension(uuid) { | ||||
|     // This gets a bit tricky. We want the update to be seamless - | ||||
|     // if we have any error during downloading or extracting, we | ||||
|     // want to not unload the current version. | ||||
|  | ||||
|     let oldExtensionTmpDir = GLib.Dir.make_tmp('XXXXXX-shell-extension'); | ||||
|     let newExtensionTmpDir = GLib.Dir.make_tmp('XXXXXX-shell-extension'); | ||||
|  | ||||
|     let params = { shell_version: Config.PACKAGE_VERSION }; | ||||
|  | ||||
|     let url = REPOSITORY_URL_DOWNLOAD.format(uuid); | ||||
|     let message = Soup.form_request_new_from_hash('GET', url, params); | ||||
|  | ||||
|     _httpSession.queue_message(message, Lang.bind(this, function(session, message) { | ||||
|         gotExtensionZipFile(session, message, uuid, newExtensionTmpDir, function() { | ||||
|             let oldExtension = ExtensionUtils.extensions[uuid]; | ||||
|             let extensionDir = oldExtension.dir; | ||||
|  | ||||
|             if (!ExtensionSystem.unloadExtension(oldExtension)) | ||||
|                 return; | ||||
|  | ||||
|             FileUtils.recursivelyMoveDir(extensionDir, oldExtensionTmpDir); | ||||
|             FileUtils.recursivelyMoveDir(newExtensionTmpDir, extensionDir); | ||||
|  | ||||
|             let extension = ExtensionUtils.createExtensionObject(uuid, extensionDir, ExtensionUtils.ExtensionType.PER_USER); | ||||
|  | ||||
|             try { | ||||
|                 ExtensionSystem.loadExtension(extension); | ||||
|             } catch(e) { | ||||
|                 ExtensionSystem.unloadExtension(extension); | ||||
|  | ||||
|                 logError(e, 'Error loading extension %s'.format(uuid)); | ||||
|  | ||||
|                 FileUtils.recursivelyDeleteDir(extensionDir, false); | ||||
|                 FileUtils.recursivelyMoveDir(oldExtensionTmpDir, extensionDir); | ||||
|  | ||||
|                 // Restore what was there before. We can't do much if we | ||||
|                 // fail here. | ||||
|                 ExtensionSystem.loadExtension(oldExtension); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             FileUtils.recursivelyDeleteDir(oldExtensionTmpDir, true); | ||||
|         }, function(code, message) { | ||||
|             log('Error while updating extension %s: %s (%s)'.format(uuid, code, message ? message : '')); | ||||
|         }); | ||||
|     })); | ||||
| } | ||||
|  | ||||
| function checkForUpdates() { | ||||
|     let metadatas = {}; | ||||
|     for (let uuid in ExtensionUtils.extensions) { | ||||
|         metadatas[uuid] = ExtensionUtils.extensions[uuid].metadata; | ||||
|     } | ||||
|  | ||||
|     let params = { shell_version: Config.PACKAGE_VERSION, | ||||
|                    installed: JSON.stringify(metadatas) }; | ||||
|  | ||||
|     let url = REPOSITORY_URL_UPDATE; | ||||
|     let message = Soup.form_request_new_from_hash('GET', url, params); | ||||
|     _httpSession.queue_message(message, function(session, message) { | ||||
|         if (message.status_code != Soup.KnownStatusCode.OK) | ||||
|             return; | ||||
|  | ||||
|         let operations = JSON.parse(message.response_body.data); | ||||
|         for (let uuid in operations) { | ||||
|             let operation = operations[uuid]; | ||||
|             if (operation == 'blacklist') | ||||
|                 uninstallExtension(uuid); | ||||
|             else if (operation == 'upgrade' || operation == 'downgrade') | ||||
|                 updateExtension(uuid); | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|  | ||||
| const InstallExtensionDialog = new Lang.Class({ | ||||
|     Name: 'InstallExtensionDialog', | ||||
|     Extends: ModalDialog.ModalDialog, | ||||
|  | ||||
|     _init: function(uuid, info, invocation) { | ||||
|         this.parent({ styleClass: 'extension-dialog' }); | ||||
|  | ||||
|         this._uuid = uuid; | ||||
|         this._info = info; | ||||
|         this._invocation = invocation; | ||||
|  | ||||
|         this.setButtons([{ label: _("Cancel"), | ||||
|                            action: Lang.bind(this, this._onCancelButtonPressed), | ||||
|                            key:    Clutter.Escape | ||||
|                          }, | ||||
|                          { label:  _("Install"), | ||||
|                            action: Lang.bind(this, this._onInstallButtonPressed), | ||||
|                            default: true | ||||
|                          }]); | ||||
|  | ||||
|         let message = _("Download and install '%s' from extensions.gnome.org?").format(info.name); | ||||
|  | ||||
|         let box = new St.BoxLayout(); | ||||
|         this.contentLayout.add(box); | ||||
|  | ||||
|         let gicon = new Gio.FileIcon({ file: Gio.File.new_for_uri(REPOSITORY_URL_BASE + info.icon) }) | ||||
|         let icon = new St.Icon({ gicon: gicon }); | ||||
|         box.add(icon); | ||||
|  | ||||
|         let label = new St.Label({ text: message }); | ||||
|         box.add(label); | ||||
|     }, | ||||
|  | ||||
|     _onCancelButtonPressed: function(button, event) { | ||||
|         this.close(global.get_current_time()); | ||||
|         this._invocation.return_value(GLib.Variant.new('(s)', ['cancelled'])); | ||||
|     }, | ||||
|  | ||||
|     _onInstallButtonPressed: function(button, event) { | ||||
|         let params = { shell_version: Config.PACKAGE_VERSION }; | ||||
|  | ||||
|         let url = REPOSITORY_URL_DOWNLOAD.format(this._uuid); | ||||
|         let message = Soup.form_request_new_from_hash('GET', url, params); | ||||
|  | ||||
|         let uuid = this._uuid; | ||||
|         let dir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions', uuid])); | ||||
|         let invocation = this._invocation; | ||||
|         function errback(code, message) { | ||||
|             invocation.return_dbus_error('org.gnome.Shell.' + code, message ? message.toString() : ''); | ||||
|         } | ||||
|  | ||||
|         function callback() { | ||||
|             // Add extension to 'enabled-extensions' for the user, always... | ||||
|             let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY); | ||||
|             if (enabledExtensions.indexOf(uuid) == -1) { | ||||
|                 enabledExtensions.push(uuid); | ||||
|                 global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions); | ||||
|             } | ||||
|  | ||||
|             let extension = ExtensionUtils.createExtensionObject(uuid, dir, ExtensionUtils.ExtensionType.PER_USER); | ||||
|  | ||||
|             try { | ||||
|                 ExtensionSystem.loadExtension(extension); | ||||
|             } catch(e) { | ||||
|                 uninstallExtension(uuid); | ||||
|                 errback('LoadExtensionError', e); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             invocation.return_value(GLib.Variant.new('(s)', 'successful')); | ||||
|         } | ||||
|  | ||||
|         _httpSession.queue_message(message, Lang.bind(this, function(session, message) { | ||||
|             gotExtensionZipFile(session, message, uuid, dir, callback, errback); | ||||
|         })); | ||||
|  | ||||
|         this.close(global.get_current_time()); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| function init() { | ||||
|     _httpSession = new Soup.SessionAsync({ ssl_use_system_ca_file: true }); | ||||
|  | ||||
|     // See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context. | ||||
|     // _httpSession.add_feature(new Soup.ProxyResolverDefault()); | ||||
|     Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault()); | ||||
| } | ||||
| @@ -3,12 +3,19 @@ | ||||
| const Lang = imports.lang; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gio = imports.gi.Gio; | ||||
| const St = imports.gi.St; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Soup = imports.gi.Soup; | ||||
|  | ||||
| const Config = imports.misc.config; | ||||
| const ExtensionUtils = imports.misc.extensionUtils; | ||||
| const Main = imports.ui.main; | ||||
| const FileUtils = imports.misc.fileUtils; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
|  | ||||
| const API_VERSION = 1; | ||||
|  | ||||
| const ExtensionState = { | ||||
|     ENABLED: 1, | ||||
| @@ -23,6 +30,29 @@ const ExtensionState = { | ||||
|     UNINSTALLED: 99 | ||||
| }; | ||||
|  | ||||
| const REPOSITORY_URL_BASE = 'https://extensions.gnome.org'; | ||||
| const REPOSITORY_URL_DOWNLOAD = REPOSITORY_URL_BASE + '/download-extension/%s.shell-extension.zip'; | ||||
| const REPOSITORY_URL_INFO =     REPOSITORY_URL_BASE + '/extension-info/'; | ||||
|  | ||||
| const _httpSession = new Soup.SessionAsync(); | ||||
|  | ||||
| // The unfortunate state of gjs, gobject-introspection and libsoup | ||||
| // means that I have to do a hack to add a feature. | ||||
| // See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context. | ||||
|  | ||||
| if (Soup.Session.prototype.add_feature != null) | ||||
|     Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault()); | ||||
|  | ||||
| function _getCertFile() { | ||||
|     let localCert = GLib.build_filenamev([global.userdatadir, 'extensions.gnome.org.crt']); | ||||
|     if (GLib.file_test(localCert, GLib.FileTest.EXISTS)) | ||||
|         return localCert; | ||||
|     else | ||||
|         return Config.SHELL_SYSTEM_CA_FILE; | ||||
| } | ||||
|  | ||||
| _httpSession.ssl_ca_file = _getCertFile(); | ||||
|  | ||||
| // Arrays of uuids | ||||
| var enabledExtensions; | ||||
| // Contains the order that extensions were enabled in. | ||||
| @@ -39,8 +69,89 @@ const disconnect = Lang.bind(_signals, _signals.disconnect); | ||||
|  | ||||
| const ENABLED_EXTENSIONS_KEY = 'enabled-extensions'; | ||||
|  | ||||
| var initted = false; | ||||
| var enabled; | ||||
| function installExtensionFromUUID(uuid, version_tag) { | ||||
|     let params = { uuid: uuid, | ||||
|                    version_tag: version_tag, | ||||
|                    shell_version: Config.PACKAGE_VERSION, | ||||
|                    api_version: API_VERSION.toString() }; | ||||
|  | ||||
|     let message = Soup.form_request_new_from_hash('GET', REPOSITORY_URL_INFO, params); | ||||
|  | ||||
|     _httpSession.queue_message(message, | ||||
|                                function(session, message) { | ||||
|                                    let info = JSON.parse(message.response_body.data); | ||||
|                                    let dialog = new InstallExtensionDialog(uuid, version_tag, info.name); | ||||
|                                    dialog.open(global.get_current_time()); | ||||
|                                }); | ||||
| } | ||||
|  | ||||
| function uninstallExtensionFromUUID(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(uuid); | ||||
|  | ||||
|     // Don't try to uninstall system extensions | ||||
|     if (extension.type != ExtensionUtils.ExtensionType.PER_USER) | ||||
|         return false; | ||||
|  | ||||
|     extension.state = ExtensionState.UNINSTALLED; | ||||
|     _signals.emit('extension-state-changed', extension); | ||||
|  | ||||
|     delete ExtensionUtils.extensions[uuid]; | ||||
|  | ||||
|     FileUtils.recursivelyDeleteDir(Gio.file_new_for_path(extension.path)); | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| function gotExtensionZipFile(session, message, uuid) { | ||||
|     if (message.status_code != Soup.KnownStatusCode.OK) { | ||||
|         logExtensionError(uuid, 'downloading extension: ' + message.status_code); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // FIXME: use a GFile mkstemp-type method once one exists | ||||
|     let fd, tmpzip; | ||||
|     try { | ||||
|         [fd, tmpzip] = GLib.file_open_tmp('XXXXXX.shell-extension.zip'); | ||||
|     } catch (e) { | ||||
|         logExtensionError(uuid, 'tempfile: ' + e.toString()); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     let stream = new Gio.UnixOutputStream({ fd: fd }); | ||||
|     let dir = ExtensionUtils.userExtensionsDir.get_child(uuid); | ||||
|     Shell.write_soup_message_to_stream(stream, message); | ||||
|     stream.close(null); | ||||
|     let [success, pid] = GLib.spawn_async(null, | ||||
|                                           ['unzip', '-uod', dir.get_path(), '--', tmpzip], | ||||
|                                           null, | ||||
|                                           GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD, | ||||
|                                           null); | ||||
|  | ||||
|     if (!success) { | ||||
|         logExtensionError(uuid, 'extract: could not extract'); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) { | ||||
|         GLib.spawn_close_pid(pid); | ||||
|  | ||||
|         // Add extension to 'enabled-extensions' for the user, always... | ||||
|         let enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY); | ||||
|         if (enabledExtensions.indexOf(uuid) == -1) { | ||||
|             enabledExtensions.push(uuid); | ||||
|             global.settings.set_strv(ENABLED_EXTENSIONS_KEY, enabledExtensions); | ||||
|         } | ||||
|  | ||||
|         loadExtension(dir, ExtensionUtils.ExtensionType.PER_USER, true); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function disableExtension(uuid) { | ||||
|     let extension = ExtensionUtils.extensions[uuid]; | ||||
| @@ -67,23 +178,23 @@ function disableExtension(uuid) { | ||||
|         try { | ||||
|             ExtensionUtils.extensions[uuid].stateObj.disable(); | ||||
|         } catch(e) { | ||||
|             logExtensionError(uuid, e); | ||||
|             logExtensionError(uuid, e.toString()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (extension.stylesheet) { | ||||
|         let theme = St.ThemeContext.get_for_stage(global.stage).get_theme(); | ||||
|         theme.unload_stylesheet(extension.stylesheet.get_path()); | ||||
|     try { | ||||
|         extension.stateObj.disable(); | ||||
|     } catch(e) { | ||||
|         logExtensionError(uuid, e.toString()); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     extension.stateObj.disable(); | ||||
|  | ||||
|     for (let i = 0; i < order.length; i++) { | ||||
|         let uuid = order[i]; | ||||
|         try { | ||||
|             ExtensionUtils.extensions[uuid].stateObj.enable(); | ||||
|         } catch(e) { | ||||
|             logExtensionError(uuid, e); | ||||
|             logExtensionError(uuid, e.toString()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -106,86 +217,68 @@ function enableExtension(uuid) { | ||||
|  | ||||
|     extensionOrder.push(uuid); | ||||
|  | ||||
|     let stylesheetFile = extension.dir.get_child('stylesheet.css'); | ||||
|     if (stylesheetFile.query_exists(null)) { | ||||
|         let theme = St.ThemeContext.get_for_stage(global.stage).get_theme(); | ||||
|         theme.load_stylesheet(stylesheetFile.get_path()); | ||||
|         extension.stylesheet = stylesheetFile; | ||||
|     try { | ||||
|         extension.stateObj.enable(); | ||||
|     } catch(e) { | ||||
|         logExtensionError(uuid, e.toString()); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     extension.stateObj.enable(); | ||||
|  | ||||
|     extension.state = ExtensionState.ENABLED; | ||||
|     _signals.emit('extension-state-changed', extension); | ||||
| } | ||||
|  | ||||
| function logExtensionError(uuid, error) { | ||||
| function logExtensionError(uuid, message, state) { | ||||
|     let extension = ExtensionUtils.extensions[uuid]; | ||||
|     if (!extension) | ||||
|         return; | ||||
|  | ||||
|     let message = '' + error; | ||||
|  | ||||
|     if (error.state) | ||||
|         extension.state = error.state; | ||||
|     else | ||||
|         extension.state = ExtensionState.ERROR; | ||||
|  | ||||
|     if (!extension.errors) | ||||
|         extension.errors = []; | ||||
|  | ||||
|     log('Extension "%s" had error: %s'.format(uuid, message)); | ||||
|     extension.errors.push(message); | ||||
|     global.logError('Extension "%s" had error: %s'.format(uuid, message)); | ||||
|     state = state || ExtensionState.ERROR; | ||||
|     _signals.emit('extension-state-changed', { uuid: uuid, | ||||
|                                                error: message, | ||||
|                                                state: extension.state }); | ||||
|                                                state: state }); | ||||
| } | ||||
|  | ||||
| function loadExtension(extension) { | ||||
| function loadExtension(dir, type, enabled) { | ||||
|     let uuid = dir.get_basename(); | ||||
|     let extension; | ||||
|  | ||||
|     if (ExtensionUtils.extensions[uuid] != undefined) { | ||||
|         global.logError('Extension "%s" is already loaded'.format(uuid)); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     try { | ||||
|         extension = ExtensionUtils.createExtensionObject(uuid, dir, type); | ||||
|     } catch(e) { | ||||
|         logExtensionError(uuid, e.message); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // Default to error, we set success as the last step | ||||
|     extension.state = ExtensionState.ERROR; | ||||
|  | ||||
|     if (ExtensionUtils.isOutOfDate(extension)) { | ||||
|         let error = new Error('extension is not compatible with current GNOME Shell and/or GJS version'); | ||||
|         error.state = ExtensionState.OUT_OF_DATE; | ||||
|         throw error; | ||||
|         logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version', ExtensionState.OUT_OF_DATE); | ||||
|         extension.state = ExtensionState.OUT_OF_DATE; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     let enabled = enabledExtensions.indexOf(extension.uuid) != -1; | ||||
|     if (enabled) { | ||||
|         initExtension(extension.uuid); | ||||
|         initExtension(uuid); | ||||
|         if (extension.state == ExtensionState.DISABLED) | ||||
|             enableExtension(extension.uuid); | ||||
|             enableExtension(uuid); | ||||
|     } else { | ||||
|         extension.state = ExtensionState.INITIALIZED; | ||||
|     } | ||||
|  | ||||
|     _signals.emit('extension-state-changed', extension); | ||||
| } | ||||
|  | ||||
| function unloadExtension(extension) { | ||||
|     // 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); | ||||
|  | ||||
|     extension.state = ExtensionState.UNINSTALLED; | ||||
|     _signals.emit('extension-state-changed', extension); | ||||
|  | ||||
|     delete ExtensionUtils.extensions[extension.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); | ||||
|     global.log('Loaded extension ' + uuid); | ||||
| } | ||||
|  | ||||
| function initExtension(uuid) { | ||||
| @@ -196,43 +289,76 @@ function initExtension(uuid) { | ||||
|         throw new Error("Extension was not properly created. Call loadExtension first"); | ||||
|  | ||||
|     let extensionJs = dir.get_child('extension.js'); | ||||
|     if (!extensionJs.query_exists(null)) | ||||
|         throw new Error('Missing extension.js'); | ||||
|     if (!extensionJs.query_exists(null)) { | ||||
|         logExtensionError(uuid, 'Missing extension.js'); | ||||
|         return; | ||||
|     } | ||||
|     let stylesheetPath = null; | ||||
|     let themeContext = St.ThemeContext.get_for_stage(global.stage); | ||||
|     let theme = themeContext.get_theme(); | ||||
|     let stylesheetFile = dir.get_child('stylesheet.css'); | ||||
|     if (stylesheetFile.query_exists(null)) { | ||||
|         try { | ||||
|             theme.load_stylesheet(stylesheetFile.get_path()); | ||||
|         } catch (e) { | ||||
|             logExtensionError(uuid, 'Stylesheet parse error: ' + e); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let extensionModule; | ||||
|     let extensionState = null; | ||||
|     try { | ||||
|         ExtensionUtils.installImporter(extension); | ||||
|         extensionModule = extension.imports.extension; | ||||
|     } catch (e) { | ||||
|         if (stylesheetPath != null) | ||||
|             theme.unload_stylesheet(stylesheetPath); | ||||
|         logExtensionError(uuid, '' + e); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     ExtensionUtils.installImporter(extension); | ||||
|     extensionModule = extension.imports.extension; | ||||
|     if (!extensionModule.init) { | ||||
|         logExtensionError(uuid, 'missing \'init\' function'); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (extensionModule.init) { | ||||
|     try { | ||||
|         extensionState = extensionModule.init(extension); | ||||
|     } catch (e) { | ||||
|         if (stylesheetPath != null) | ||||
|             theme.unload_stylesheet(stylesheetPath); | ||||
|         logExtensionError(uuid, 'Failed to evaluate init function:' + e); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!extensionState) | ||||
|         extensionState = extensionModule; | ||||
|     extension.stateObj = extensionState; | ||||
|  | ||||
|     if (!extensionState.enable) { | ||||
|         logExtensionError(uuid, 'missing \'enable\' function'); | ||||
|         return; | ||||
|     } | ||||
|     if (!extensionState.disable) { | ||||
|         logExtensionError(uuid, 'missing \'disable\' function'); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     extension.state = ExtensionState.DISABLED; | ||||
|  | ||||
|     _signals.emit('extension-loaded', 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) { | ||||
|         return enabledExtensions.indexOf(uuid) == -1; | ||||
|     }).forEach(function(uuid) { | ||||
|         try { | ||||
|             enableExtension(uuid); | ||||
|         } catch(e) { | ||||
|             logExtensionError(uuid, e); | ||||
|         } | ||||
|         enableExtension(uuid); | ||||
|     }); | ||||
|  | ||||
|     // Find and disable all the newly disabled extensions: UUIDs found in the | ||||
| @@ -240,67 +366,87 @@ function onEnabledExtensionsChanged() { | ||||
|     enabledExtensions.filter(function(item) { | ||||
|         return newEnabledExtensions.indexOf(item) == -1; | ||||
|     }).forEach(function(uuid) { | ||||
|         try { | ||||
|             disableExtension(uuid); | ||||
|         } catch(e) { | ||||
|             logExtensionError(uuid, e); | ||||
|         } | ||||
|         disableExtension(uuid); | ||||
|     }); | ||||
|  | ||||
|     enabledExtensions = newEnabledExtensions; | ||||
| } | ||||
|  | ||||
| function _loadExtensions() { | ||||
| function init() { | ||||
|     ExtensionUtils.init(); | ||||
|  | ||||
|     global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged); | ||||
|     enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY); | ||||
| } | ||||
|  | ||||
|     let finder = new ExtensionUtils.ExtensionFinder(); | ||||
|     finder.connect('extension-found', function(signals, extension) { | ||||
|         try { | ||||
|             loadExtension(extension); | ||||
|         } catch(e) { | ||||
|             logExtensionError(extension.uuid, e); | ||||
|         } | ||||
| function loadExtensions() { | ||||
|     ExtensionUtils.scanExtensions(function(uuid, dir, type) { | ||||
|         let enabled = enabledExtensions.indexOf(uuid) != -1; | ||||
|         loadExtension(dir, type, enabled); | ||||
|     }); | ||||
|     finder.scanExtensions(); | ||||
| } | ||||
|  | ||||
| function enableAllExtensions() { | ||||
|     if (enabled) | ||||
|         return; | ||||
| const InstallExtensionDialog = new Lang.Class({ | ||||
|     Name: 'InstallExtensionDialog', | ||||
|     Extends: ModalDialog.ModalDialog, | ||||
|  | ||||
|     if (!initted) { | ||||
|         _loadExtensions(); | ||||
|         initted = true; | ||||
|     } else { | ||||
|         enabledExtensions.forEach(function(uuid) { | ||||
|             enableExtension(uuid); | ||||
|         }); | ||||
|     _init: function(uuid, version_tag, name) { | ||||
|         this.parent({ styleClass: 'extension-dialog' }); | ||||
|  | ||||
|         this._uuid = uuid; | ||||
|         this._version_tag = version_tag; | ||||
|         this._name = name; | ||||
|  | ||||
|         this.setButtons([{ label: _("Cancel"), | ||||
|                            action: Lang.bind(this, this._onCancelButtonPressed), | ||||
|                            key:    Clutter.Escape | ||||
|                          }, | ||||
|                          { label:  _("Install"), | ||||
|                            action: Lang.bind(this, this._onInstallButtonPressed) | ||||
|                          }]); | ||||
|  | ||||
|         let message = _("Download and install '%s' from extensions.gnome.org?").format(name); | ||||
|  | ||||
|         this._descriptionLabel = new St.Label({ text: message }); | ||||
|  | ||||
|         this.contentLayout.add(this._descriptionLabel, | ||||
|                                { y_fill:  true, | ||||
|                                  y_align: St.Align.START }); | ||||
|     }, | ||||
|  | ||||
|     _onCancelButtonPressed: function(button, event) { | ||||
|         this.close(global.get_current_time()); | ||||
|  | ||||
|         // Even though the extension is already "uninstalled", send through | ||||
|         // a state-changed signal for any users who want to know if the install | ||||
|         // went through correctly -- using proper async DBus would block more | ||||
|         // traditional clients like the plugin | ||||
|         let meta = { uuid: this._uuid, | ||||
|                      state: ExtensionState.UNINSTALLED, | ||||
|                      error: '' }; | ||||
|  | ||||
|         _signals.emit('extension-state-changed', meta); | ||||
|     }, | ||||
|  | ||||
|     _onInstallButtonPressed: function(button, event) { | ||||
|         let state = { uuid: this._uuid, | ||||
|                       state: ExtensionState.DOWNLOADING, | ||||
|                       error: '' }; | ||||
|  | ||||
|         _signals.emit('extension-state-changed', state); | ||||
|  | ||||
|         let params = { version_tag: this._version_tag, | ||||
|                        shell_version: Config.PACKAGE_VERSION, | ||||
|                        api_version: API_VERSION.toString() }; | ||||
|  | ||||
|         let url = REPOSITORY_URL_DOWNLOAD.format(this._uuid); | ||||
|         let message = Soup.form_request_new_from_hash('GET', url, params); | ||||
|  | ||||
|         _httpSession.queue_message(message, | ||||
|                                    Lang.bind(this, function(session, message) { | ||||
|                                        gotExtensionZipFile(session, message, this._uuid); | ||||
|                                    })); | ||||
|  | ||||
|         this.close(global.get_current_time()); | ||||
|     } | ||||
|     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(); | ||||
|     } | ||||
| }); | ||||
| @@ -1,228 +0,0 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const IBus = imports.gi.IBus; | ||||
| const Lang = imports.lang; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const BoxPointer = imports.ui.boxpointer; | ||||
| const Main = imports.ui.main; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
|  | ||||
| const MAX_CANDIDATES_PER_PAGE = 16; | ||||
|  | ||||
| const CandidateArea = new Lang.Class({ | ||||
|     Name: 'CandidateArea', | ||||
|     Extends: PopupMenu.PopupBaseMenuItem, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent({ reactive: false }); | ||||
|  | ||||
|         // St.Table exhibits some sizing problems so let's go with a | ||||
|         // clutter layout manager for now. | ||||
|         this._table = new Clutter.Actor(); | ||||
|         this.addActor(this._table); | ||||
|  | ||||
|         this._tableLayout = new Clutter.TableLayout(); | ||||
|         this._table.set_layout_manager(this._tableLayout); | ||||
|  | ||||
|         this._indexLabels = []; | ||||
|         this._candidateLabels = []; | ||||
|         for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) { | ||||
|             this._indexLabels.push(new St.Label({ style_class: 'candidate-index' })); | ||||
|             this._candidateLabels.push(new St.Label({ style_class: 'candidate-label' })); | ||||
|         } | ||||
|  | ||||
|         this._orientation = -1; | ||||
|         this._cursorPosition = 0; | ||||
|     }, | ||||
|  | ||||
|     _setOrientation: function(orientation) { | ||||
|         if (this._orientation == orientation) | ||||
|             return; | ||||
|  | ||||
|         this._orientation = orientation; | ||||
|  | ||||
|         this._table.remove_all_children(); | ||||
|  | ||||
|         if (this._orientation == IBus.Orientation.HORIZONTAL) | ||||
|             for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) { | ||||
|                 this._tableLayout.pack(this._indexLabels[i], i*2, 0); | ||||
|                 this._tableLayout.pack(this._candidateLabels[i], i*2 + 1, 0); | ||||
|             } | ||||
|         else                    // VERTICAL || SYSTEM | ||||
|             for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) { | ||||
|                 this._tableLayout.pack(this._indexLabels[i], 0, i); | ||||
|                 this._tableLayout.pack(this._candidateLabels[i], 1, i); | ||||
|             } | ||||
|     }, | ||||
|  | ||||
|     setCandidates: function(indexes, candidates, orientation, cursorPosition, cursorVisible) { | ||||
|         this._setOrientation(orientation); | ||||
|  | ||||
|         for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) { | ||||
|             let visible = i < candidates.length; | ||||
|             this._indexLabels[i].visible = visible; | ||||
|             this._candidateLabels[i].visible = visible; | ||||
|  | ||||
|             if (!visible) | ||||
|                 continue; | ||||
|  | ||||
|             this._indexLabels[i].text = ((indexes && indexes[i]) ? indexes[i] : '%x.'.format(i + 1)); | ||||
|             this._candidateLabels[i].text = candidates[i]; | ||||
|         } | ||||
|  | ||||
|         this._candidateLabels[this._cursorPosition].remove_style_pseudo_class('selected'); | ||||
|         this._cursorPosition = cursorPosition; | ||||
|         if (cursorVisible) | ||||
|             this._candidateLabels[cursorPosition].add_style_pseudo_class('selected'); | ||||
|     }, | ||||
| }); | ||||
|  | ||||
| const CandidatePopup = new Lang.Class({ | ||||
|     Name: 'CandidatePopup', | ||||
|     Extends: PopupMenu.PopupMenu, | ||||
|  | ||||
|     _init: function() { | ||||
|         this._cursor = new St.Bin({ opacity: 0 }); | ||||
|         Main.uiGroup.add_actor(this._cursor); | ||||
|  | ||||
|         this.parent(this._cursor, 0, St.Side.TOP); | ||||
|         this.actor.hide(); | ||||
|         Main.uiGroup.add_actor(this.actor); | ||||
|  | ||||
|         this._preeditTextItem = new PopupMenu.PopupMenuItem('', { reactive: false }); | ||||
|         this._preeditTextItem.actor.hide(); | ||||
|         this.addMenuItem(this._preeditTextItem); | ||||
|  | ||||
|         this._auxTextItem = new PopupMenu.PopupMenuItem('', { reactive: false }); | ||||
|         this._auxTextItem.actor.hide(); | ||||
|         this.addMenuItem(this._auxTextItem); | ||||
|  | ||||
|         this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|  | ||||
|         this._lookupTableItem = new CandidateArea(); | ||||
|         this._lookupTableItem.actor.hide(); | ||||
|         this.addMenuItem(this._lookupTableItem); | ||||
|  | ||||
|         this._panelService = null; | ||||
|     }, | ||||
|  | ||||
|     setPanelService: function(panelService) { | ||||
|         this._panelService = panelService; | ||||
|         if (!panelService) | ||||
|             return; | ||||
|  | ||||
|         panelService.connect('set-cursor-location', | ||||
|                              Lang.bind(this, function(ps, x, y, w, h) { | ||||
|                                  this._cursor.set_position(x, y); | ||||
|                                  this._cursor.set_size(w, h); | ||||
|                              })); | ||||
|         panelService.connect('update-preedit-text', | ||||
|                              Lang.bind(this, function(ps, text, cursorPosition, visible) { | ||||
|                                  if (visible) | ||||
|                                      this._preeditTextItem.actor.show(); | ||||
|                                  else | ||||
|                                      this._preeditTextItem.actor.hide(); | ||||
|                                  this._updateVisibility(); | ||||
|  | ||||
|                                  this._preeditTextItem.actor.label_actor.text = text.get_text(); | ||||
|  | ||||
|                                  let attrs = text.get_attributes(); | ||||
|                                  if (attrs) | ||||
|                                      this._setTextAttributes(this._preeditTextItem.actor.label_actor.clutter_text, | ||||
|                                                              attrs); | ||||
|                              })); | ||||
|         panelService.connect('show-preedit-text', | ||||
|                              Lang.bind(this, function(ps) { | ||||
|                                  this._preeditTextItem.actor.show(); | ||||
|                                  this._updateVisibility(); | ||||
|                              })); | ||||
|         panelService.connect('hide-preedit-text', | ||||
|                              Lang.bind(this, function(ps) { | ||||
|                                  this._preeditTextItem.actor.hide(); | ||||
|                                  this._updateVisibility(); | ||||
|                              })); | ||||
|         panelService.connect('update-auxiliary-text', | ||||
|                              Lang.bind(this, function(ps, text, visible) { | ||||
|                                  if (visible) | ||||
|                                      this._auxTextItem.actor.show(); | ||||
|                                  else | ||||
|                                      this._auxTextItem.actor.hide(); | ||||
|                                  this._updateVisibility(); | ||||
|  | ||||
|                                  this._auxTextItem.actor.label_actor.text = text.get_text(); | ||||
|                              })); | ||||
|         panelService.connect('show-auxiliary-text', | ||||
|                              Lang.bind(this, function(ps) { | ||||
|                                  this._auxTextItem.actor.show(); | ||||
|                                  this._updateVisibility(); | ||||
|                              })); | ||||
|         panelService.connect('hide-auxiliary-text', | ||||
|                              Lang.bind(this, function(ps) { | ||||
|                                  this._auxTextItem.actor.hide(); | ||||
|                                  this._updateVisibility(); | ||||
|                              })); | ||||
|         panelService.connect('update-lookup-table', | ||||
|                              Lang.bind(this, function(ps, lookupTable, visible) { | ||||
|                                  if (visible) | ||||
|                                      this._lookupTableItem.actor.show(); | ||||
|                                  else | ||||
|                                      this._lookupTableItem.actor.hide(); | ||||
|                                  this._updateVisibility(); | ||||
|  | ||||
|                                  let cursorPos = lookupTable.get_cursor_pos(); | ||||
|                                  let pageSize = lookupTable.get_page_size(); | ||||
|                                  let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize)); | ||||
|                                  let startIndex = page * pageSize; | ||||
|                                  let endIndex = Math.min((page + 1) * pageSize, | ||||
|                                                          lookupTable.get_number_of_candidates()); | ||||
|                                  let indexes = []; | ||||
|                                  let indexLabel; | ||||
|                                  for (let i = 0; indexLabel = lookupTable.get_label(i); ++i) | ||||
|                                       indexes.push(indexLabel.get_text()); | ||||
|  | ||||
|                                  let candidates = []; | ||||
|                                  for (let i = startIndex; i < endIndex; ++i) | ||||
|                                      candidates.push(lookupTable.get_candidate(i).get_text()); | ||||
|  | ||||
|                                  this._lookupTableItem.setCandidates(indexes, | ||||
|                                                                      candidates, | ||||
|                                                                      lookupTable.get_orientation(), | ||||
|                                                                      cursorPos % pageSize, | ||||
|                                                                      lookupTable.is_cursor_visible()); | ||||
|                              })); | ||||
|         panelService.connect('show-lookup-table', | ||||
|                              Lang.bind(this, function(ps) { | ||||
|                                  this._lookupTableItem.actor.show(); | ||||
|                                  this._updateVisibility(); | ||||
|                              })); | ||||
|         panelService.connect('hide-lookup-table', | ||||
|                              Lang.bind(this, function(ps) { | ||||
|                                  this._lookupTableItem.actor.hide(); | ||||
|                                  this._updateVisibility(); | ||||
|                              })); | ||||
|         panelService.connect('focus-out', | ||||
|                              Lang.bind(this, function(ps) { | ||||
|                                  this.close(BoxPointer.PopupAnimation.NONE); | ||||
|                              })); | ||||
|     }, | ||||
|  | ||||
|     _updateVisibility: function() { | ||||
|         let isVisible = (this._preeditTextItem.actor.visible || | ||||
|                          this._auxTextItem.actor.visible || | ||||
|                          this._lookupTableItem.actor.visible); | ||||
|  | ||||
|         if (isVisible) | ||||
|             this.open(BoxPointer.PopupAnimation.NONE); | ||||
|         else | ||||
|             this.close(BoxPointer.PopupAnimation.NONE); | ||||
|     }, | ||||
|  | ||||
|     _setTextAttributes: function(clutterText, ibusAttrList) { | ||||
|         let attr; | ||||
|         for (let i = 0; attr = ibusAttrList.get(i); ++i) | ||||
|             if (attr.get_attr_type() == IBus.AttrType.BACKGROUND) | ||||
|                 clutterText.set_selection(attr.get_start_index(), attr.get_end_index()); | ||||
|     } | ||||
| }); | ||||
| @@ -282,10 +282,6 @@ const IconGrid = new Lang.Class({ | ||||
|         return this._computeLayout(rowWidth)[0]; | ||||
|     }, | ||||
|  | ||||
|     getRowLimit: function() { | ||||
|         return this._rowLimit; | ||||
|     }, | ||||
|  | ||||
|     _computeLayout: function (forWidth) { | ||||
|         let nColumns = 0; | ||||
|         let usedWidth = 0; | ||||
| @@ -309,22 +305,21 @@ const IconGrid = new Lang.Class({ | ||||
|         this._grid.queue_relayout(); | ||||
|     }, | ||||
|  | ||||
|     removeAll: function() { | ||||
|         this._grid.destroy_all_children(); | ||||
|     removeAll: function () { | ||||
|         this._grid.get_children().forEach(Lang.bind(this, function (child) { | ||||
|             child.destroy(); | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     addItem: function(actor, index) { | ||||
|         if (index !== undefined) | ||||
|             this._grid.insert_child_at_index(actor, index); | ||||
|         else | ||||
|             this._grid.add_actor(actor); | ||||
|     addItem: function(actor) { | ||||
|         this._grid.add_actor(actor); | ||||
|     }, | ||||
|  | ||||
|     getItemAtIndex: function(index) { | ||||
|         return this._grid.get_child_at_index(index); | ||||
|         return this._grid.get_children()[index]; | ||||
|     }, | ||||
|  | ||||
|     visibleItemsCount: function() { | ||||
|         return this._grid.get_n_children() - this._grid.get_n_skip_paint(); | ||||
|         return this._grid.get_children().length - this._grid.get_n_skip_paint(); | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -93,7 +93,7 @@ const Key = new Lang.Class({ | ||||
|             this._getExtendedKeys(); | ||||
|             this.actor._extended_keys = this._extended_keyboard; | ||||
|             this._boxPointer.actor.hide(); | ||||
|             Main.layoutManager.addChrome(this._boxPointer.actor); | ||||
|             Main.layoutManager.addChrome(this._boxPointer.actor, { visibleInFullscreen: true }); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -175,7 +175,7 @@ const Key = new Lang.Class({ | ||||
|             this.actor.fake_release(); | ||||
|             this._boxPointer.actor.raise_top(); | ||||
|             this._boxPointer.setPosition(this.actor, 0.5); | ||||
|             this._boxPointer.show(BoxPointer.PopupAnimation.FULL); | ||||
|             this._boxPointer.show(true); | ||||
|             this.actor.set_hover(false); | ||||
|             if (!this._grabbed) { | ||||
|                  Main.pushModal(this.actor); | ||||
| @@ -186,7 +186,7 @@ const Key = new Lang.Class({ | ||||
|         } else { | ||||
|             if (this._grabbed) | ||||
|                 this._ungrab(); | ||||
|             this._boxPointer.hide(BoxPointer.PopupAnimation.FULL); | ||||
|             this._boxPointer.hide(true); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
| @@ -200,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 }); | ||||
| @@ -290,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(); | ||||
| @@ -349,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; | ||||
|     }, | ||||
| @@ -485,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(); | ||||
|  | ||||
| @@ -517,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(); | ||||
|     }, | ||||
|  | ||||
| @@ -548,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(); | ||||
|     }, | ||||
|  | ||||
| @@ -581,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() { | ||||
|   | ||||
| @@ -65,8 +65,7 @@ const KeyringDialog = new Lang.Class({ | ||||
|                          key:    Clutter.Escape | ||||
|                        }, | ||||
|                        { label: '', | ||||
|                          action: Lang.bind(this, this._onContinueButton), | ||||
|                          default: true | ||||
|                          action: Lang.bind(this, this._onContinueButton) | ||||
|                        }] | ||||
| 
 | ||||
|         this.setButtons(buttons); | ||||
| @@ -84,10 +83,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 +99,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 +185,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); | ||||
| } | ||||
							
								
								
									
										227
									
								
								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; | ||||
| @@ -12,88 +11,12 @@ const St = imports.gi.St; | ||||
| const DND = imports.ui.dnd; | ||||
| const Main = imports.ui.main; | ||||
| const Params = imports.misc.params; | ||||
| const ScreenSaver = imports.misc.screenSaver; | ||||
| 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,46 +27,32 @@ const LayoutManager = new Lang.Class({ | ||||
|         this.primaryMonitor = null; | ||||
|         this.primaryIndex = -1; | ||||
|         this._hotCorners = []; | ||||
|         this._rootPixmap = null; | ||||
|         this._leftPanelBarrier = 0; | ||||
|         this._rightPanelBarrier = 0; | ||||
|         this._trayBarrier = 0; | ||||
|  | ||||
|         this._chrome = new Chrome(this); | ||||
|  | ||||
|         this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup', | ||||
|                                                  visible: false, | ||||
|                                                  clip_to_allocation: true, | ||||
|                                                  layout_manager: new Clutter.BinLayout(), | ||||
|                                                }); | ||||
|         this.addChrome(this.screenShieldGroup); | ||||
|  | ||||
|         this.panelBox = new St.BoxLayout({ name: 'panelBox', | ||||
|                                            vertical: true }); | ||||
|         this.addChrome(this.panelBox, { affectsStruts: true, | ||||
|                                         trackFullscreen: true }); | ||||
|         this.addChrome(this.panelBox, { affectsStruts: true }); | ||||
|         this.panelBox.connect('allocation-changed', | ||||
|                               Lang.bind(this, this._updatePanelBarriers)); | ||||
|  | ||||
|         this.trayBox = new St.Widget({ name: 'trayBox', | ||||
|                                        layout_manager: new Clutter.BinLayout() });  | ||||
|         this.addChrome(this.trayBox); | ||||
|         this.trayBox = new St.BoxLayout({ name: 'trayBox' });  | ||||
|         this.addChrome(this.trayBox, { visibleInFullscreen: true }); | ||||
|         this.trayBox.connect('allocation-changed', | ||||
|                              Lang.bind(this, this._updateTrayBarrier)); | ||||
|  | ||||
|         this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox', | ||||
|                                               reactive: true, | ||||
|                                               track_hover: true }); | ||||
|         this.addChrome(this.keyboardBox); | ||||
|         this.addChrome(this.keyboardBox, { visibleInFullscreen: true }); | ||||
|         this._keyboardHeightNotifyId = 0; | ||||
|  | ||||
|         global.screen.connect('monitors-changed', | ||||
|                               Lang.bind(this, this._monitorsChanged)); | ||||
|         this._monitorsChanged(); | ||||
|  | ||||
|         this._chrome.connect('primary-fullscreen-changed', Lang.bind(this, function(chrome, state) { | ||||
|             this.emit('primary-fullscreen-changed', state); | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     // This is called by Main after everything else is constructed; | ||||
| @@ -236,9 +145,6 @@ const LayoutManager = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _updateBoxes: function() { | ||||
|         this.screenShieldGroup.set_position(0, 0); | ||||
|         this.screenShieldGroup.set_size(global.screen_width, global.screen_height); | ||||
|  | ||||
|         this.panelBox.set_position(this.primaryMonitor.x, this.primaryMonitor.y); | ||||
|         this.panelBox.set_size(this.primaryMonitor.width, -1); | ||||
|  | ||||
| @@ -318,47 +224,33 @@ const LayoutManager = new Lang.Class({ | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     get currentMonitor() { | ||||
|         let index = global.screen.get_current_monitor(); | ||||
|         return this.monitors[index]; | ||||
|     }, | ||||
|     get focusIndex() { | ||||
|         let focusWindow = global.display.focus_window; | ||||
|  | ||||
|     _startupAnimation: function() { | ||||
|         this.panelBox.anchor_y = this.panelBox.height; | ||||
|         if (focusWindow) { | ||||
|             let wrect = focusWindow.get_outer_rect(); | ||||
|             for (let i = 0; i < this.monitors.length; i++) { | ||||
|                 let monitor = this.monitors[i]; | ||||
|  | ||||
|         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._rootPixmap = global.create_xrootpmap_texture(); | ||||
|             if (this._rootPixmap != null) { | ||||
|                 Main.uiGroup.add_actor(this._rootPixmap); | ||||
|                 Tweener.addTween(this._rootPixmap, | ||||
|                                  { opacity: 0, | ||||
|                                    time: PLYMOUTH_TRANSITION_TIME, | ||||
|                                    transition: 'linear', | ||||
|                                    onComplete: this._fadeRootpmapComplete, | ||||
|                                    onCompleteScope: this }); | ||||
|                 plymouthTransitionRunning = true; | ||||
|                 if (monitor.x <= wrect.x && monitor.y <= wrect.y && | ||||
|                     monitor.x + monitor.width > wrect.x && | ||||
|                     monitor.y + monitor.height > wrect.y) | ||||
|                     return i; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (!plymouthTransitionRunning) | ||||
|             this._fadeRootpmapComplete(); | ||||
|         return this.primaryIndex; | ||||
|     }, | ||||
|  | ||||
|     _fadeRootpmapComplete: function() { | ||||
|     get focusMonitor() { | ||||
|         return this.monitors[this.focusIndex]; | ||||
|     }, | ||||
|  | ||||
|     _startupAnimation: function() { | ||||
|         // Don't animate the strut | ||||
|         this._chrome.freezeUpdateRegions(); | ||||
|  | ||||
|         if (this._rootPixmap != null) { | ||||
|             this._rootPixmap.destroy(); | ||||
|             this._rootPixmap = null; | ||||
|         } | ||||
|  | ||||
|         this.panelBox.anchor_y = this.panelBox.height; | ||||
|         Tweener.addTween(this.panelBox, | ||||
|                          { anchor_y: 0, | ||||
|                            time: STARTUP_ANIMATION_TIME, | ||||
| @@ -373,6 +265,7 @@ const LayoutManager = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     showKeyboard: function () { | ||||
|         Main.messageTray.hide(); | ||||
|         this.keyboardBox.raise_top(); | ||||
|         Tweener.addTween(this.keyboardBox, | ||||
|                          { anchor_y: this.keyboardBox.height, | ||||
| @@ -386,8 +279,6 @@ const LayoutManager = new Lang.Class({ | ||||
|                            time: KEYBOARD_ANIMATION_TIME, | ||||
|                            transition: 'easeOutQuad' | ||||
|                          }); | ||||
|  | ||||
|         this.emit('keyboard-visible-changed', true); | ||||
|     }, | ||||
|  | ||||
|     _showKeyboardComplete: function() { | ||||
| @@ -402,6 +293,7 @@ const LayoutManager = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     hideKeyboard: function (immediate) { | ||||
|         Main.messageTray.hide(); | ||||
|         if (this._keyboardHeightNotifyId) { | ||||
|             this.keyboardBox.disconnect(this._keyboardHeightNotifyId); | ||||
|             this._keyboardHeightNotifyId = 0; | ||||
| @@ -418,8 +310,6 @@ const LayoutManager = new Lang.Class({ | ||||
|                            time: immediate ? 0 : KEYBOARD_ANIMATION_TIME, | ||||
|                            transition: 'easeOutQuad' | ||||
|                          }); | ||||
|  | ||||
|         this.emit('keyboard-visible-changed', false); | ||||
|     }, | ||||
|  | ||||
|     _hideKeyboardComplete: function() { | ||||
| @@ -441,10 +331,8 @@ const LayoutManager = new Lang.Class({ | ||||
|     // the window manager struts. Changes to @actor's visibility will | ||||
|     // NOT affect whether or not the strut is present, however. | ||||
|     // | ||||
|     // If %trackFullscreen in @params is %true, the actor's visibility | ||||
|     // will be bound to the presence of fullscreen windows on the same | ||||
|     // monitor (it will be hidden whenever a fullscreen window is visible, | ||||
|     // and shown otherwise) | ||||
|     // If %visibleInFullscreen in @params is %true, the actor will be | ||||
|     // visible even when a fullscreen window should be covering it. | ||||
|     addChrome: function(actor, params) { | ||||
|         this._chrome.addActor(actor, params); | ||||
|     }, | ||||
| @@ -458,8 +346,10 @@ const LayoutManager = new Lang.Class({ | ||||
|     // struts or input region to cover specific children. | ||||
|     // | ||||
|     // @params can have any of the same values as in addChrome(), | ||||
|     // though some possibilities don't make sense. By default, @actor has | ||||
|     // the same params as its chrome ancestor. | ||||
|     // though some possibilities don't make sense (eg, trying to have | ||||
|     // a %visibleInFullscreen child of a non-%visibleInFullscreen | ||||
|     // parent). By default, @actor has the same params as its chrome | ||||
|     // ancestor. | ||||
|     trackChrome: function(actor, params) { | ||||
|         this._chrome.trackActor(actor, params); | ||||
|     }, | ||||
| @@ -665,7 +555,7 @@ const HotCorner = new Lang.Class({ | ||||
| // workspace content. | ||||
|  | ||||
| const defaultParams = { | ||||
|     trackFullscreen: false, | ||||
|     visibleInFullscreen: false, | ||||
|     affectsStruts: false, | ||||
|     affectsInputRegion: true | ||||
| }; | ||||
| @@ -692,13 +582,24 @@ const Chrome = new Lang.Class({ | ||||
|         global.screen.connect('notify::n-workspaces', | ||||
|                               Lang.bind(this, this._queueUpdateRegions)); | ||||
|  | ||||
|         this._screenSaverActive = false; | ||||
|         this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy(); | ||||
|         this._screenSaverProxy.connectSignal('ActiveChanged', Lang.bind(this, function(proxy, senderName, [isActive]) { | ||||
|             this._onScreenSaverActiveChanged(isActive); | ||||
|         })); | ||||
|         this._screenSaverProxy.GetActiveRemote(Lang.bind(this, function(result, err) { | ||||
|             if (!err) | ||||
|                 this._onScreenSaverActiveChanged(result[0]); | ||||
|         })); | ||||
|  | ||||
|         this._relayout(); | ||||
|     }, | ||||
|  | ||||
|     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)); | ||||
|     }, | ||||
|  | ||||
|     addActor: function(actor, params) { | ||||
| @@ -784,30 +685,28 @@ 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() { | ||||
|         for (let i = 0; i < this._trackedActors.length; i++) { | ||||
|             let actorData = this._trackedActors[i], visible; | ||||
|             if (!actorData.trackFullscreen) | ||||
|                 continue; | ||||
|             if (!actorData.isToplevel) | ||||
|                 continue; | ||||
|  | ||||
|             if (this._inOverview || !Main.sessionMode.hasWindows) | ||||
|             if (this._screenSaverActive) | ||||
|                 visible = false; | ||||
|             else if (this._inOverview) | ||||
|                 visible = true; | ||||
|             else if (this.findMonitorForActor(actorData.actor).inFullscreen) | ||||
|             else if (!actorData.visibleInFullscreen && | ||||
|                      this.findMonitorForActor(actorData.actor).inFullscreen) | ||||
|                 visible = false; | ||||
|             else | ||||
|                 visible = true; | ||||
|             actorData.actor.visible = visible; | ||||
|             Main.uiGroup.set_skip_paint(actorData.actor, !visible); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -823,11 +722,6 @@ const Chrome = new Lang.Class({ | ||||
|         this._queueUpdateRegions(); | ||||
|     }, | ||||
|  | ||||
|     _sessionUpdated: function() { | ||||
|         this._updateVisibility(); | ||||
|         this._queueUpdateRegions(); | ||||
|     }, | ||||
|  | ||||
|     _relayout: function() { | ||||
|         this._monitors = this._layoutManager.monitors; | ||||
|         this._primaryMonitor = this._layoutManager.primaryMonitor; | ||||
| @@ -837,6 +731,12 @@ const Chrome = new Lang.Class({ | ||||
|         this._queueUpdateRegions(); | ||||
|     }, | ||||
|  | ||||
|     _onScreenSaverActiveChanged: function(screenSaverActive) { | ||||
|         this._screenSaverActive = screenSaverActive; | ||||
|         this._updateVisibility(); | ||||
|         this._queueUpdateRegions(); | ||||
|     }, | ||||
|  | ||||
|     _findMonitorForRect: function(x, y, w, h) { | ||||
|         // First look at what monitor the center of the rectangle is at | ||||
|         let cx = x + w/2; | ||||
| @@ -952,8 +852,6 @@ const Chrome = new Lang.Class({ | ||||
|         for (let i = 0; i < this._monitors.length; i++) | ||||
|             wasInFullscreen[i] = this._monitors[i].inFullscreen; | ||||
|  | ||||
|         let primaryWasInFullscreen = this._primaryMonitor.inFullscreen; | ||||
|  | ||||
|         this._updateFullscreen(); | ||||
|  | ||||
|         let changed = false; | ||||
| @@ -963,15 +861,10 @@ const Chrome = new Lang.Class({ | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (changed) { | ||||
|             this._updateVisibility(); | ||||
|             this._queueUpdateRegions(); | ||||
|         } | ||||
|  | ||||
|         if (primaryWasInFullscreen != this._primaryMonitor.inFullscreen) { | ||||
|             this.emit('primary-fullscreen-changed', this._primaryMonitor.inFullscreen); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     updateRegions: function() { | ||||
| @@ -1086,5 +979,3 @@ const Chrome = new Lang.Class({ | ||||
|         return false; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| Signals.addSignalMethods(Chrome.prototype); | ||||
|   | ||||
							
								
								
									
										21
									
								
								js/ui/link.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,21 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Lang = imports.lang; | ||||
| const Signals = imports.signals; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const Link = new Lang.Class({ | ||||
|     Name: 'Link', | ||||
|  | ||||
|     _init : function(props) { | ||||
|         let realProps = { reactive: true, | ||||
|                           track_hover: true, | ||||
|                           style_class: 'shell-link' }; | ||||
|         // The user can pass in reactive: false to override the above and get | ||||
|         // a non-reactive link (a link to the current page, perhaps) | ||||
|         Lang.copyProperties(props, realProps); | ||||
|  | ||||
|         this.actor = new St.Button(realProps); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(Link.prototype); | ||||
| @@ -12,18 +12,16 @@ const Shell = imports.gi.Shell; | ||||
| const Signals = imports.signals; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const System = imports.system; | ||||
|  | ||||
| const History = imports.misc.history; | ||||
| const ExtensionSystem = imports.ui.extensionSystem; | ||||
| const ExtensionUtils = imports.misc.extensionUtils; | ||||
| const Link = imports.ui.link; | ||||
| const ShellEntry = imports.ui.shellEntry; | ||||
| const Tweener = imports.ui.tweener; | ||||
| const Main = imports.ui.main; | ||||
| const JsParse = imports.misc.jsParse; | ||||
|  | ||||
| const CHEVRON = '>>> '; | ||||
|  | ||||
| /* Imports...feel free to add here as needed */ | ||||
| var commandHeader = 'const Clutter = imports.gi.Clutter; ' + | ||||
|                     'const GLib = imports.gi.GLib; ' + | ||||
| @@ -38,8 +36,9 @@ 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 it = Main.lookingGlass.getIt(); ' + | ||||
|                     'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); '; | ||||
|  | ||||
| const HISTORY_KEY = 'looking-glass-history'; | ||||
| @@ -262,8 +261,9 @@ function objectToString(o) { | ||||
|  | ||||
| const ObjLink = new Lang.Class({ | ||||
|     Name: 'ObjLink', | ||||
|     Extends: Link.Link, | ||||
|  | ||||
|     _init: function(lookingGlass, o, title) { | ||||
|     _init: function(o, title) { | ||||
|         let text; | ||||
|         if (title) | ||||
|             text = title; | ||||
| @@ -272,30 +272,24 @@ const ObjLink = new Lang.Class({ | ||||
|         text = GLib.markup_escape_text(text, -1); | ||||
|         this._obj = o; | ||||
|  | ||||
|         this.actor = new St.Button({ reactive: true, | ||||
|                                      track_hover: true, | ||||
|                                      style_class: 'shell-link', | ||||
|                                      label: text }); | ||||
|         this.parent({ label: text }); | ||||
|         this.actor.get_child().single_line_mode = true; | ||||
|         this.actor.connect('clicked', Lang.bind(this, this._onClicked)); | ||||
|  | ||||
|         this._lookingGlass = lookingGlass; | ||||
|     }, | ||||
|  | ||||
|     _onClicked: function (link) { | ||||
|         this._lookingGlass.inspectObject(this._obj, this.actor); | ||||
|         Main.lookingGlass.inspectObject(this._obj, this.actor); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const Result = new Lang.Class({ | ||||
|     Name: 'Result', | ||||
|  | ||||
|     _init: function(lookingGlass, command, o, index) { | ||||
|     _init : function(command, o, index) { | ||||
|         this.index = index; | ||||
|         this.o = o; | ||||
|  | ||||
|         this.actor = new St.BoxLayout({ vertical: true }); | ||||
|         this._lookingGlass = lookingGlass; | ||||
|  | ||||
|         let cmdTxt = new St.Label({ text: command }); | ||||
|         cmdTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END; | ||||
| @@ -305,7 +299,7 @@ const Result = new Lang.Class({ | ||||
|         let resultTxt = new St.Label({ text: 'r(' + index + ') = ' }); | ||||
|         resultTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END; | ||||
|         box.add(resultTxt); | ||||
|         let objLink = new ObjLink(this._lookingGlass, o); | ||||
|         let objLink = new ObjLink(o); | ||||
|         box.add(objLink.actor); | ||||
|         let line = new Clutter.Rectangle({ name: 'Separator' }); | ||||
|         let padBin = new St.Bin({ name: 'Separator', x_fill: true, y_fill: true }); | ||||
| @@ -317,18 +311,16 @@ const Result = new Lang.Class({ | ||||
| const WindowList = new Lang.Class({ | ||||
|     Name: 'WindowList', | ||||
|  | ||||
|     _init: function(lookingGlass) { | ||||
|     _init : function () { | ||||
|         this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' }); | ||||
|         let tracker = Shell.WindowTracker.get_default(); | ||||
|         this._updateId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._updateWindowList)); | ||||
|         global.display.connect('window-created', Lang.bind(this, this._updateWindowList)); | ||||
|         tracker.connect('tracked-windows-changed', Lang.bind(this, this._updateWindowList)); | ||||
|  | ||||
|         this._lookingGlass = lookingGlass; | ||||
|     }, | ||||
|  | ||||
|     _updateWindowList: function() { | ||||
|         this.actor.destroy_all_children(); | ||||
|         this.actor.get_children().forEach(function (actor) { actor.destroy(); }); | ||||
|         let windows = global.get_window_actors(); | ||||
|         let tracker = Shell.WindowTracker.get_default(); | ||||
|         for (let i = 0; i < windows.length; i++) { | ||||
| @@ -340,7 +332,7 @@ const WindowList = new Lang.Class({ | ||||
|             } | ||||
|             let box = new St.BoxLayout({ vertical: true }); | ||||
|             this.actor.add(box); | ||||
|             let windowLink = new ObjLink(this._lookingGlass, metaWindow, metaWindow.title); | ||||
|             let windowLink = new ObjLink(metaWindow, metaWindow.title); | ||||
|             box.add(windowLink.actor, { x_align: St.Align.START, x_fill: false }); | ||||
|             let propsBox = new St.BoxLayout({ vertical: true, style: 'padding-left: 6px;' }); | ||||
|             box.add(propsBox); | ||||
| @@ -351,7 +343,7 @@ const WindowList = new Lang.Class({ | ||||
|                 let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' }); | ||||
|                 propsBox.add(propBox); | ||||
|                 propBox.add(new St.Label({ text: 'app: ' }), { y_fill: false }); | ||||
|                 let appLink = new ObjLink(this._lookingGlass, app, app.get_id()); | ||||
|                 let appLink = new ObjLink(app, app.get_id()); | ||||
|                 propBox.add(appLink.actor, { y_fill: false }); | ||||
|                 propBox.add(icon, { y_fill: false }); | ||||
|             } else { | ||||
| @@ -365,21 +357,18 @@ Signals.addSignalMethods(WindowList.prototype); | ||||
| const ObjInspector = new Lang.Class({ | ||||
|     Name: 'ObjInspector', | ||||
|  | ||||
|     _init: function(lookingGlass) { | ||||
|     _init : function () { | ||||
|         this._obj = null; | ||||
|         this._previousObj = null; | ||||
|  | ||||
|         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', | ||||
|                                              vertical: true }); | ||||
|         this.actor.add_actor(this._container); | ||||
|  | ||||
|         this._lookingGlass = lookingGlass; | ||||
|     }, | ||||
|  | ||||
|     selectObject: function(obj, skipPrevious) { | ||||
| @@ -389,7 +378,7 @@ const ObjInspector = new Lang.Class({ | ||||
|             this._previousObj = null; | ||||
|         this._obj = obj; | ||||
|  | ||||
|         this._container.destroy_all_children(); | ||||
|         this._container.get_children().forEach(function (child) { child.destroy(); }); | ||||
|  | ||||
|         let hbox = new St.BoxLayout({ style_class: 'lg-obj-inspector-title' }); | ||||
|         this._container.add_actor(hbox); | ||||
| @@ -423,7 +412,7 @@ const ObjInspector = new Lang.Class({ | ||||
|                 let link; | ||||
|                 try { | ||||
|                     let prop = obj[propName]; | ||||
|                     link = new ObjLink(this._lookingGlass, prop).actor; | ||||
|                     link = new ObjLink(prop).actor; | ||||
|                 } catch (e) { | ||||
|                     link = new St.Label({ text: '<error>' }); | ||||
|                 } | ||||
| @@ -444,6 +433,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 }); | ||||
| @@ -464,7 +457,7 @@ const ObjInspector = new Lang.Class({ | ||||
|     _onInsert: function() { | ||||
|         let obj = this._obj; | ||||
|         this.close(); | ||||
|         this._lookingGlass.insertObject(obj); | ||||
|         Main.lookingGlass.insertObject(obj); | ||||
|     }, | ||||
|  | ||||
|     _onBack: function() { | ||||
| @@ -472,36 +465,34 @@ const ObjInspector = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const RedBorderEffect = new Lang.Class({ | ||||
|     Name: 'RedBorderEffect', | ||||
|     Extends: Clutter.Effect, | ||||
| function addBorderPaintHook(actor) { | ||||
|     let signalId = actor.connect_after('paint', | ||||
|         function () { | ||||
|             let color = new Cogl.Color(); | ||||
|             color.init_from_4ub(0xff, 0, 0, 0xc4); | ||||
|             Cogl.set_source_color(color); | ||||
|  | ||||
|     vfunc_paint: function() { | ||||
|         let actor = this.get_actor(); | ||||
|         actor.continue_paint(); | ||||
|             let geom = actor.get_allocation_geometry(); | ||||
|             let width = 2; | ||||
|  | ||||
|         let color = new Cogl.Color(); | ||||
|         color.init_from_4ub(0xff, 0, 0, 0xc4); | ||||
|         Cogl.set_source_color(color); | ||||
|             // clockwise order | ||||
|             Cogl.rectangle(0, 0, geom.width, width); | ||||
|             Cogl.rectangle(geom.width - width, width, | ||||
|                            geom.width, geom.height); | ||||
|             Cogl.rectangle(0, geom.height, | ||||
|                            geom.width - width, geom.height - width); | ||||
|             Cogl.rectangle(0, geom.height - width, | ||||
|                            width, width); | ||||
|         }); | ||||
|  | ||||
|         let geom = actor.get_allocation_geometry(); | ||||
|         let width = 2; | ||||
|  | ||||
|         // clockwise order | ||||
|         Cogl.rectangle(0, 0, geom.width, width); | ||||
|         Cogl.rectangle(geom.width - width, width, | ||||
|                        geom.width, geom.height); | ||||
|         Cogl.rectangle(0, geom.height, | ||||
|                        geom.width - width, geom.height - width); | ||||
|         Cogl.rectangle(0, geom.height - width, | ||||
|                        width, width); | ||||
|     }, | ||||
| }); | ||||
|     actor.queue_redraw(); | ||||
|     return signalId; | ||||
| } | ||||
|  | ||||
| const Inspector = new Lang.Class({ | ||||
|     Name: 'Inspector', | ||||
|  | ||||
|     _init: function(lookingGlass) { | ||||
|     _init: function() { | ||||
|         let container = new Shell.GenericContainer({ width: 0, | ||||
|                                                      height: 0 }); | ||||
|         container.connect('allocate', Lang.bind(this, this._allocate)); | ||||
| @@ -515,6 +506,9 @@ const Inspector = new Lang.Class({ | ||||
|         this._displayText = new St.Label(); | ||||
|         eventHandler.add(this._displayText, { expand: true }); | ||||
|  | ||||
|         this._borderPaintTarget = null; | ||||
|         this._borderPaintId = null; | ||||
|         eventHandler.connect('destroy', Lang.bind(this, this._onDestroy)); | ||||
|         eventHandler.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent)); | ||||
|         eventHandler.connect('button-press-event', Lang.bind(this, this._onButtonPressEvent)); | ||||
|         eventHandler.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); | ||||
| @@ -529,8 +523,6 @@ const Inspector = new Lang.Class({ | ||||
|         // out, or move the pointer outside of _pointerTarget. | ||||
|         this._target = null; | ||||
|         this._pointerTarget = null; | ||||
|  | ||||
|         this._lookingGlass = lookingGlass; | ||||
|     }, | ||||
|  | ||||
|     _allocate: function(actor, box, flags) { | ||||
| @@ -551,13 +543,18 @@ const Inspector = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _close: function() { | ||||
|         Clutter.ungrab_pointer(); | ||||
|         Clutter.ungrab_keyboard(); | ||||
|         Clutter.ungrab_pointer(this._eventHandler); | ||||
|         Clutter.ungrab_keyboard(this._eventHandler); | ||||
|         this._eventHandler.destroy(); | ||||
|         this._eventHandler = null; | ||||
|         this.emit('closed'); | ||||
|     }, | ||||
|  | ||||
|     _onDestroy: function() { | ||||
|         if (this._borderPaintTarget != null) | ||||
|             this._borderPaintTarget.disconnect(this._borderPaintId); | ||||
|     }, | ||||
|  | ||||
|     _onKeyPressEvent: function (actor, event) { | ||||
|         if (event.get_key_symbol() == Clutter.Escape) | ||||
|             this._close(); | ||||
| @@ -626,7 +623,12 @@ const Inspector = new Lang.Class({ | ||||
|         this._displayText.text = ''; | ||||
|         this._displayText.text = position + ' ' + this._target; | ||||
|  | ||||
|         this._lookingGlass.setBorderPaintTarget(this._target); | ||||
|         if (this._borderPaintTarget != this._target) { | ||||
|             if (this._borderPaintTarget != null) | ||||
|                 this._borderPaintTarget.disconnect(this._borderPaintId); | ||||
|             this._borderPaintTarget = this._target; | ||||
|             this._borderPaintId = addBorderPaintHook(this._target); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -660,7 +662,7 @@ const Memory = new Lang.Class({ | ||||
|  | ||||
|         this._gcbutton = new St.Button({ label: 'Full GC', | ||||
|                                          style_class: 'lg-obj-inspector-button' }); | ||||
|         this._gcbutton.connect('clicked', Lang.bind(this, function () { System.gc(); this._renderText(); })); | ||||
|         this._gcbutton.connect('clicked', Lang.bind(this, function () { global.gc(); this._renderText(); })); | ||||
|         this.actor.add(this._gcbutton, { x_align: St.Align.START, | ||||
|                                          x_fill: false }); | ||||
|  | ||||
| @@ -721,13 +723,13 @@ const Extensions = new Lang.Class({ | ||||
|         let extension = actor._extension; | ||||
|         let uri = extension.dir.get_uri(); | ||||
|         Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context()); | ||||
|         this._lookingGlass.close(); | ||||
|         Main.lookingGlass.close(); | ||||
|     }, | ||||
|  | ||||
|     _onWebPage: function (actor) { | ||||
|         let extension = actor._extension; | ||||
|         Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context()); | ||||
|         this._lookingGlass.close(); | ||||
|         Main.lookingGlass.close(); | ||||
|     }, | ||||
|  | ||||
|     _onViewErrors: function (actor) { | ||||
| @@ -791,33 +793,24 @@ const Extensions = new Lang.Class({ | ||||
|                                    text: this._stateToString(extension.state) }); | ||||
|         metaBox.add(state); | ||||
|  | ||||
|         let viewsource = new St.Button({ reactive: true, | ||||
|                                          track_hover: true, | ||||
|                                          style_class: 'shell-link', | ||||
|                                          label: _("View Source") }); | ||||
|         viewsource._extension = extension; | ||||
|         viewsource.connect('clicked', Lang.bind(this, this._onViewSource)); | ||||
|         metaBox.add(viewsource); | ||||
|         let viewsource = new Link.Link({ label: _("View Source") }); | ||||
|         viewsource.actor._extension = extension; | ||||
|         viewsource.actor.connect('clicked', Lang.bind(this, this._onViewSource)); | ||||
|         metaBox.add(viewsource.actor); | ||||
|  | ||||
|         if (extension.metadata.url) { | ||||
|             let webpage = new St.Button({ reactive: true, | ||||
|                                           track_hover: true, | ||||
|                                           style_class: 'shell-link', | ||||
|                                           label: _("Web Page") }); | ||||
|             webpage._extension = extension; | ||||
|             webpage.connect('clicked', Lang.bind(this, this._onWebPage)); | ||||
|             metaBox.add(webpage); | ||||
|             let webpage = new Link.Link({ label: _("Web Page") }); | ||||
|             webpage.actor._extension = extension; | ||||
|             webpage.actor.connect('clicked', Lang.bind(this, this._onWebPage)); | ||||
|             metaBox.add(webpage.actor); | ||||
|         } | ||||
|  | ||||
|         let viewerrors = new St.Button({ reactive: true, | ||||
|                                          track_hover: true, | ||||
|                                          style_class: 'shell-link', | ||||
|                                          label: _("Show Errors") }); | ||||
|         viewerrors._extension = extension; | ||||
|         viewerrors._parentBox = box; | ||||
|         viewerrors._isShowing = false; | ||||
|         viewerrors.connect('clicked', Lang.bind(this, this._onViewErrors)); | ||||
|         metaBox.add(viewerrors); | ||||
|         let viewerrors = new Link.Link({ label: _("Show Errors") }); | ||||
|         viewerrors.actor._extension = extension; | ||||
|         viewerrors.actor._parentBox = box; | ||||
|         viewerrors.actor._isShowing = false; | ||||
|         viewerrors.actor.connect('clicked', Lang.bind(this, this._onViewErrors)); | ||||
|         metaBox.add(viewerrors.actor); | ||||
|  | ||||
|         return box; | ||||
|     } | ||||
| @@ -828,7 +821,8 @@ const LookingGlass = new Lang.Class({ | ||||
|  | ||||
|     _init : function() { | ||||
|         this._borderPaintTarget = null; | ||||
|         this._redBorderEffect = new RedBorderEffect(); | ||||
|         this._borderPaintId = 0; | ||||
|         this._borderDestroyId = 0; | ||||
|  | ||||
|         this._open = false; | ||||
|  | ||||
| @@ -858,18 +852,19 @@ const LookingGlass = new Lang.Class({ | ||||
|         Main.layoutManager.keyboardBox.connect('allocation-changed', | ||||
|                                                Lang.bind(this, this._queueResize)); | ||||
|  | ||||
|         this._objInspector = new ObjInspector(this); | ||||
|         this._objInspector = new ObjInspector(); | ||||
|         Main.uiGroup.add_actor(this._objInspector.actor); | ||||
|         this._objInspector.actor.hide(); | ||||
|  | ||||
|         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; | ||||
|         inspectIcon.connect('button-press-event', Lang.bind(this, function () { | ||||
|             let inspector = new Inspector(this); | ||||
|             let inspector = new Inspector(); | ||||
|             inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) { | ||||
|                 this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>', | ||||
|                                  target); | ||||
| @@ -899,14 +894,18 @@ const LookingGlass = new Lang.Class({ | ||||
|         this._entryArea = new St.BoxLayout({ name: 'EntryArea' }); | ||||
|         this._evalBox.add_actor(this._entryArea); | ||||
|  | ||||
|         let label = new St.Label({ text: CHEVRON }); | ||||
|         let label = new St.Label({ text: 'js>>> ' }); | ||||
|         this._entryArea.add(label); | ||||
|  | ||||
|         this._entry = new St.Entry({ can_focus: true }); | ||||
|         ShellEntry.addContextMenu(this._entry); | ||||
|         this._entryArea.add(this._entry, { expand: true }); | ||||
|  | ||||
|         this._windowList = new WindowList(this); | ||||
|         this._windowList = new WindowList(); | ||||
|         this._windowList.connect('selected', Lang.bind(this, function(list, window) { | ||||
|             notebook.selectIndex(0); | ||||
|             this._pushResult('<window selection>', window); | ||||
|         })); | ||||
|         notebook.appendPage('Windows', this._windowList.actor); | ||||
|  | ||||
|         this._memory = new Memory(); | ||||
| @@ -960,22 +959,23 @@ const LookingGlass = new Lang.Class({ | ||||
|             + 'font-family: "' + fontDesc.get_family() + '";'; | ||||
|     }, | ||||
|  | ||||
|     setBorderPaintTarget: function(obj) { | ||||
|         if (this._borderPaintTarget != null) | ||||
|             this._borderPaintTarget.remove_effect(this._redBorderEffect); | ||||
|         this._borderPaintTarget = obj; | ||||
|         if (this._borderPaintTarget != null) | ||||
|             this._borderPaintTarget.add_effect(this._redBorderEffect); | ||||
|     }, | ||||
|  | ||||
|     _pushResult: function(command, obj) { | ||||
|         let index = this._results.length + this._offset; | ||||
|         let result = new Result(this, CHEVRON + command, obj, index); | ||||
|         let result = new Result('>>> ' + command, obj, index); | ||||
|         this._results.push(result); | ||||
|         this._resultsArea.add(result.actor); | ||||
|         if (obj instanceof Clutter.Actor) | ||||
|             this.setBorderPaintTarget(obj); | ||||
|  | ||||
|         if (this._borderPaintTarget != null) { | ||||
|             this._borderPaintTarget.disconnect(this._borderPaintId); | ||||
|             this._borderPaintTarget = null; | ||||
|         } | ||||
|         if (obj instanceof Clutter.Actor) { | ||||
|             this._borderPaintTarget = obj; | ||||
|             this._borderPaintId = addBorderPaintHook(obj); | ||||
|             this._borderDestroyId = obj.connect('destroy', Lang.bind(this, function () { | ||||
|                 this._borderDestroyId = 0; | ||||
|                 this._borderPaintTarget = null; | ||||
|             })); | ||||
|         } | ||||
|         let children = this._resultsArea.get_children(); | ||||
|         if (children.length > this._maxItems) { | ||||
|             this._results.shift(); | ||||
| @@ -1089,8 +1089,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) { | ||||
| @@ -1156,7 +1156,11 @@ const LookingGlass = new Lang.Class({ | ||||
|         this._open = false; | ||||
|         Tweener.removeTweens(this.actor); | ||||
|  | ||||
|         this.setBorderPaintTarget(null); | ||||
|         if (this._borderPaintTarget != null) { | ||||
|             this._borderPaintTarget.disconnect(this._borderPaintId); | ||||
|             this._borderPaintTarget.disconnect(this._borderDestroyId); | ||||
|             this._borderPaintTarget = null; | ||||
|         } | ||||
|  | ||||
|         Main.popModal(this._entry); | ||||
|  | ||||
|   | ||||
| @@ -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]; | ||||
| @@ -26,7 +26,6 @@ const MAGNIFIER_SCHEMA          = 'org.gnome.desktop.a11y.magnifier'; | ||||
| const SCREEN_POSITION_KEY       = 'screen-position'; | ||||
| const MAG_FACTOR_KEY            = 'mag-factor'; | ||||
| const INVERT_LIGHTNESS_KEY      = 'invert-lightness'; | ||||
| const COLOR_SATURATION_KEY      = 'color-saturation'; | ||||
| const BRIGHT_RED_KEY            = 'brightness-red'; | ||||
| const BRIGHT_GREEN_KEY          = 'brightness-green'; | ||||
| const BRIGHT_BLUE_KEY           = 'brightness-blue'; | ||||
| @@ -56,7 +55,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 +135,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 +147,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 +302,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); | ||||
|         } | ||||
|     }, | ||||
| @@ -453,10 +456,6 @@ const Magnifier = new Lang.Class({ | ||||
|             if (aPref) | ||||
|                 zoomRegion.setInvertLightness(aPref); | ||||
|  | ||||
|             aPref = this._settings.get_double(COLOR_SATURATION_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setColorSaturation(aPref); | ||||
|  | ||||
|             let bc = {}; | ||||
|             bc.r = this._settings.get_double(BRIGHT_RED_KEY); | ||||
|             bc.g = this._settings.get_double(BRIGHT_GREEN_KEY); | ||||
| @@ -491,8 +490,6 @@ const Magnifier = new Lang.Class({ | ||||
|  | ||||
|         this._settings.connect('changed::' + INVERT_LIGHTNESS_KEY, | ||||
|                                Lang.bind(this, this._updateInvertLightness)); | ||||
|         this._settings.connect('changed::' + COLOR_SATURATION_KEY, | ||||
|                                Lang.bind(this, this._updateColorSaturation)); | ||||
|  | ||||
|         this._settings.connect('changed::' + BRIGHT_RED_KEY, | ||||
|                                Lang.bind(this, this._updateBrightness)); | ||||
| @@ -594,15 +591,6 @@ const Magnifier = new Lang.Class({ | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _updateColorSaturation: function() { | ||||
|         // Applies only to the first zoom region. | ||||
|         if (this._zoomRegions.length) { | ||||
|             this._zoomRegions[0].setColorSaturation( | ||||
|                 this._settings.get_double(COLOR_SATURATION_KEY) | ||||
|             ); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _updateBrightness: function() { | ||||
|         // Applies only to the first zoom region. | ||||
|         if (this._zoomRegions.length) { | ||||
| @@ -638,7 +626,6 @@ const ZoomRegion = new Lang.Class({ | ||||
|         this._lensMode = false; | ||||
|         this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN; | ||||
|         this._invertLightness = false; | ||||
|         this._colorSaturation = 1.0; | ||||
|         this._brightness = { r: NO_CHANGE, g: NO_CHANGE, b: NO_CHANGE }; | ||||
|         this._contrast = { r: NO_CHANGE, g: NO_CHANGE, b: NO_CHANGE }; | ||||
|  | ||||
| @@ -986,27 +973,6 @@ const ZoomRegion = new Lang.Class({ | ||||
|         return this._invertLightness; | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * setColorSaturation: | ||||
|      * Set the color saturation of the magnified view. | ||||
|      * @sauration  A value from 0.0 to 1.0 that defines the color | ||||
|      *             saturation, with 0.0 defining no color (grayscale), | ||||
|      *             and 1.0 defining full color. | ||||
|      */ | ||||
|     setColorSaturation: function(saturation) { | ||||
|         this._colorSaturation = saturation; | ||||
|         if (this._magShaderEffects) | ||||
|             this._magShaderEffects.setColorSaturation(this._colorSaturation); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * getColorSaturation: | ||||
|      * Retrieve the color saturation of the magnified view. | ||||
|      */ | ||||
|     getColorSaturation: function() { | ||||
|         return this._colorSaturation; | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * setBrightness: | ||||
|      * Alter the brightness of the magnified view. | ||||
| @@ -1077,21 +1043,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. | ||||
| @@ -1108,7 +1074,6 @@ const ZoomRegion = new Lang.Class({ | ||||
|  | ||||
|         // Contrast and brightness effects. | ||||
|         this._magShaderEffects = new MagShaderEffects(this._uiGroupClone); | ||||
|         this._magShaderEffects.setColorSaturation(this._colorSaturation); | ||||
|         this._magShaderEffects.setInvertLightness(this._invertLightness); | ||||
|         this._magShaderEffects.setBrightness(this._brightness); | ||||
|         this._magShaderEffects.setContrast(this._contrast); | ||||
| @@ -1325,7 +1290,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 +1318,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 +1342,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 +1421,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 +1433,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; | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
| @@ -1634,14 +1602,12 @@ const MagShaderEffects = new Lang.Class({ | ||||
|     _init: function(uiGroupClone) { | ||||
|         this._inverse = new Shell.InvertLightnessEffect(); | ||||
|         this._brightnessContrast = new Clutter.BrightnessContrastEffect(); | ||||
|         this._colorDesaturation = new Clutter.DesaturateEffect(); | ||||
|         this._inverse.set_enabled(false); | ||||
|         this._brightnessContrast.set_enabled(false); | ||||
|  | ||||
|         this._magView = uiGroupClone; | ||||
|         this._magView.add_effect(this._inverse); | ||||
|         this._magView.add_effect(this._brightnessContrast); | ||||
|         this._magView.add_effect(this._colorDesaturation); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
| @@ -1652,7 +1618,6 @@ const MagShaderEffects = new Lang.Class({ | ||||
|      */ | ||||
|     destroyEffects: function() { | ||||
|         this._magView.clear_effects(); | ||||
|         this._colorDesaturation = null; | ||||
|         this._brightnessContrast = null; | ||||
|         this._inverse = null; | ||||
|         this._magView = null; | ||||
| @@ -1676,14 +1641,6 @@ const MagShaderEffects = new Lang.Class({ | ||||
|         return this._inverse.get_enabled(); | ||||
|     }, | ||||
|  | ||||
|     setColorSaturation: function(factor) { | ||||
|         this._colorDesaturation.set_factor(1.0 - factor); | ||||
|     }, | ||||
|  | ||||
|     getColorSaturation: function() { | ||||
|         return 1.0 - this._colorDesaturation.get_factor(); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * setBrightness: | ||||
|      * Set the brightness of the magnified view. | ||||
|   | ||||
							
								
								
									
										198
									
								
								js/ui/main.js
									
									
									
									
									
								
							
							
						
						| @@ -10,37 +10,43 @@ 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; | ||||
| 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; | ||||
| const Scripting = imports.ui.scripting; | ||||
| const SessionMode = imports.ui.sessionMode; | ||||
| const ShellDBus = imports.ui.shellDBus; | ||||
| const ShellMountOperation = imports.ui.shellMountOperation; | ||||
| const UnlockDialog = imports.ui.unlockDialog; | ||||
| const TelepathyClient = imports.ui.telepathyClient; | ||||
| const WindowManager = imports.ui.windowManager; | ||||
| const Magnifier = imports.ui.magnifier; | ||||
| const XdndHandler = imports.ui.xdndHandler; | ||||
| const StatusIconDispatcher = imports.ui.statusIconDispatcher; | ||||
| 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,29 +55,100 @@ let messageTray = null; | ||||
| let screenShield = null; | ||||
| let notificationDaemon = null; | ||||
| let windowAttentionHandler = null; | ||||
| let telepathyClient = null; | ||||
| let ctrlAltTabManager = null; | ||||
| let sessionMode = null; | ||||
| let recorder = null; | ||||
| let shellDBusService = null; | ||||
| let shellMountOpDBusService = null; | ||||
| let screenSaverDBus = null; | ||||
| let modalCount = 0; | ||||
| let modalActorFocusStack = []; | ||||
| let uiGroup = null; | ||||
| let magnifier = null; | ||||
| let xdndHandler = null; | ||||
| let statusIconDispatcher = null; | ||||
| let keyboard = null; | ||||
| let layoutManager = null; | ||||
| let networkAgent = null; | ||||
| let _startDate; | ||||
| let _defaultCssStylesheet = null; | ||||
| let _cssStylesheet = null; | ||||
| let _gdmCssStylesheet = null; | ||||
| let _overridesSettings = null; | ||||
|  | ||||
| let background = null; | ||||
|  | ||||
| function _sessionUpdated() { | ||||
|     Meta.keybindings_set_custom_handler('panel-run-dialog', sessionMode.hasRunDialog ? openRunDialog : 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(); | ||||
| } | ||||
|  | ||||
| function _createGDMSession() { | ||||
|     // 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(); | ||||
|     loginDialog.connect('loaded', function() { | ||||
|                             loginDialog.open(); | ||||
|                         }); | ||||
| } | ||||
|  | ||||
| function _initRecorder() { | ||||
|     let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' }); | ||||
|     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 { | ||||
|             // 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 _initUserSession() { | ||||
|     _initRecorder(); | ||||
|  | ||||
|     global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1); | ||||
|  | ||||
|     ExtensionSystem.init(); | ||||
|     ExtensionSystem.loadExtensions(); | ||||
|  | ||||
|     Meta.keybindings_set_custom_handler('panel-run-dialog', function() { | ||||
|        getRunDialog().open(); | ||||
|     }); | ||||
|  | ||||
|     Meta.keybindings_set_custom_handler('panel-main-menu', function () { | ||||
|         overview.toggle(); | ||||
|     }); | ||||
|  | ||||
|     global.display.connect('overlay-key', Lang.bind(overview, overview.toggle)); | ||||
|  | ||||
| } | ||||
|  | ||||
| function start() { | ||||
| @@ -84,9 +161,7 @@ function start() { | ||||
|  | ||||
|     Gio.DesktopAppInfo.set_desktop_env('GNOME'); | ||||
|  | ||||
|     sessionMode = new SessionMode.SessionMode(); | ||||
|     shellDBusService = new ShellDBus.GnomeShell(); | ||||
|     shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler(); | ||||
|  | ||||
|     // Ensure ShellWindowTracker and ShellAppUsage are initialized; this will | ||||
|     // also initialize ShellAppSystem first.  ShellAppSystem | ||||
| @@ -108,6 +183,7 @@ function start() { | ||||
|     global.stage.no_clear_hint = true; | ||||
|  | ||||
|     _defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css'; | ||||
|     _gdmCssStylesheet = global.datadir + '/theme/gdm.css'; | ||||
|     loadTheme(); | ||||
|  | ||||
|     // Set up stage hierarchy to group all UI actors under one container. | ||||
| @@ -135,36 +211,43 @@ function start() { | ||||
|     layoutManager = new Layout.LayoutManager(); | ||||
|     xdndHandler = new XdndHandler.XdndHandler(); | ||||
|     ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager(); | ||||
|     overview = new Overview.Overview(); | ||||
|     // This overview object is just a stub for non-user sessions | ||||
|     overview = new Overview.Overview({ isDummy: global.session_type != Shell.SessionType.USER }); | ||||
|     magnifier = new Magnifier.Magnifier(); | ||||
|     if (UnlockDialog.isSupported()) | ||||
|         screenShield = new ScreenShield.ScreenShield(); | ||||
|     else | ||||
|         screenShield = new ScreenShield.ScreenShieldFallback(); | ||||
|     statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher(); | ||||
|     panel = new Panel.Panel(); | ||||
|     wm = new WindowManager.WindowManager(); | ||||
|     messageTray = new MessageTray.MessageTray(); | ||||
|     screenShield = new ScreenShield.ScreenShield(); | ||||
|     keyboard = new Keyboard.Keyboard(); | ||||
|     notificationDaemon = new NotificationDaemon.NotificationDaemon(); | ||||
|     windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler(); | ||||
|     componentManager = new Components.ComponentManager(); | ||||
|  | ||||
|     if (global.session_type == Shell.SessionType.USER) | ||||
|         _createUserSession(); | ||||
|     else if (global.session_type == Shell.SessionType.GDM) | ||||
|         _createGDMSession(); | ||||
|  | ||||
|     panel.startStatusArea(); | ||||
|  | ||||
|     layoutManager.init(); | ||||
|     keyboard.init(); | ||||
|     overview.init(); | ||||
|  | ||||
|     global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, | ||||
|                                             false, -1, 1); | ||||
|     Meta.keybindings_set_custom_handler('panel-main-menu', Lang.bind(overview, overview.toggle)); | ||||
|     global.display.connect('overlay-key', Lang.bind(overview, overview.toggle)); | ||||
|  | ||||
|     sessionMode.connect('update', _sessionUpdated); | ||||
|     _sessionUpdated(); | ||||
|     if (global.session_type == Shell.SessionType.USER) | ||||
|         _initUserSession(); | ||||
|     statusIconDispatcher.start(messageTray.actor); | ||||
|  | ||||
|     // 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); | ||||
| @@ -188,9 +271,6 @@ function start() { | ||||
|     global.screen.connect('restacked', _windowsRestacked); | ||||
|  | ||||
|     _nWorkspacesChanged(); | ||||
|  | ||||
|     ExtensionDownloader.init(); | ||||
|     ExtensionSystem.init(); | ||||
| } | ||||
|  | ||||
| let _workspaces = []; | ||||
| @@ -414,6 +494,9 @@ function loadTheme() { | ||||
|  | ||||
|     let theme = new St.Theme ({ application_stylesheet: cssStylesheet }); | ||||
|  | ||||
|     // FIXME: merge back into main stylesheet | ||||
|     theme.load_stylesheet(_gdmCssStylesheet); | ||||
|  | ||||
|     if (previousTheme) { | ||||
|         let customStylesheets = previousTheme.get_custom_stylesheets(); | ||||
|  | ||||
| @@ -476,11 +559,6 @@ function _globalKeyPressHandler(actor, event) { | ||||
|     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(); | ||||
| @@ -489,49 +567,48 @@ function _globalKeyPressHandler(actor, event) { | ||||
|     // This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType | ||||
|     let action = global.display.get_keybinding_action(keyCode, modifierState); | ||||
|  | ||||
|     // Other bindings are only available to the user session when the overview is up and | ||||
|     // no modal dialog is present. | ||||
|     if (global.session_type == Shell.SessionType.USER && (!overview.visible || modalCount > 1)) | ||||
|         return false; | ||||
|  | ||||
|     // 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; | ||||
|     } | ||||
|  | ||||
|     // None of the other bindings are relevant outside of the user's session | ||||
|     if (global.session_type != Shell.SessionType.USER) | ||||
|         return false; | ||||
|  | ||||
|     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); | ||||
|             wm.actionMoveWorkspaceUp(); | ||||
|             return true; | ||||
|         case Meta.KeyBindingAction.WORKSPACE_DOWN: | ||||
|             if (!sessionMode.hasWorkspaces) | ||||
|                 return false; | ||||
|  | ||||
|             wm.actionMoveWorkspace(Meta.MotionDirection.DOWN); | ||||
|             wm.actionMoveWorkspaceDown(); | ||||
|             return true; | ||||
|         case Meta.KeyBindingAction.PANEL_RUN_DIALOG: | ||||
|         case Meta.KeyBindingAction.COMMAND_2: | ||||
|             if (!sessionMode.hasRunDialog) | ||||
|                 return false; | ||||
|  | ||||
|             openRunDialog(); | ||||
|             getRunDialog().open(); | ||||
|             return true; | ||||
|         case Meta.KeyBindingAction.PANEL_MAIN_MENU: | ||||
|         case Meta.KeyBindingAction.OVERLAY_KEY: | ||||
|             overview.hide(); | ||||
|             return true; | ||||
|     } | ||||
| @@ -583,7 +660,6 @@ function pushModal(actor, timestamp, options) { | ||||
|             log('pushModal: invocation of begin_modal failed'); | ||||
|             return false; | ||||
|         } | ||||
|         Meta.disable_unredirect_for_screen(global.screen); | ||||
|     } | ||||
|  | ||||
|     global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN); | ||||
| @@ -664,7 +740,6 @@ function popModal(actor, timestamp) { | ||||
|  | ||||
|     global.end_modal(timestamp); | ||||
|     global.set_stage_input_mode(Shell.StageInputMode.NORMAL); | ||||
|     Meta.enable_unredirect_for_screen(global.screen); | ||||
| } | ||||
|  | ||||
| function createLookingGlass() { | ||||
| @@ -674,11 +749,11 @@ function createLookingGlass() { | ||||
|     return lookingGlass; | ||||
| } | ||||
|  | ||||
| function openRunDialog() { | ||||
| function getRunDialog() { | ||||
|     if (runDialog == null) { | ||||
|         runDialog = new RunDialog.RunDialog(); | ||||
|     } | ||||
|     runDialog.open(); | ||||
|     return runDialog; | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -807,8 +882,7 @@ function initializeDeferredWork(actor, callback, props) { | ||||
| function queueDeferredWork(workId) { | ||||
|     let data = _deferredWorkData[workId]; | ||||
|     if (!data) { | ||||
|         let message = 'Invalid work id %d'.format(workId); | ||||
|         logError(new Error(message), message); | ||||
|         global.logError('invalid work id ', workId); | ||||
|         return; | ||||
|     } | ||||
|     if (_deferredWorkQueue.indexOf(workId) < 0) | ||||
|   | ||||
							
								
								
									
										1452
									
								
								js/ui/messageTray.js
									
									
									
									
									
								
							
							
						
						| @@ -14,7 +14,6 @@ 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; | ||||
| @@ -36,20 +35,17 @@ const ModalDialog = new Lang.Class({ | ||||
|  | ||||
|     _init: function(params) { | ||||
|         params = Params.parse(params, { shellReactive: false, | ||||
|                                         styleClass: null, | ||||
|                                         parentActor: Main.uiGroup, | ||||
|                                         shouldFadeIn: true }); | ||||
|                                         styleClass: null }); | ||||
|  | ||||
|         this.state = State.CLOSED; | ||||
|         this._hasModal = false; | ||||
|         this._shellReactive = params.shellReactive; | ||||
|         this._shouldFadeIn = params.shouldFadeIn; | ||||
|  | ||||
|         this._group = new St.Widget({ visible: false, | ||||
|                                       x: 0, | ||||
|                                       y: 0, | ||||
|                                       accessible_role: Atk.Role.DIALOG }); | ||||
|         params.parentActor.add_actor(this._group); | ||||
|         Main.uiGroup.add_actor(this._group); | ||||
|  | ||||
|         let constraint = new Clutter.BindConstraint({ source: global.stage, | ||||
|                                                       coordinate: Clutter.BindCoordinate.ALL }); | ||||
| @@ -58,17 +54,15 @@ const ModalDialog = new Lang.Class({ | ||||
|         this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy)); | ||||
|  | ||||
|         this._actionKeys = {}; | ||||
|         this._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent)); | ||||
|         this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent)); | ||||
|  | ||||
|         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) { | ||||
| @@ -81,29 +75,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; | ||||
|     }, | ||||
| @@ -112,10 +106,6 @@ const ModalDialog = new Lang.Class({ | ||||
|         this._group.destroy(); | ||||
|     }, | ||||
|  | ||||
|     setActionKey: function(key, action) { | ||||
|         this._actionKeys[key] = action; | ||||
|     }, | ||||
|  | ||||
|     setButtons: function(buttons) { | ||||
|         let hadChildren = this._buttonLayout.get_children() > 0; | ||||
|  | ||||
| @@ -129,20 +119,11 @@ const ModalDialog = new Lang.Class({ | ||||
|             let label = buttonInfo['label']; | ||||
|             let action = buttonInfo['action']; | ||||
|             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; | ||||
|                 key = Clutter.KEY_Return; | ||||
|             } | ||||
|  | ||||
|             buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button', | ||||
|                                                 reactive:    true, | ||||
|                                                 can_focus:   true, | ||||
|                                                 label:       label }); | ||||
|             if (isDefault) | ||||
|                 buttonInfo.button.add_style_pseudo_class('default'); | ||||
|  | ||||
|             let x_alignment; | ||||
|             if (buttons.length == 1) | ||||
| @@ -186,38 +167,34 @@ const ModalDialog = new Lang.Class({ | ||||
|  | ||||
|     }, | ||||
|  | ||||
|     _onKeyReleaseEvent: function(object, event) { | ||||
|         let symbol = event.get_key_symbol(); | ||||
|     _onKeyPressEvent: function(object, keyPressEvent) { | ||||
|         let symbol = keyPressEvent.get_key_symbol(); | ||||
|         let action = this._actionKeys[symbol]; | ||||
|  | ||||
|         if (action) { | ||||
|         if (action) | ||||
|             action(); | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     _onGroupDestroy: function() { | ||||
|         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.focusMonitor; | ||||
|  | ||||
|         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() { | ||||
| @@ -234,19 +211,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; | ||||
|     }, | ||||
|  | ||||
| @@ -327,7 +304,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++; | ||||
| 
 | ||||
| @@ -131,7 +147,7 @@ const NetworkSecretDialog = new Lang.Class({ | ||||
| 
 | ||||
|         this._okButton = { label:  _("Connect"), | ||||
|                            action: Lang.bind(this, this._onOk), | ||||
|                            default: true | ||||
|                            key:    Clutter.KEY_Return, | ||||
|                          }; | ||||
| 
 | ||||
|         this.setButtons([{ label: _("Cancel"), | ||||
| @@ -150,6 +166,10 @@ const NetworkSecretDialog = new Lang.Class({ | ||||
| 
 | ||||
|         this._okButton.button.reactive = valid; | ||||
|         this._okButton.button.can_focus = valid; | ||||
|         if (valid) | ||||
|             this._okButton.button.remove_style_pseudo_class('disabled'); | ||||
|         else | ||||
|             this._okButton.button.add_style_pseudo_class('disabled'); | ||||
|     }, | ||||
| 
 | ||||
|     _onOk: function() { | ||||
| @@ -584,7 +604,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 = { }; | ||||
| @@ -594,14 +614,6 @@ const NetworkAgent = new Lang.Class({ | ||||
|         this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest)); | ||||
|     }, | ||||
| 
 | ||||
|     enable: function() { | ||||
|         this._native.register(); | ||||
|     }, | ||||
| 
 | ||||
|     disable: function() { | ||||
|         this._native.unregister(); | ||||
|     }, | ||||
| 
 | ||||
|     _newRequest:  function(agent, requestId, connection, settingName, hints, flags) { | ||||
|         if (settingName == 'vpn') { | ||||
|             this._vpnRequest(requestId, connection, hints, flags); | ||||
| @@ -671,10 +683,7 @@ const NetworkAgent = new Lang.Class({ | ||||
|                     try { | ||||
|                         externalUIMode = keyfile.get_boolean('GNOME', 'supports-external-ui-mode'); | ||||
|                     } catch(e) { } // ignore errors if key does not exist
 | ||||
|                     let path = binary; | ||||
|                     if (!GLib.path_is_absolute(path)) { | ||||
|                         path = GLib.build_filenamev([Config.LIBEXECDIR, path]); | ||||
|                     } | ||||
|                     let path = GLib.build_filenamev([Config.LIBEXECDIR, binary]); | ||||
| 
 | ||||
|                     if (GLib.file_test(path, GLib.FileTest.IS_EXECUTABLE)) | ||||
|                         this._vpnBinaries[service] = { fileName: path, externalUIMode: externalUIMode }; | ||||
| @@ -691,4 +700,3 @@ const NetworkAgent = new Lang.Class({ | ||||
|         } | ||||
|     } | ||||
| }); | ||||
| const Component = NetworkAgent; | ||||
| @@ -1,7 +1,6 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GdkPixbuf = imports.gi.GdkPixbuf; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| @@ -88,21 +87,6 @@ const rewriteRules = { | ||||
|     ] | ||||
| }; | ||||
|  | ||||
| const STANDARD_TRAY_ICON_IMPLEMENTATIONS = { | ||||
|     'bluetooth-applet': 'bluetooth', | ||||
|     'gnome-volume-control-applet': 'volume', // renamed to gnome-sound-applet | ||||
|                                              // when moved to control center | ||||
|     'gnome-sound-applet': 'volume', | ||||
|     'nm-applet': 'network', | ||||
|     'gnome-power-manager': 'battery', | ||||
|     'keyboard': 'keyboard', | ||||
|     'a11y-keyboard': 'a11y', | ||||
|     'kbd-scrolllock': 'keyboard', | ||||
|     'kbd-numlock': 'keyboard', | ||||
|     'kbd-capslock': 'keyboard', | ||||
|     'ibus-ui-gtk': 'keyboard' | ||||
| }; | ||||
|  | ||||
| const NotificationDaemon = new Lang.Class({ | ||||
|     Name: 'NotificationDaemon', | ||||
|  | ||||
| @@ -115,19 +99,18 @@ const NotificationDaemon = new Lang.Class({ | ||||
|         this._notifications = {}; | ||||
|         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)); | ||||
|         Main.statusIconDispatcher.connect('message-icon-added', Lang.bind(this, this._onTrayIconAdded)); | ||||
|         Main.statusIconDispatcher.connect('message-icon-removed', Lang.bind(this, this._onTrayIconRemoved)); | ||||
|  | ||||
|         Shell.WindowTracker.get_default().connect('notify::focus-app', | ||||
|             Lang.bind(this, this._onFocusAppChanged)); | ||||
|         Main.overview.connect('hidden', | ||||
|             Lang.bind(this, this._onFocusAppChanged)); | ||||
|  | ||||
|         this._trayManager.manage_stage(global.stage, Main.messageTray.actor); | ||||
|     }, | ||||
|  | ||||
|     _iconForNotificationData: function(icon, hints) { | ||||
|     _iconForNotificationData: function(icon, hints, size) { | ||||
|         let textureCache = St.TextureCache.get_default(); | ||||
|  | ||||
|         // If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon | ||||
|         // and don't show a large image. There are currently many applications that use | ||||
|         // notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets | ||||
| @@ -138,18 +121,20 @@ const NotificationDaemon = new Lang.Class({ | ||||
|         // a large image. | ||||
|         if (icon) { | ||||
|             if (icon.substr(0, 7) == 'file://') | ||||
|                 return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) }); | ||||
|                 return textureCache.load_uri_async(icon, size, size); | ||||
|             else if (icon[0] == '/') { | ||||
|                 return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) }); | ||||
|                 let uri = GLib.filename_to_uri(icon, null); | ||||
|                 return textureCache.load_uri_async(uri, size, size); | ||||
|             } else | ||||
|                 return new Gio.ThemedIcon({ name: icon }); | ||||
|                 return new St.Icon({ icon_name: icon, | ||||
|                                      icon_type: St.IconType.FULLCOLOR, | ||||
|                                      icon_size: size }); | ||||
|         } else if (hints['image-data']) { | ||||
|             let [width, height, rowStride, hasAlpha, | ||||
|                  bitsPerSample, nChannels, data] = hints['image-data']; | ||||
|             return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha, | ||||
|                                                       bitsPerSample, width, height, rowStride); | ||||
|             return textureCache.load_from_raw(data, hasAlpha, width, height, rowStride, size); | ||||
|         } else if (hints['image-path']) { | ||||
|             return new Gio.FileIcon({ file: Gio.File.new_for_path(hints['image-path']) }); | ||||
|             return textureCache.load_uri_async(GLib.filename_to_uri(hints['image-path'], null), size, size); | ||||
|         } else { | ||||
|             let stockIcon; | ||||
|             switch (hints.urgency) { | ||||
| @@ -161,7 +146,9 @@ const NotificationDaemon = new Lang.Class({ | ||||
|                     stockIcon = 'gtk-dialog-error'; | ||||
|                     break; | ||||
|             } | ||||
|             return new Gio.ThemedIcon({ name: stockIcon }); | ||||
|             return new St.Icon({ icon_name: stockIcon, | ||||
|                                  icon_type: St.IconType.FULLCOLOR, | ||||
|                                  icon_size: size }); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -234,19 +221,12 @@ const NotificationDaemon = new Lang.Class({ | ||||
|         let [appName, replacesId, icon, summary, body, actions, hints, timeout] = params; | ||||
|         let id; | ||||
|  | ||||
|         for (let hint in hints) { | ||||
|             // unpack the variants | ||||
|             hints[hint] = hints[hint].deep_unpack(); | ||||
|         } | ||||
|  | ||||
|         hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true); | ||||
|  | ||||
|         // Filter out chat, presence, calls and invitation notifications from | ||||
|         // Empathy, since we handle that information from telepathyClient.js | ||||
|         if (appName == 'Empathy' && (hints['category'] == 'im.received' || | ||||
|               hints['category'] == 'x-empathy.im.room-invitation' || | ||||
|               hints['category'] == 'x-empathy.call.incoming' || | ||||
|               hints['category'] == 'x-empathy.transfer.incoming' || | ||||
|               hints['category'] == 'x-empathy.call.incoming"' || | ||||
|               hints['category'] == 'x-empathy.im.subscription-request' || | ||||
|               hints['category'] == 'presence.online' || | ||||
|               hints['category'] == 'presence.offline')) { | ||||
| @@ -256,7 +236,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])); | ||||
|         } | ||||
| @@ -270,6 +249,13 @@ const NotificationDaemon = new Lang.Class({ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         for (let hint in hints) { | ||||
|             // unpack the variants | ||||
|             hints[hint] = hints[hint].deep_unpack(); | ||||
|         } | ||||
|  | ||||
|         hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true); | ||||
|  | ||||
|         // Be compatible with the various hints for image data and image path | ||||
|         // 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2 | ||||
|  | ||||
| @@ -355,9 +341,7 @@ const NotificationDaemon = new Lang.Class({ | ||||
|             [ndata.id, ndata.icon, ndata.summary, ndata.body, | ||||
|              ndata.actions, ndata.hints, ndata.notification]; | ||||
|  | ||||
|         let gicon = this._iconForNotificationData(icon, hints); | ||||
|         let iconActor = new St.Icon({ gicon: gicon, | ||||
|                                       icon_size: MessageTray.NOTIFICATION_ICON_SIZE }); | ||||
|         let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE); | ||||
|  | ||||
|         if (notification == null) { | ||||
|             notification = new MessageTray.Notification(source, summary, body, | ||||
| @@ -437,8 +421,8 @@ const NotificationDaemon = new Lang.Class({ | ||||
|         // of the 'transient' hint with hints['transient'] rather than hints.transient | ||||
|         notification.setTransient(hints['transient'] == true); | ||||
|  | ||||
|         let sourceGIcon = source.useNotificationIcon ? this._iconForNotificationData(icon, hints) : null; | ||||
|         source.processNotification(notification, sourceGIcon); | ||||
|         let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null; | ||||
|         source.processNotification(notification, sourceIconActor); | ||||
|     }, | ||||
|  | ||||
|     CloseNotification: function(id) { | ||||
| @@ -499,11 +483,7 @@ const NotificationDaemon = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onTrayIconAdded: function(o, icon) { | ||||
|         let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : ''; | ||||
|         if (STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass] !== undefined) | ||||
|             return; | ||||
|  | ||||
|         let source = this._getSource(icon.title || icon.wm_class || C_("program", "Unknown"), icon.pid, null, null, icon); | ||||
|         let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null, null, icon); | ||||
|     }, | ||||
|  | ||||
|     _onTrayIconRemoved: function(o, icon) { | ||||
| @@ -554,11 +534,11 @@ const Source = new Lang.Class({ | ||||
|             this.destroy(); | ||||
|     }, | ||||
|  | ||||
|     processNotification: function(notification, gicon) { | ||||
|         if (gicon) | ||||
|             this._gicon = gicon; | ||||
|         if (!this.trayIcon) | ||||
|             this.iconUpdated(); | ||||
|     processNotification: function(notification, icon) { | ||||
|         if (!this.app) | ||||
|             this._setApp(); | ||||
|         if (!this.app && icon) | ||||
|             this._setSummaryIcon(icon); | ||||
|  | ||||
|         let tracker = Shell.WindowTracker.get_default(); | ||||
|         if (notification.resident && this.app && tracker.focus_app == this.app) | ||||
| @@ -583,12 +563,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; | ||||
|     }, | ||||
|  | ||||
| @@ -620,20 +606,10 @@ const Source = new Lang.Class({ | ||||
|         // notification-based icons (ie, not a trayicon) or if it was unset before | ||||
|         if (!this.trayIcon) { | ||||
|             this.useNotificationIcon = false; | ||||
|             this.iconUpdated(); | ||||
|             this._setSummaryIcon(this.app.create_icon_texture (this.ICON_SIZE)); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     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(); | ||||
| @@ -664,18 +640,8 @@ const Source = new Lang.Class({ | ||||
|         this.parent(); | ||||
|     }, | ||||
|  | ||||
|     createIcon: function(size) { | ||||
|         if (this.trayIcon) { | ||||
|             return new Clutter.Clone({ width: size, | ||||
|                                        height: size, | ||||
|                                        source: this.trayIcon }); | ||||
|         } else if (this.app) { | ||||
|             return this.app.create_icon_texture(size); | ||||
|         } else if (this._gicon) { | ||||
|             return new St.Icon({ gicon: this._gicon, | ||||
|                                  icon_size: size }); | ||||
|         } else { | ||||
|             return null; | ||||
|         } | ||||
|     createNotificationIcon: function() { | ||||
|         // We set the summary icon ourselves. | ||||
|         return null; | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -10,14 +10,21 @@ const St = imports.gi.St; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Gdk = imports.gi.Gdk; | ||||
|  | ||||
| const AppDisplay = imports.ui.appDisplay; | ||||
| const ContactDisplay = imports.ui.contactDisplay; | ||||
| const Dash = imports.ui.dash; | ||||
| const DND = imports.ui.dnd; | ||||
| const Lightbox = imports.ui.lightbox; | ||||
| 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 | ||||
| @@ -70,12 +77,13 @@ 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); | ||||
|         } else { | ||||
|             notification = this._source.notifications[0]; | ||||
|             notification.update(text, null, { clear: true }); | ||||
|         } | ||||
|  | ||||
|         notification.setTransient(true); | ||||
|  | ||||
|         this._undoCallback = undoCallback; | ||||
|         if (undoCallback) { | ||||
|             notification.addButton('system-undo', | ||||
| @@ -91,21 +99,18 @@ const ShellInfo = new Lang.Class({ | ||||
| const Overview = new Lang.Class({ | ||||
|     Name: 'Overview', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._overviewCreated = false; | ||||
|     _init : function(params) { | ||||
|         params = Params.parse(params, { isDummy: false }); | ||||
|  | ||||
|         Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); | ||||
|         this._sessionUpdated(); | ||||
|     }, | ||||
|         this.isDummy = params.isDummy; | ||||
|  | ||||
|     _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 | ||||
| @@ -142,6 +147,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() | ||||
| @@ -178,11 +185,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 | ||||
| @@ -193,23 +195,30 @@ 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()); | ||||
|         this.addSearchProvider(new ContactDisplay.ContactSearchProvider()); | ||||
|  | ||||
|         // 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', | ||||
| @@ -219,7 +228,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(); | ||||
| @@ -470,9 +479,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(); | ||||
|         })); | ||||
| @@ -497,13 +504,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 dashY = searchY + searchHeight + this._spacing; | ||||
|         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; | ||||
|  | ||||
|         // 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); | ||||
| @@ -511,14 +518,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); | ||||
|     }, | ||||
| @@ -568,28 +569,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; | ||||
| @@ -610,7 +589,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, | ||||
| @@ -737,7 +730,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, | ||||
| @@ -779,7 +781,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(); | ||||
|   | ||||
							
								
								
									
										306
									
								
								js/ui/panel.js
									
									
									
									
									
								
							
							
						
						| @@ -14,7 +14,6 @@ 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; | ||||
| @@ -22,6 +21,7 @@ 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; | ||||
|  | ||||
| @@ -32,6 +32,33 @@ const BUTTON_DND_ACTIVATION_TIMEOUT = 250; | ||||
| const ANIMATED_ICON_UPDATE_TIMEOUT = 100; | ||||
| const SPINNER_ANIMATION_TIME = 0.2; | ||||
|  | ||||
| const STANDARD_STATUS_AREA_ORDER = ['a11y', 'keyboard', 'volume', 'bluetooth', 'network', 'battery', 'userMenu']; | ||||
| 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.XKBIndicator, | ||||
|     '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 GDM_STATUS_AREA_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'battery', 'powerMenu']; | ||||
| const GDM_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.XKBIndicator, | ||||
|     'powerMenu': imports.gdm.powerMenu.PowerMenuButton | ||||
| }; | ||||
|  | ||||
| // To make sure the panel corners blend nicely with the panel, | ||||
| // we draw background and borders the same way, e.g. drawing | ||||
| // them as filled shapes from the outside inwards instead of | ||||
| @@ -222,14 +249,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; | ||||
| @@ -286,7 +313,7 @@ const AppMenuButton = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     show: function() { | ||||
|         if (this._visible || Main.screenShield.locked) | ||||
|         if (this._visible) | ||||
|             return; | ||||
|  | ||||
|         this._visible = true; | ||||
| @@ -347,7 +374,6 @@ const AppMenuButton = new Lang.Class({ | ||||
|             return; | ||||
|  | ||||
|         this._stop = true; | ||||
|         this.actor.reactive = true; | ||||
|         Tweener.addTween(this._spinner.actor, | ||||
|                          { opacity: 0, | ||||
|                            time: SPINNER_ANIMATION_TIME, | ||||
| @@ -362,7 +388,6 @@ const AppMenuButton = new Lang.Class({ | ||||
|  | ||||
|     startAnimation: function() { | ||||
|         this._stop = false; | ||||
|         this.actor.reactive = false; | ||||
|         this._spinner.actor.show(); | ||||
|     }, | ||||
|  | ||||
| @@ -601,8 +626,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); | ||||
| @@ -652,10 +677,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) { | ||||
| @@ -677,7 +702,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; | ||||
| @@ -754,11 +779,10 @@ const PanelCorner = new Lang.Class({ | ||||
|             return null; | ||||
|  | ||||
|         // Start at the back and work backward | ||||
|         let index; | ||||
|         for (index = children.length - 1; index >= 0; index--) { | ||||
|             if (children[index].visible) | ||||
|                 break; | ||||
|         } | ||||
|         let index = children.length - 1; | ||||
|         while (!children[index].visible && index >= 0) | ||||
|             index--; | ||||
|  | ||||
|         if (index < 0) | ||||
|             return null; | ||||
|  | ||||
| @@ -779,11 +803,10 @@ const PanelCorner = new Lang.Class({ | ||||
|             return null; | ||||
|  | ||||
|         // Start at the front and work forward | ||||
|         let index; | ||||
|         for (index = 0; index < children.length; index++) { | ||||
|             if (children[index].visible) | ||||
|                 break; | ||||
|         } | ||||
|         let index = 0; | ||||
|         while (!children[index].visible && index < children.length) | ||||
|             index++; | ||||
|  | ||||
|         if (index == children.length) | ||||
|             return null; | ||||
|  | ||||
| @@ -893,29 +916,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, | ||||
|     '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', | ||||
| @@ -925,7 +925,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'); | ||||
| @@ -934,7 +934,7 @@ const Panel = new Lang.Class({ | ||||
|             this.actor.remove_style_class_name('in-overview'); | ||||
|         })); | ||||
|  | ||||
|         this.menuManager = new PopupMenu.PopupMenuManager(this); | ||||
|         this._menus = new PopupMenu.PopupMenuManager(this); | ||||
|  | ||||
|         this._leftBox = new St.BoxLayout({ name: 'panelLeft' }); | ||||
|         this.actor.add_actor(this._leftBox); | ||||
| @@ -961,12 +961,43 @@ 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 (global.session_type == Shell.SessionType.USER) { | ||||
|             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); | ||||
|  | ||||
|             this._appMenu = new AppMenuButton(this._menus); | ||||
|             this._leftBox.add(this._appMenu.actor); | ||||
|         } | ||||
|  | ||||
|         /* center */ | ||||
|         if (global.session_type == Shell.SessionType.USER) | ||||
|             this._dateMenu = new DateMenu.DateMenuButton({ showEvents: true }); | ||||
|         else | ||||
|             this._dateMenu = new DateMenu.DateMenuButton({ showEvents: false }); | ||||
|         this._centerBox.add(this._dateMenu.actor, { y_fill: true }); | ||||
|         this._menus.addMenu(this._dateMenu.menu); | ||||
|  | ||||
|         /* right */ | ||||
|         if (global.session_type == Shell.SessionType.GDM) { | ||||
|             this._status_area_order = GDM_STATUS_AREA_ORDER; | ||||
|             this._status_area_shell_implementation = GDM_STATUS_AREA_SHELL_IMPLEMENTATION; | ||||
|         } else { | ||||
|             this._status_area_order = STANDARD_STATUS_AREA_ORDER; | ||||
|             this._status_area_shell_implementation = STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION; | ||||
|         } | ||||
|  | ||||
|         Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded)); | ||||
|         Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved)); | ||||
|  | ||||
|         Main.layoutManager.panelBox.add(this.actor); | ||||
|         Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here', | ||||
|                                         { sortGroup: CtrlAltTab.SortGroup.TOP }); | ||||
|     }, | ||||
|  | ||||
|     _getPreferredWidth: function(actor, forHeight, alloc) { | ||||
| @@ -1026,19 +1057,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; | ||||
| @@ -1086,126 +1114,84 @@ 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 < this._status_area_order.length; i++) { | ||||
|             let role = this._status_area_order[i]; | ||||
|             let constructor = this._status_area_shell_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); | ||||
|         this._menus.addMenu(indicator.menu); | ||||
|  | ||||
|         this._statusArea[role] = indicator; | ||||
|         let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) { | ||||
|             this._statusArea[role] = null; | ||||
|             emitter.disconnect(destroyId); | ||||
|         })); | ||||
|  | ||||
|         return indicator; | ||||
|     } | ||||
|     }, | ||||
|  | ||||
|     _onTrayIconAdded: function(o, icon, role) { | ||||
|         if (this._status_area_shell_implementation[role]) { | ||||
|             // This icon is legacy, and replaced by a Shell version | ||||
|             // Hide it | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         icon.height = PANEL_ICON_SIZE; | ||||
|         let buttonBox = new PanelMenu.ButtonBox(); | ||||
|         let box = buttonBox.actor; | ||||
|         box.add_actor(icon); | ||||
|  | ||||
|         this._insertStatusItem(box, this._status_area_order.indexOf(role)); | ||||
|     }, | ||||
|  | ||||
|     _onTrayIconRemoved: function(o, icon) { | ||||
|         let box = icon.get_parent(); | ||||
|         if (box && box._delegate instanceof PanelMenu.ButtonBox) | ||||
|             box.destroy(); | ||||
|     }, | ||||
| }); | ||||
|   | ||||
| @@ -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 | ||||
| @@ -186,7 +175,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); | ||||
| @@ -234,36 +224,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
									
								
							
							
						
						| @@ -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,126 +0,0 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| 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 = Shell.IdleMonitor.get(); | ||||
|         idleMonitor.add_watch(IDLE_TIME, | ||||
|                               Lang.bind(this, this._onIdleMonitorWatch)); | ||||
|         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; | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onIdleMonitorWatch: function(monitor, id, userBecameIdle) { | ||||
|         this._idle = userBecameIdle; | ||||
|         if (!userBecameIdle) | ||||
|             this._updatePointer(); | ||||
|  | ||||
|         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, | ||||
| @@ -121,17 +135,17 @@ const AuthenticationDialog = new Lang.Class({ | ||||
| 
 | ||||
|         this._onUserChanged(); | ||||
| 
 | ||||
|         this._passwordBox = new St.BoxLayout({ vertical: false, style_class: 'prompt-dialog-password-box' }); | ||||
|         this._passwordBox = new St.BoxLayout({ vertical: false }); | ||||
|         messageBox.add(this._passwordBox); | ||||
|         this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label' })); | ||||
|         this._passwordBox.add(this._passwordLabel, { y_fill: false, y_align: St.Align.MIDDLE }); | ||||
|         this._passwordBox.add(this._passwordLabel); | ||||
|         this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry', | ||||
|                                              text: "", | ||||
|                                              can_focus: true}); | ||||
|         ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true }); | ||||
|         this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate)); | ||||
|         this._passwordBox.add(this._passwordEntry, | ||||
|                               { expand: true }); | ||||
|                               {expand: true }); | ||||
|         this.setInitialKeyFocus(this._passwordEntry); | ||||
|         this._passwordBox.hide(); | ||||
| 
 | ||||
| @@ -153,7 +167,6 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|          */ | ||||
|         this._nullMessageLabel = new St.Label({ style_class: 'prompt-dialog-null-label', | ||||
|                                                 text: 'abc'}); | ||||
|         this._nullMessageLabel.add_style_class_name('hidden'); | ||||
|         this._nullMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         this._nullMessageLabel.clutter_text.line_wrap = true; | ||||
|         messageBox.add(this._nullMessageLabel); | ||||
| @@ -164,8 +177,7 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|                            key:    Clutter.Escape | ||||
|                          }, | ||||
|                          { label:  _("Authenticate"), | ||||
|                            action: Lang.bind(this, this._onAuthenticateButtonPressed), | ||||
|                            default: true | ||||
|                            action: Lang.bind(this, this._onAuthenticateButtonPressed) | ||||
|                          }]); | ||||
| 
 | ||||
|         this._doneEmitted = false; | ||||
| @@ -256,7 +268,7 @@ const AuthenticationDialog = new Lang.Class({ | ||||
| 
 | ||||
|     _onSessionRequest: function(session, request, echo_on) { | ||||
|         // Cheap localization trick
 | ||||
|         if (request == 'Password:' || request == 'Password: ') | ||||
|         if (request == 'Password:') | ||||
|             this._passwordLabel.set_text(_("Password:")); | ||||
|         else | ||||
|             this._passwordLabel.set_text(request); | ||||
| @@ -299,9 +311,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 +339,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 +401,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) { | ||||
| @@ -139,6 +136,10 @@ const PopupBaseMenuItem = new Lang.Class({ | ||||
|         this.actor.reactive = sensitive; | ||||
|         this.actor.can_focus = sensitive; | ||||
|  | ||||
|         if (sensitive) | ||||
|             this.actor.remove_style_pseudo_class('insensitive'); | ||||
|         else | ||||
|             this.actor.add_style_pseudo_class('insensitive'); | ||||
|         this.emit('sensitive-changed', sensitive); | ||||
|     }, | ||||
|  | ||||
| @@ -183,14 +184,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 +400,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 +719,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 +763,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; | ||||
|     }, | ||||
| @@ -776,10 +773,12 @@ const PopupSwitchMenuItem = new Lang.Class({ | ||||
|             this._statusLabel.text = text; | ||||
|             this._statusBin.child = this._statusLabel; | ||||
|             this.actor.reactive = false; | ||||
|             this.actor.can_focus = false; | ||||
|             this.actor.accessible_role = Atk.Role.MENU_ITEM; | ||||
|         } else { | ||||
|             this._statusBin.child = this._switch.actor; | ||||
|             this.actor.reactive = true; | ||||
|             this.actor.can_focus = true; | ||||
|             this.actor.accessible_role = Atk.Role.CHECK_MENU_ITEM; | ||||
|         } | ||||
|         this.checkAccessibleState(); | ||||
| @@ -870,15 +869,12 @@ 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 +888,10 @@ const PopupMenuBase = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     addSettingsAction: function(title, desktopFile) { | ||||
|         // Don't allow user settings to get edited unless we're in a user session | ||||
|         if (global.session_type != Shell.SessionType.USER) | ||||
|             return null; | ||||
|  | ||||
|         let menuItem = this.addAction(title, function() { | ||||
|                            let app = Shell.AppSystem.get_default().lookup_setting(desktopFile); | ||||
|  | ||||
| @@ -903,22 +903,11 @@ const PopupMenuBase = new Lang.Class({ | ||||
|                            Main.overview.hide(); | ||||
|                            app.activate(); | ||||
|                        }); | ||||
|  | ||||
|         menuItem.actor.visible = Main.sessionMode.allowSettings; | ||||
|         this._settingsActions[desktopFile] = menuItem; | ||||
|  | ||||
|         return menuItem; | ||||
|     }, | ||||
|  | ||||
|     _setSettingsVisibility: function(visible) { | ||||
|         for (let id in this._settingsActions) { | ||||
|             let item = this._settingsActions[id]; | ||||
|             item.actor.visible = visible; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     isEmpty: function() { | ||||
|         return this.box.get_n_children() == 0; | ||||
|         return this.box.get_children().length == 0; | ||||
|     }, | ||||
|  | ||||
|     isChildMenu: function(menu) { | ||||
| @@ -954,7 +943,7 @@ const PopupMenuBase = new Lang.Class({ | ||||
|     _connectSubMenuSignals: function(object, menu) { | ||||
|         object._subMenuActivateId = menu.connect('activate', Lang.bind(this, function() { | ||||
|             this.emit('activate'); | ||||
|             this.close(BoxPointer.PopupAnimation.FULL); | ||||
|             this.close(true); | ||||
|         })); | ||||
|         object._subMenuActiveChangeId = menu.connect('active-changed', Lang.bind(this, function(submenu, submenuItem) { | ||||
|             if (this._activeMenuItem && this._activeMenuItem != submenuItem) | ||||
| @@ -989,7 +978,7 @@ const PopupMenuBase = new Lang.Class({ | ||||
|         })); | ||||
|         menuItem._activateId = menuItem.connect('activate', Lang.bind(this, function (menuItem, event) { | ||||
|             this.emit('activate', menuItem); | ||||
|             this.close(BoxPointer.PopupAnimation.FULL); | ||||
|             this.close(true); | ||||
|         })); | ||||
|         // the weird name is to avoid a conflict with some random property | ||||
|         // the menuItem may have, called destroyId | ||||
| @@ -1059,17 +1048,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(false); | ||||
|                 }); | ||||
|             menuItem.connect('destroy', Lang.bind(this, function() { | ||||
|                 menuItem.disconnect(menuItem._subMenuActivateId); | ||||
|                 menuItem.disconnect(menuItem._subMenuActiveChangeId); | ||||
|                 this.disconnect(menuItem._parentOpenStateChangedId); | ||||
|  | ||||
|                 this.length--; | ||||
|             })); | ||||
| @@ -1082,7 +1068,7 @@ const PopupMenuBase = new Lang.Class({ | ||||
|             this._connectItemSignals(menuItem); | ||||
|             menuItem._closingId = this.connect('open-state-changed', function(self, open) { | ||||
|                 if (!open) | ||||
|                     menuItem.menu.close(BoxPointer.PopupAnimation.FADE); | ||||
|                     menuItem.menu.close(false); | ||||
|             }); | ||||
|         } else if (menuItem instanceof PopupSeparatorMenuItem) { | ||||
|             this._connectItemSignals(menuItem); | ||||
| @@ -1104,8 +1090,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(); | ||||
| @@ -1170,9 +1155,9 @@ const PopupMenuBase = new Lang.Class({ | ||||
|  | ||||
|     toggle: function() { | ||||
|         if (this.isOpen) | ||||
|             this.close(BoxPointer.PopupAnimation.FULL); | ||||
|             this.close(true); | ||||
|         else | ||||
|             this.open(BoxPointer.PopupAnimation.FULL); | ||||
|             this.open(true); | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
| @@ -1180,9 +1165,6 @@ const PopupMenuBase = new Lang.Class({ | ||||
|         this.actor.destroy(); | ||||
|  | ||||
|         this.emit('destroy'); | ||||
|  | ||||
|         Main.sessionMode.disconnect(this._sessionUpdatedId); | ||||
|         this._sessionUpdatedId = 0; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(PopupMenuBase.prototype); | ||||
| @@ -1236,7 +1218,7 @@ const PopupMenu = new Lang.Class({ | ||||
|  | ||||
|     _onKeyPressEvent: function(actor, event) { | ||||
|         if (event.get_key_symbol() == Clutter.Escape) { | ||||
|             this.close(BoxPointer.PopupAnimation.FULL); | ||||
|             this.close(true); | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
| @@ -1299,6 +1281,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; | ||||
| @@ -1348,11 +1348,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) | ||||
| @@ -1425,7 +1420,7 @@ const PopupSubMenu = new Lang.Class({ | ||||
|         // Move focus back to parent menu if the user types Left. | ||||
|  | ||||
|         if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) { | ||||
|             this.close(BoxPointer.PopupAnimation.FULL); | ||||
|             this.close(true); | ||||
|             this.sourceActor._delegate.setActive(true); | ||||
|             return true; | ||||
|         } | ||||
| @@ -1459,7 +1454,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() { | ||||
| @@ -1507,7 +1502,7 @@ const PopupSubMenuMenuItem = new Lang.Class({ | ||||
|         let symbol = event.get_key_symbol(); | ||||
|  | ||||
|         if (symbol == Clutter.KEY_Right) { | ||||
|             this.menu.open(BoxPointer.PopupAnimation.FULL); | ||||
|             this.menu.open(true); | ||||
|             this.menu.actor.navigate_focus(null, Gtk.DirectionType.DOWN, false); | ||||
|             return true; | ||||
|         } else if (symbol == Clutter.KEY_Left && this.menu.isOpen) { | ||||
| @@ -1519,7 +1514,7 @@ const PopupSubMenuMenuItem = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     activate: function(event) { | ||||
|         this.menu.open(BoxPointer.PopupAnimation.FULL); | ||||
|         this.menu.open(true); | ||||
|     }, | ||||
|  | ||||
|     _onButtonReleaseEvent: function(actor) { | ||||
| @@ -1546,7 +1541,7 @@ const PopupComboMenu = new Lang.Class({ | ||||
|  | ||||
|     _onKeyPressEvent: function(actor, event) { | ||||
|         if (event.get_key_symbol() == Clutter.Escape) { | ||||
|             this.close(BoxPointer.PopupAnimation.FULL); | ||||
|             this.close(true); | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
| @@ -1898,6 +1893,10 @@ const RemoteMenu = new Lang.Class({ | ||||
|         } | ||||
|  | ||||
|         item.actor.reactive = item.actor.can_focus = action.enabled; | ||||
|         if (action.enabled) | ||||
|             item.actor.remove_style_pseudo_class('insensitive'); | ||||
|         else | ||||
|             item.actor.add_style_pseudo_class('insensitive'); | ||||
|  | ||||
|         destroyId = item.connect('destroy', Lang.bind(this, function() { | ||||
|             item.disconnect(destroyId); | ||||
| @@ -1922,7 +1921,7 @@ const RemoteMenu = new Lang.Class({ | ||||
|         while (k0 < currentItems.length && currentItems[k0]._ignored) | ||||
|             k0++; | ||||
|         // find the right menu item matching the model item | ||||
|         for (j0 = 0; k0 < currentItems.length && j0 < position; j0++, k0++) { | ||||
|         for (j0 = 0; j0 < position; j0++, k0++) { | ||||
|             if (currentItems[k0]._ignored) | ||||
|                 k0++; | ||||
|         } | ||||
| @@ -1932,7 +1931,7 @@ const RemoteMenu = new Lang.Class({ | ||||
|             for (k = k0; k < currentItems.length; k++) | ||||
|                 currentItems[k].destroy(); | ||||
|         } else { | ||||
|             for (j = j0, k = k0; k < currentItems.length && j < j0 + removed; j++, k++) { | ||||
|             for (j = j0, k = k0; j < j0 + removed; j++, k++) { | ||||
|                 currentItems[k].destroy(); | ||||
|  | ||||
|                 if (currentItems[k]._ignored) | ||||
| @@ -1963,9 +1962,8 @@ const RemoteMenu = new Lang.Class({ | ||||
|                     k++; | ||||
|                 } | ||||
|             } else if (changeSignal) { | ||||
|                 let signalId = this.actionGroup.connect(changeSignal, Lang.bind(this, function(actionGroup, actionName) { | ||||
|                     actionGroup.disconnect(signalId); | ||||
|                     if (this._actions[actionName]) return; | ||||
|                 let signalId = this.actionGroup.connect(changeSignal, Lang.bind(this, function() { | ||||
|                     this.actionGroup.disconnect(signalId); | ||||
|  | ||||
|                     // force a full update | ||||
|                     this._modelChanged(model, 0, -1, model.get_n_items(), target); | ||||
| @@ -2030,6 +2028,11 @@ const RemoteMenu = new Lang.Class({ | ||||
|             for (let i = 0; i < action.items.length; i++) { | ||||
|                 let item = action.items[i]; | ||||
|                 item.actor.reactive = item.actor.can_focus = action.enabled; | ||||
|  | ||||
|                 if (action.enabled) | ||||
|                     item.actor.remove_style_pseudo_class('insensitive'); | ||||
|                 else | ||||
|                     item.actor.add_style_pseudo_class('insensitive'); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -2057,9 +2060,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)), | ||||
| @@ -2194,11 +2194,11 @@ const PopupMenuManager = new Lang.Class({ | ||||
|             let oldMenu = this._activeMenu; | ||||
|             this._activeMenu = null; | ||||
|             for (let i = this._menuStack.length - 1; i >= 0; i--) | ||||
|                 this._menuStack[i].close(BoxPointer.PopupAnimation.FADE); | ||||
|             oldMenu.close(BoxPointer.PopupAnimation.FADE); | ||||
|             newMenu.open(BoxPointer.PopupAnimation.FADE); | ||||
|                 this._menuStack[i].close(false); | ||||
|             oldMenu.close(false); | ||||
|             newMenu.open(false); | ||||
|         } else | ||||
|             newMenu.open(BoxPointer.PopupAnimation.FULL); | ||||
|             newMenu.open(true); | ||||
|     }, | ||||
|  | ||||
|     _onMenuSourceEnter: function(menu) { | ||||
| @@ -2283,6 +2283,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; | ||||
| @@ -2310,6 +2313,6 @@ const PopupMenuManager = new Lang.Class({ | ||||
|  | ||||
|     _closeMenu: function() { | ||||
|         if (this._activeMenu != null) | ||||
|             this._activeMenu.close(BoxPointer.PopupAnimation.FULL); | ||||
|             this._activeMenu.close(true); | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -34,17 +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); | ||||
|         if (!dir.query_exists(null)) | ||||
|             continue; | ||||
|         loadRemoteSearchProvidersFromDir(dir, loadedProviders, addProviderCallback); | ||||
|         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++) { | ||||
| @@ -63,34 +62,15 @@ function loadRemoteSearchProvidersFromDir(dir, loadedProviders, addProviderCallb | ||||
|             let remoteProvider, title; | ||||
|             try { | ||||
|                 let group = KEY_FILE_GROUP; | ||||
|                 let icon = keyfile.get_string(group, 'Icon'); | ||||
|                 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) { | ||||
|                 } | ||||
|  | ||||
|                 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); | ||||
|                 } | ||||
|                 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(title, e.toString())); | ||||
|                 continue; | ||||
| @@ -117,7 +97,8 @@ const RemoteSearchProvider = new Lang.Class({ | ||||
|     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']; | ||||
| @@ -128,7 +109,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) { | ||||
|   | ||||
| @@ -1,339 +1,19 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Cairo = imports.cairo; | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const GnomeDesktop = imports.gi.GnomeDesktop; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Signals = imports.signals; | ||||
| const St = imports.gi.St; | ||||
| const TweenerEquations = imports.tweener.equations; | ||||
|  | ||||
| const GnomeSession = imports.misc.gnomeSession; | ||||
| const Layout = imports.ui.layout; | ||||
| const LoginManager = imports.misc.loginManager; | ||||
| const Lightbox = imports.ui.lightbox; | ||||
| const UnlockDialog = imports.ui.unlockDialog; | ||||
| const Main = imports.ui.main; | ||||
| const Overview = imports.ui.overview; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const ShellDBus = imports.ui.shellDBus; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver'; | ||||
| const LOCK_ENABLED_KEY = 'lock-enabled'; | ||||
|  | ||||
| const CURTAIN_SLIDE_TIME = 0.5; | ||||
| // fraction of screen height the arrow must reach before completing | ||||
| // the slide up automatically | ||||
| const ARROW_DRAG_THRESHOLD = 0.1; | ||||
|  | ||||
| // Parameters for the arrow animation | ||||
| const N_ARROWS = 3; | ||||
| const ARROW_ANIMATION_TIME = 0.6; | ||||
| const ARROW_ANIMATION_PEAK_OPACITY = 0.4; | ||||
|  | ||||
| // The distance in px that the lock screen will move to when pressing | ||||
| // a key that has no effect in the lock screen (bumping it) | ||||
| const BUMP_SIZE = 25; | ||||
| const BUMP_TIME = 0.3; | ||||
|  | ||||
| const SUMMARY_ICON_SIZE = 48; | ||||
|  | ||||
| // Lightbox fading times | ||||
| // STANDARD_FADE_TIME is used when the session goes idle, while | ||||
| // SHORT_FADE_TIME is used when requesting lock explicitly from the user menu | ||||
| const STANDARD_FADE_TIME = 10; | ||||
| const SHORT_FADE_TIME = 0.8; | ||||
|  | ||||
| const Clock = new Lang.Class({ | ||||
|     Name: 'ScreenShieldClock', | ||||
|  | ||||
|     CLOCK_FORMAT_KEY: 'clock-format', | ||||
|     CLOCK_SHOW_SECONDS_KEY: 'clock-show-seconds', | ||||
|  | ||||
|     _init: function() { | ||||
|         this.actor = new St.BoxLayout({ style_class: 'screen-shield-clock', | ||||
|                                         vertical: true }); | ||||
|  | ||||
|         this._time = new St.Label({ style_class: 'screen-shield-clock-time' }); | ||||
|         this._date = new St.Label({ style_class: 'screen-shield-clock-date' }); | ||||
|  | ||||
|         this.actor.add(this._time, { x_align: St.Align.MIDDLE }); | ||||
|         this.actor.add(this._date, { x_align: St.Align.MIDDLE }); | ||||
|  | ||||
|         this._wallClock = new GnomeDesktop.WallClock({ time_only: true }); | ||||
|         this._wallClock.connect('notify::clock', Lang.bind(this, this._updateClock)); | ||||
|  | ||||
|         this._updateClock(); | ||||
|     }, | ||||
|  | ||||
|     _updateClock: function() { | ||||
|         this._time.text = this._wallClock.clock; | ||||
|  | ||||
|         let date = new Date(); | ||||
|         /* Translators: This is a time format for a date in | ||||
|            long format */ | ||||
|         this._date.text = date.toLocaleFormat(_("%A, %B %d")); | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
|         this.actor.destroy(); | ||||
|         this._wallClock.run_dispose(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const NotificationsBox = new Lang.Class({ | ||||
|     Name: 'NotificationsBox', | ||||
|  | ||||
|     _init: function() { | ||||
|         this.actor = new St.BoxLayout({ vertical: true, | ||||
|                                         name: 'screenShieldNotifications', | ||||
|                                         style_class: 'screen-shield-notifications-box' }); | ||||
|  | ||||
|         this._residentNotificationBox = new St.BoxLayout({ vertical: true, | ||||
|                                                            style_class: 'screen-shield-notifications-box' }); | ||||
|         let scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START }); | ||||
|         this._persistentNotificationBox = new St.BoxLayout({ vertical: true, | ||||
|                                                              style_class: 'screen-shield-notifications-box' }); | ||||
|         scrollView.add_actor(this._persistentNotificationBox); | ||||
|  | ||||
|         this.actor.add(this._residentNotificationBox, { x_fill: true }); | ||||
|         this.actor.add(scrollView, { x_fill: true, x_align: St.Align.START }); | ||||
|  | ||||
|         this._items = []; | ||||
|         Main.messageTray.getSummaryItems().forEach(Lang.bind(this, function(item) { | ||||
|             this._summaryItemAdded(Main.messageTray, item, true); | ||||
|         })); | ||||
|         this._updateVisibility(); | ||||
|  | ||||
|         this._summaryAddedId = Main.messageTray.connect('summary-item-added', Lang.bind(this, this._summaryItemAdded)); | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
|         if (this._summaryAddedId) { | ||||
|             Main.messageTray.disconnect(this._summaryAddedId); | ||||
|             this._summaryAddedId = 0; | ||||
|         } | ||||
|  | ||||
|         for (let i = 0; i < this._items.length; i++) | ||||
|             this._removeItem(this._items[i]); | ||||
|         this._items = []; | ||||
|  | ||||
|         this.actor.destroy(); | ||||
|     }, | ||||
|  | ||||
|     _updateVisibility: function() { | ||||
|         this._residentNotificationBox.visible = this._residentNotificationBox.get_n_children() > 0; | ||||
|         this._persistentNotificationBox.visible = this._persistentNotificationBox.get_children().some(function(a) { | ||||
|             return a.visible; | ||||
|         }); | ||||
|  | ||||
|         this.actor.visible = this._residentNotificationBox.visible || this._persistentNotificationBox.visible; | ||||
|     }, | ||||
|  | ||||
|     _sourceIsResident: function(source) { | ||||
|         return source.hasResidentNotification() && !source.isChat; | ||||
|     }, | ||||
|  | ||||
|     _makeNotificationCountText: function(count, isChat) { | ||||
|         if (isChat) | ||||
|             return ngettext("%d new message", "%d new messages", count).format(count); | ||||
|         else | ||||
|             return ngettext("%d new notification", "%d new notifications", count).format(count); | ||||
|     }, | ||||
|  | ||||
|     _makeNotificationSource: function(source) { | ||||
|         let box = new St.BoxLayout({ style_class: 'screen-shield-notification-source' }); | ||||
|  | ||||
|         let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE); | ||||
|         box.add(sourceActor.actor, { y_fill: true }); | ||||
|  | ||||
|         let textBox = new St.BoxLayout({ vertical: true }); | ||||
|         box.add(textBox, { y_fill: false, y_align: St.Align.START }); | ||||
|  | ||||
|         let label = new St.Label({ text: source.title, | ||||
|                                    style_class: 'screen-shield-notification-label' }); | ||||
|         textBox.add(label); | ||||
|  | ||||
|         let count = source.unseenCount; | ||||
|         let countLabel = new St.Label({ text: this._makeNotificationCountText(count, source.isChat), | ||||
|                                         style_class: 'screen-shield-notification-count-text' }); | ||||
|         textBox.add(countLabel); | ||||
|  | ||||
|         box.visible = count != 0; | ||||
|         return [box, countLabel]; | ||||
|     }, | ||||
|  | ||||
|     _summaryItemAdded: function(tray, item, dontUpdateVisibility) { | ||||
|         // Ignore transient sources, or sources explicitly marked not to show | ||||
|         // in the lock screen | ||||
|         if (item.source.isTransient || !item.source.showInLockScreen) | ||||
|             return; | ||||
|  | ||||
|         let obj = { | ||||
|             item: item, | ||||
|             source: item.source, | ||||
|             resident: this._sourceIsResident(item.source), | ||||
|             contentUpdatedId: 0, | ||||
|             sourceDestroyId: 0, | ||||
|             sourceBox: null, | ||||
|             countLabel: null, | ||||
|         }; | ||||
|  | ||||
|         if (obj.resident) { | ||||
|             this._residentNotificationBox.add(item.notificationStackWidget); | ||||
|             item.closeButtonVisible = false; | ||||
|             item.prepareNotificationStackForShowing(); | ||||
|         } else { | ||||
|             [obj.sourceBox, obj.countLabel] = this._makeNotificationSource(item.source); | ||||
|             this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START }); | ||||
|         } | ||||
|  | ||||
|         obj.contentUpdatedId = item.connect('content-updated', Lang.bind(this, this._onItemContentUpdated)); | ||||
|         obj.sourceCountChangedId = item.source.connect('count-updated', Lang.bind(this, this._onSourceChanged)); | ||||
|         obj.sourceTitleChangedId = item.source.connect('title-changed', Lang.bind(this, this._onSourceChanged)); | ||||
|         obj.sourceDestroyId = item.source.connect('destroy', Lang.bind(this, this._onSourceDestroy)); | ||||
|         this._items.push(obj); | ||||
|  | ||||
|         if (!dontUpdateVisibility) | ||||
|             this._updateVisibility(); | ||||
|     }, | ||||
|  | ||||
|     _findSource: function(source) { | ||||
|         for (let i = 0; i < this._items.length; i++) { | ||||
|             if (this._items[i].source == source) | ||||
|                 return i; | ||||
|         } | ||||
|  | ||||
|         return -1; | ||||
|     }, | ||||
|  | ||||
|     _onItemContentUpdated: function(item) { | ||||
|         let obj = this._items[this._findSource(item.source)]; | ||||
|         this._updateItem(obj); | ||||
|     }, | ||||
|  | ||||
|     _onSourceChanged: function(source) { | ||||
|         let obj = this._items[this._findSource(source)]; | ||||
|         this._updateItem(obj); | ||||
|     }, | ||||
|  | ||||
|     _updateItem: function(obj) { | ||||
|         let itemShouldBeResident = this._sourceIsResident(obj.source); | ||||
|  | ||||
|         if (itemShouldBeResident && obj.resident) { | ||||
|             // Nothing to do here, the actor is already updated | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (obj.resident && !itemShouldBeResident) { | ||||
|             // make into a regular item | ||||
|             obj.item.doneShowingNotificationStack(); | ||||
|             this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget); | ||||
|  | ||||
|             [obj.sourceBox, obj.countLabel] = this._makeNotificationSource(obj.source); | ||||
|             this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START }); | ||||
|         } else if (itemShouldBeResident && !obj.resident) { | ||||
|             // make into a resident item | ||||
|             obj.sourceBox.destroy(); | ||||
|             obj.sourceBox = obj.countLabel = null; | ||||
|             obj.resident = true; | ||||
|  | ||||
|             this._residentNotificationBox.add(obj.item.notificationStackWidget); | ||||
|             obj.item.closeButtonVisible = false; | ||||
|             obj.item.prepareNotificationStackForShowing(); | ||||
|         } else { | ||||
|             // just update the counter | ||||
|             let count = obj.source.unseenCount; | ||||
|             obj.countLabel.text = this._makeNotificationCountText(count, obj.source.isChat); | ||||
|             obj.sourceBox.visible = count != 0; | ||||
|         } | ||||
|  | ||||
|         this._updateVisibility(); | ||||
|     }, | ||||
|  | ||||
|     _onSourceDestroy: function(source) { | ||||
|         let idx = this._findSource(source); | ||||
|  | ||||
|         this._removeItem(this._items[idx]); | ||||
|         this._items.splice(idx, 1); | ||||
|  | ||||
|         this._updateVisibility(); | ||||
|     }, | ||||
|  | ||||
|     _removeItem: function(obj) { | ||||
|         if (obj.resident) { | ||||
|             obj.item.doneShowingNotificationStack(); | ||||
|             this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget); | ||||
|         } else { | ||||
|             obj.sourceBox.destroy(); | ||||
|         } | ||||
|  | ||||
|         obj.item.disconnect(obj.contentUpdatedId); | ||||
|         obj.source.disconnect(obj.sourceDestroyId); | ||||
|         obj.source.disconnect(obj.sourceCountChangedId); | ||||
|     }, | ||||
| }); | ||||
|  | ||||
| const Arrow = new Lang.Class({ | ||||
|     Name: 'Arrow', | ||||
|     Extends: St.Bin, | ||||
|  | ||||
|     _init: function(params) { | ||||
|         this.parent(params); | ||||
|         this.x_fill = this.y_fill = true; | ||||
|         this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS); | ||||
|  | ||||
|         this._drawingArea = new St.DrawingArea(); | ||||
|         this._drawingArea.connect('repaint', Lang.bind(this, this._drawArrow)); | ||||
|         this.child = this._drawingArea; | ||||
|  | ||||
|         this._shadowHelper = null; | ||||
|         this._shadowWidth = this._shadowHeight = 0; | ||||
|     }, | ||||
|  | ||||
|     _drawArrow: function(arrow) { | ||||
|         let cr = arrow.get_context(); | ||||
|         let [w, h] = arrow.get_surface_size(); | ||||
|         let node = this.get_theme_node(); | ||||
|         let thickness = node.get_length('-arrow-thickness'); | ||||
|  | ||||
|         Clutter.cairo_set_source_color(cr, node.get_foreground_color()); | ||||
|  | ||||
|         cr.setLineCap(Cairo.LineCap.ROUND); | ||||
|         cr.setLineWidth(thickness); | ||||
|  | ||||
|         cr.moveTo(thickness / 2, h - thickness / 2); | ||||
|         cr.lineTo(w/2, thickness); | ||||
|         cr.lineTo(w - thickness / 2, h - thickness / 2); | ||||
|         cr.stroke(); | ||||
|     }, | ||||
|  | ||||
|     vfunc_style_changed: function() { | ||||
|         let node = this.get_theme_node(); | ||||
|         this._shadow = node.get_shadow('-arrow-shadow'); | ||||
|         if (this._shadow) | ||||
|             this._shadowHelper = St.ShadowHelper.new(this._shadow); | ||||
|         else | ||||
|             this._shadowHelper = null; | ||||
|     }, | ||||
|  | ||||
|     vfunc_paint: function() { | ||||
|         if (this._shadowHelper) { | ||||
|             this._shadowHelper.update(this._drawingArea); | ||||
|  | ||||
|             let allocation = this._drawingArea.get_allocation_box(); | ||||
|             let paintOpacity = this._drawingArea.get_paint_opacity(); | ||||
|             this._shadowHelper.paint(allocation, paintOpacity); | ||||
|         } | ||||
|  | ||||
|         this._drawingArea.paint(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| /** | ||||
|  * To test screen shield, make sure to kill gnome-screensaver. | ||||
|  * | ||||
| @@ -348,556 +28,115 @@ const ScreenShield = new Lang.Class({ | ||||
|     Name: 'ScreenShield', | ||||
|  | ||||
|     _init: function() { | ||||
|         this.actor = Main.layoutManager.screenShieldGroup; | ||||
|  | ||||
|         this._lockScreenState = MessageTray.State.HIDDEN; | ||||
|         this._lockScreenGroup = new St.Widget({ x_expand: true, | ||||
|                                                 y_expand: true, | ||||
|                                                 reactive: true, | ||||
|                                                 can_focus: true, | ||||
|                                                 name: 'lockScreenGroup', | ||||
|                                               }); | ||||
|         this._lockScreenGroup.connect('key-release-event', | ||||
|                                       Lang.bind(this, this._onLockScreenKeyRelease)); | ||||
|         this._lockScreenGroup.connect('scroll-event', | ||||
|                                       Lang.bind(this, this._onLockScreenScroll)); | ||||
|  | ||||
|         this._lockScreenContents = new St.Widget({ layout_manager: new Clutter.BinLayout(), | ||||
|                                                    name: 'lockScreenContents' }); | ||||
|         this._lockScreenContents.add_constraint(new Layout.MonitorConstraint({ primary: true })); | ||||
|  | ||||
|         this._background = new St.Bin({ style_class: 'screen-shield-background', | ||||
|                                         child: Meta.BackgroundActor.new_for_screen(global.screen) }); | ||||
|         this._lockScreenGroup.add_actor(this._background); | ||||
|         this._lockScreenGroup.add_actor(this._lockScreenContents); | ||||
|  | ||||
|         this._arrowContainer = new St.BoxLayout({ style_class: 'screen-shield-arrows', | ||||
|                                                   vertical: true, | ||||
|                                                   x_align: Clutter.ActorAlign.CENTER, | ||||
|                                                   y_align: Clutter.ActorAlign.END, | ||||
|                                                   // HACK: without these, ClutterBinLayout | ||||
|                                                   // ignores alignment properties on the actor | ||||
|                                                   x_expand: true, | ||||
|                                                   y_expand: true }); | ||||
|  | ||||
|         for (let i = 0; i < N_ARROWS; i++) { | ||||
|             let arrow = new Arrow({ opacity: 0 }); | ||||
|             this._arrowContainer.add_actor(arrow); | ||||
|         } | ||||
|         this._lockScreenContents.add_actor(this._arrowContainer); | ||||
|  | ||||
|         let dragArea = new Clutter.Rect({ origin: new Clutter.Point({ x: 0, y: -global.screen_height, }), | ||||
|                                           size: new Clutter.Size({ width: global.screen_width, | ||||
|                                                                    height: global.screen_height }) }); | ||||
|         let action = new Clutter.DragAction({ drag_axis: Clutter.DragAxis.Y_AXIS, | ||||
|                                               drag_area: dragArea }); | ||||
|         action.connect('drag-begin', Lang.bind(this, this._onDragBegin)); | ||||
|         action.connect('drag-end', Lang.bind(this, this._onDragEnd)); | ||||
|         this._lockScreenGroup.add_action(action); | ||||
|  | ||||
|         this._lockDialogGroup = new St.Widget({ x_expand: true, | ||||
|                                                 y_expand: true, | ||||
|                                                 pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }), | ||||
|                                                 name: 'lockDialogGroup' }); | ||||
|  | ||||
|         this.actor.add_actor(this._lockDialogGroup); | ||||
|         this.actor.add_actor(this._lockScreenGroup); | ||||
|  | ||||
|         this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) { | ||||
|             if (error) { | ||||
|                 logError(error, 'Error while reading gnome-session presence'); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             this._onStatusChanged(proxy.status); | ||||
|         })); | ||||
|         this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) { | ||||
|             this._onStatusChanged(status); | ||||
|         })); | ||||
|  | ||||
|         this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this); | ||||
|  | ||||
|         this._loginManager = LoginManager.getLoginManager(); | ||||
|         this._loginSession = this._loginManager.getCurrentSessionProxy(); | ||||
|         this._loginSession.connectSignal('Lock', Lang.bind(this, function() { this.lock(false); })); | ||||
|         this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.unlock(); })); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA }); | ||||
|  | ||||
|         this._isModal = false; | ||||
|         this._hasLockScreen = false; | ||||
|         this._isGreeter = false; | ||||
|         this._isActive = false; | ||||
|         this._inUnlockAnimation = false; | ||||
|         this._isLocked = false; | ||||
|         this._group = new St.Widget({ x: 0, | ||||
|                                       y: 0 }); | ||||
|         Main.uiGroup.add_actor(this._group); | ||||
|         let constraint = new Clutter.BindConstraint({ source: global.stage, | ||||
|                                                       coordinate: Clutter.BindCoordinate.POSITION | Clutter.BindCoordinate.SIZE }); | ||||
|         this._group.add_constraint(constraint); | ||||
|  | ||||
|         this._lightbox = new Lightbox.Lightbox(Main.uiGroup, | ||||
|                                                { inhibitEvents: true, | ||||
|                                                  fadeInTime: STANDARD_FADE_TIME, | ||||
|                                                  fadeFactor: 1 }); | ||||
|     }, | ||||
|  | ||||
|     _onLockScreenKeyRelease: function(actor, event) { | ||||
|         let symbol = event.get_key_symbol(); | ||||
|  | ||||
|         // Do nothing if the lock screen is not fully shown. | ||||
|         // This avoids reusing the previous (and stale) unlock | ||||
|         // dialog if esc is pressed while the curtain is going | ||||
|         // down after cancel. | ||||
|         // Similarly, don't bump if the lock screen is not showing or is | ||||
|         // animating, as the bump overrides the animation and would | ||||
|         // remove any onComplete handler. | ||||
|  | ||||
|         if (this._lockScreenState != MessageTray.State.SHOWN) | ||||
|             return false; | ||||
|  | ||||
|         if (symbol == Clutter.KEY_Escape || | ||||
|             symbol == Clutter.KEY_Return || | ||||
|             symbol == Clutter.KEY_KP_Enter) { | ||||
|             this._ensureUnlockDialog(true); | ||||
|             this._hideLockScreen(true); | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         this._bumpLockScreen(); | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _onLockScreenScroll: function(actor, event) { | ||||
|         if (this._lockScreenState != MessageTray.State.SHOWN) | ||||
|             return false; | ||||
|  | ||||
|         let delta = 0; | ||||
|         if (event.get_scroll_direction() == Clutter.ScrollDirection.UP) | ||||
|             delta = 5; | ||||
|         else if (event.get_scroll_direction() == Clutter.ScrollDirection.SMOOTH) | ||||
|             delta = Math.max(0, event.get_scroll_delta()[0]); | ||||
|  | ||||
|         this._lockScreenScrollCounter += delta; | ||||
|  | ||||
|         // 7 standard scrolls to lift up | ||||
|         if (this._lockScreenScrollCounter > 35) { | ||||
|             this._ensureUnlockDialog(false); | ||||
|             this._hideLockScreen(true); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _animateArrows: function() { | ||||
|         let arrows = this._arrowContainer.get_children(); | ||||
|         let unitaryDelay = ARROW_ANIMATION_TIME / (arrows.length + 1); | ||||
|         let maxOpacity = 255 * ARROW_ANIMATION_PEAK_OPACITY; | ||||
|         for (let i = 0; i < arrows.length; i++) { | ||||
|             arrows.opacity = 0; | ||||
|             Tweener.addTween(arrows[i], | ||||
|                              { opacity: 0, | ||||
|                                delay: unitaryDelay * (N_ARROWS - (i + 1)), | ||||
|                                time: ARROW_ANIMATION_TIME, | ||||
|                                transition: function(t, b, c, d) { | ||||
|                                  if (t < d/2) | ||||
|                                      return TweenerEquations.easeOutQuad(t, 0, maxOpacity, d/2); | ||||
|                                  else | ||||
|                                      return TweenerEquations.easeInQuad(t - d/2, maxOpacity, -maxOpacity, d/2); | ||||
|                                } | ||||
|                              }); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _onDragBegin: function() { | ||||
|         Tweener.removeTweens(this._lockScreenGroup); | ||||
|         this._lockScreenState = MessageTray.State.HIDING; | ||||
|         this._ensureUnlockDialog(false); | ||||
|     }, | ||||
|  | ||||
|     _onDragEnd: function(action, actor, eventX, eventY, modifiers) { | ||||
|         if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) { | ||||
|             // Complete motion automatically | ||||
|             this._hideLockScreen(true); | ||||
|         } else { | ||||
|             // restore the lock screen to its original place | ||||
|             // try to use the same speed as the normal animation | ||||
|             let h = global.stage.height; | ||||
|             let time = CURTAIN_SLIDE_TIME * (-this._lockScreenGroup.y) / h; | ||||
|             Tweener.removeTweens(this._lockScreenGroup); | ||||
|             Tweener.addTween(this._lockScreenGroup, | ||||
|                              { y: 0, | ||||
|                                time: time, | ||||
|                                transition: 'linear', | ||||
|                                onComplete: function() { | ||||
|                                    this._lockScreenGroup.fixed_position_set = false; | ||||
|                                    this._lockScreenState = MessageTray.State.SHOWN; | ||||
|                                }, | ||||
|                                onCompleteScope: this, | ||||
|                              }); | ||||
|  | ||||
|             // If we have a unlock dialog, cancel it | ||||
|             if (this._dialog) { | ||||
|                 this._dialog.cancel(); | ||||
|                 if (!this._isGreeter) { | ||||
|                     this._dialog = null; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         this._lightbox = new Lightbox.Lightbox(this._group, | ||||
|                                                { inhibitEvents: true, fadeInTime: 10, fadeFactor: 1 }); | ||||
|         this._background = Meta.BackgroundActor.new_for_screen(global.screen); | ||||
|         this._background.hide(); | ||||
|         Main.uiGroup.add_actor(this._background); | ||||
|     }, | ||||
|  | ||||
|     _onStatusChanged: function(status) { | ||||
|         log ("in _onStatusChanged"); | ||||
|         if (status == GnomeSession.PresenceStatus.IDLE) { | ||||
|             log("session gone idle"); | ||||
|  | ||||
|             if (this._dialog) { | ||||
|                 log('canceling existing dialog'); | ||||
|                 this._dialog.cancel(); | ||||
|                 if (!this._isGreeter) { | ||||
|                     this._dialog = null; | ||||
|                 } | ||||
|                 this._dialog = null; | ||||
|             } | ||||
|  | ||||
|             this._group.reactive = true; | ||||
|             if (!this._isModal) { | ||||
|                 Main.pushModal(this.actor); | ||||
|                 Main.pushModal(this._group); | ||||
|                 this._isModal = true; | ||||
|             } | ||||
|  | ||||
|             if (!this._isActive) | ||||
|                 this._lightbox.show(); | ||||
|             this._group.raise_top(); | ||||
|             this._lightbox.show(); | ||||
|         } else { | ||||
|             log('status is now ' + status); | ||||
|  | ||||
|             let lightboxWasShown = this._lightbox.shown; | ||||
|             log("this._lightbox.shown " + this._lightbox.shown); | ||||
|             this._lightbox.hide(); | ||||
|  | ||||
|             let shouldLock = lightboxWasShown && this._settings.get_boolean(LOCK_ENABLED_KEY); | ||||
|             if (shouldLock || this._isActive) { | ||||
|                 this.lock(false); | ||||
|             if (shouldLock || this._isLocked) { | ||||
|                 this._isLocked = true; | ||||
|                 this._background.show(); | ||||
|                 this._background.raise_top(); | ||||
|  | ||||
|                 this._showUnlockDialog(); | ||||
|             } else if (this._isModal) { | ||||
|                 this.unlock(); | ||||
|                 this._popModal(); | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     showDialog: function() { | ||||
|         // Ensure that the stage window is mapped, before taking a grab | ||||
|         // otherwise X errors out | ||||
|         Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { | ||||
|             if (!this._isModal) { | ||||
|                 Main.pushModal(this.actor); | ||||
|                 this._isModal = true; | ||||
|             } | ||||
|     _popModal: function() { | ||||
|         this._group.reactive = false; | ||||
|         Main.popModal(this._group); | ||||
|  | ||||
|             return false; | ||||
|         })); | ||||
|  | ||||
|         this.actor.show(); | ||||
|         this._isGreeter = Main.sessionMode.isGreeter; | ||||
|         this._ensureUnlockDialog(true); | ||||
|         this._hideLockScreen(false); | ||||
|         this._background.hide(); | ||||
|     }, | ||||
|  | ||||
|     _bumpLockScreen: function() { | ||||
|         Tweener.removeTweens(this._lockScreenGroup); | ||||
|         Tweener.addTween(this._lockScreenGroup, | ||||
|                          { y: -BUMP_SIZE, | ||||
|                            time: BUMP_TIME / 2, | ||||
|                            transition: 'easeOutQuad', | ||||
|                            onComplete: function() { | ||||
|                                Tweener.addTween(this, | ||||
|                                                 { y: 0, | ||||
|                                                   time: BUMP_TIME / 2, | ||||
|                                                   transition: 'easeInQuad' }); | ||||
|                            } | ||||
|                          }); | ||||
|     }, | ||||
|  | ||||
|     _hideLockScreen: function(animate) { | ||||
|         this._lockScreenState = MessageTray.State.HIDING; | ||||
|  | ||||
|         if (animate) { | ||||
|             // Tween the lock screen out of screen | ||||
|             // try to use the same speed regardless of original position | ||||
|             let h = global.stage.height; | ||||
|             let time = CURTAIN_SLIDE_TIME * (h + this._lockScreenGroup.y) / h; | ||||
|             Tweener.removeTweens(this._lockScreenGroup); | ||||
|             Tweener.addTween(this._lockScreenGroup, | ||||
|                              { y: -h, | ||||
|                                time: time, | ||||
|                                transition: 'linear', | ||||
|                                onComplete: function() { | ||||
|                                    this._lockScreenState = MessageTray.State.HIDDEN; | ||||
|                                    this._lockScreenGroup.hide(); | ||||
|                                }, | ||||
|                                onCompleteScope: this, | ||||
|                              }); | ||||
|         } else { | ||||
|             this._lockScreenState = MessageTray.State.HIDDEN; | ||||
|             this._lockScreenGroup.hide(); | ||||
|     _showUnlockDialog: function() { | ||||
|         if (this._dialog) { | ||||
|             log ('_showUnlockDialog called again when the dialog was already there'); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (Main.sessionMode.currentMode == 'lock-screen') | ||||
|             Main.sessionMode.popMode('lock-screen'); | ||||
|     }, | ||||
|  | ||||
|     _ensureUnlockDialog: function(onPrimary) { | ||||
|         if (!this._dialog) { | ||||
|             let constructor = Main.sessionMode.unlockDialog; | ||||
|             this._dialog = new constructor(this._lockDialogGroup); | ||||
|             if (!this._dialog) { | ||||
|                 // This session mode has no locking capabilities | ||||
|                 this.unlock(); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             let time = global.get_current_time(); | ||||
|             this._dialog.connect('loaded', Lang.bind(this, function() { | ||||
|                 if (!this._dialog.open(time, onPrimary)) { | ||||
|                     log('Could not open login dialog: failed to acquire grab'); | ||||
|                     this.unlock(); | ||||
|                 } | ||||
|             })); | ||||
|  | ||||
|         try { | ||||
|             this._dialog = new UnlockDialog.UnlockDialog(); | ||||
|             this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed)); | ||||
|             this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded)); | ||||
|  | ||||
|             if (!this._dialog.open(global.get_current_time())) | ||||
|                 throw new Error('open failed!') | ||||
|  | ||||
|             this._dialog._group.raise_top(); | ||||
|         } catch(e) { | ||||
|             // FIXME: this is for debugging purposes | ||||
|             logError(e, 'error while creating unlock dialog'); | ||||
|  | ||||
|             this._popModal(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onUnlockFailed: function() { | ||||
|         this._resetLockScreen(true, false); | ||||
|         // FIXME: for now, on failure we just destroy the dialog and create a new | ||||
|         // one (this is what gnome-screensaver does) | ||||
|         // in the future, we may want to go back to the lock screen instead | ||||
|  | ||||
|         this._dialog.destroy(); | ||||
|         this._dialog = null; | ||||
|  | ||||
|         this._showUnlockDialog(); | ||||
|     }, | ||||
|  | ||||
|     _onUnlockSucceded: function() { | ||||
|         this._tweenUnlocked(); | ||||
|     }, | ||||
|         this._dialog.destroy(); | ||||
|         this._dialog = null; | ||||
|  | ||||
|     _resetLockScreen: function(animateLockScreen, animateLockDialog) { | ||||
|         this._ensureLockScreen(); | ||||
|         this._lockDialogGroup.scale_x = 1; | ||||
|         this._lockDialogGroup.scale_y = 1; | ||||
|  | ||||
|         this._lockScreenGroup.show(); | ||||
|         this._lockScreenState = MessageTray.State.SHOWING; | ||||
|  | ||||
|         if (animateLockScreen) { | ||||
|             this._lockScreenGroup.y = -global.screen_height; | ||||
|             Tweener.removeTweens(this._lockScreenGroup); | ||||
|             Tweener.addTween(this._lockScreenGroup, | ||||
|                              { y: 0, | ||||
|                                time: SHORT_FADE_TIME, | ||||
|                                transition: 'linear', | ||||
|                                onComplete: function() { | ||||
|                                    this._lockScreenShown(); | ||||
|                                }, | ||||
|                                onCompleteScope: this | ||||
|                              }); | ||||
|         } else { | ||||
|             this._lockScreenGroup.fixed_position_set = false; | ||||
|             this._lockScreenShown(); | ||||
|         } | ||||
|  | ||||
|         if (animateLockDialog) { | ||||
|             this._lockDialogGroup.opacity = 0; | ||||
|             Tweener.removeTweens(this._lockDialogGroup); | ||||
|             Tweener.addTween(this._lockDialogGroup, | ||||
|                              { opacity: 255, | ||||
|                                time: SHORT_FADE_TIME, | ||||
|                                transition: 'easeOutQuad' }); | ||||
|         } else { | ||||
|             this._lockDialogGroup.opacity = 255; | ||||
|         } | ||||
|  | ||||
|         this._lockScreenGroup.grab_key_focus(); | ||||
|  | ||||
|         if (Main.sessionMode.currentMode != 'lock-screen') | ||||
|             Main.sessionMode.pushMode('lock-screen'); | ||||
|     }, | ||||
|  | ||||
|     _lockScreenShown: function() { | ||||
|         if (this._dialog && !this._isGreeter) { | ||||
|             this._dialog.destroy(); | ||||
|             this._dialog = null; | ||||
|         } | ||||
|  | ||||
|         if (this._arrowAnimationId) | ||||
|             Mainloop.source_remove(this._arrowAnimationId); | ||||
|         this._arrowAnimationId = Mainloop.timeout_add(6000, Lang.bind(this, this._animateArrows)); | ||||
|         this._animateArrows(); | ||||
|  | ||||
|         this._lockScreenState = MessageTray.State.SHOWN; | ||||
|         this._lockScreenGroup.fixed_position_set = false; | ||||
|         this._lockScreenScrollCounter = 0; | ||||
|  | ||||
|         this.emit('lock-screen-shown'); | ||||
|     }, | ||||
|  | ||||
|     // Some of the actors in the lock screen are heavy in | ||||
|     // resources, so we only create them when needed | ||||
|     _ensureLockScreen: function() { | ||||
|         if (this._hasLockScreen) | ||||
|             return; | ||||
|  | ||||
|         this._lockScreenContentsBox = new St.BoxLayout({ x_align: Clutter.ActorAlign.CENTER, | ||||
|                                                          y_align: Clutter.ActorAlign.CENTER, | ||||
|                                                          x_expand: true, | ||||
|                                                          y_expand: true, | ||||
|                                                          vertical: true, | ||||
|                                                          style_class: 'screen-shield-contents-box' }); | ||||
|         this._clock = new Clock(); | ||||
|         this._lockScreenContentsBox.add(this._clock.actor, { x_fill: true, | ||||
|                                                              y_fill: true }); | ||||
|  | ||||
|         this._lockScreenContents.add_actor(this._lockScreenContentsBox); | ||||
|  | ||||
|         if (this._settings.get_boolean('show-notifications')) { | ||||
|             this._notificationsBox = new NotificationsBox(); | ||||
|             this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true, | ||||
|                                                                             y_fill: true, | ||||
|                                                                             expand: true }); | ||||
|         } | ||||
|  | ||||
|         this._hasLockScreen = true; | ||||
|     }, | ||||
|  | ||||
|     _clearLockScreen: function() { | ||||
|         this._clock.destroy(); | ||||
|         this._clock = null; | ||||
|  | ||||
|         if (this._notificationsBox) { | ||||
|             this._notificationsBox.destroy(); | ||||
|             this._notificationsBox = null; | ||||
|         } | ||||
|  | ||||
|         this._lockScreenContentsBox.destroy(); | ||||
|  | ||||
|         if (this._arrowAnimationId) { | ||||
|             Mainloop.source_remove(this._arrowAnimationId); | ||||
|             this._arrowAnimationId = 0; | ||||
|         } | ||||
|  | ||||
|         this._hasLockScreen = false; | ||||
|     }, | ||||
|  | ||||
|     get locked() { | ||||
|         return this._isActive; | ||||
|     }, | ||||
|  | ||||
|     _tweenUnlocked: function() { | ||||
|         this._inUnlockAnimation = true; | ||||
|         this.unlock(); | ||||
|         Tweener.addTween(this._lockDialogGroup, { | ||||
|             scale_x: 0, | ||||
|             scale_y: 0, | ||||
|             time: Overview.ANIMATION_TIME, | ||||
|             transition: 'easeOutQuad', | ||||
|             onComplete: function() { | ||||
|                 if (this._dialog) { | ||||
|                     this._dialog.destroy(); | ||||
|                     this._dialog = null; | ||||
|                 } | ||||
|                 this.actor.hide(); | ||||
|                 this._inUnlockAnimation = false; | ||||
|             }, | ||||
|             onCompleteScope: this | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|     unlock: function() { | ||||
|         if (this._hasLockScreen) | ||||
|             this._clearLockScreen(); | ||||
|  | ||||
|         if (this._dialog && !this._isGreeter) { | ||||
|             this._dialog.destroy(); | ||||
|             this._dialog = null; | ||||
|         } | ||||
|  | ||||
|         this._lightbox.hide(); | ||||
|  | ||||
|         if (this._isModal) { | ||||
|             Main.popModal(this.actor); | ||||
|             this._isModal = false; | ||||
|         } | ||||
|  | ||||
|         if (!this._inUnlockAnimation) | ||||
|             this.actor.hide(); | ||||
|  | ||||
|         if (Main.sessionMode.currentMode == 'lock-screen') | ||||
|             Main.sessionMode.popMode('lock-screen'); | ||||
|         if (Main.sessionMode.currentMode == 'unlock-dialog') | ||||
|             Main.sessionMode.popMode('unlock-dialog'); | ||||
|  | ||||
|         this._isActive = false; | ||||
|         this.emit('lock-status-changed'); | ||||
|     }, | ||||
|  | ||||
|     lock: function(animate) { | ||||
|         if (!this._isModal) { | ||||
|             Main.pushModal(this.actor); | ||||
|             this._isModal = true; | ||||
|         } | ||||
|  | ||||
|         this.actor.show(); | ||||
|  | ||||
|         if (Main.sessionMode.currentMode != 'unlock-dialog' && | ||||
|             Main.sessionMode.currentMode != 'lock-screen') { | ||||
|             this._isGreeter = Main.sessionMode.isGreeter; | ||||
|             if (!this._isGreeter) | ||||
|                 Main.sessionMode.pushMode('unlock-dialog'); | ||||
|         } | ||||
|  | ||||
|         this._resetLockScreen(animate, animate); | ||||
|  | ||||
|         this._isActive = true; | ||||
|         this.emit('lock-status-changed'); | ||||
|         this._popModal(); | ||||
|     }, | ||||
| }); | ||||
| Signals.addSignalMethods(ScreenShield.prototype); | ||||
|  | ||||
| /* Fallback code to handle session locking using gnome-screensaver, | ||||
|    in case the required GDM dependency is not there | ||||
| */ | ||||
| const ScreenShieldFallback = new Lang.Class({ | ||||
|     Name: 'ScreenShieldFallback', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._proxy = new Gio.DBusProxy({ g_connection: Gio.DBus.session, | ||||
|                                           g_name: 'org.gnome.ScreenSaver', | ||||
|                                           g_object_path: '/org/gnome/ScreenSaver', | ||||
|                                           g_interface_name: 'org.gnome.ScreenSaver', | ||||
|                                           g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START | | ||||
|                                                     Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES), | ||||
|                                         }); | ||||
|         this._proxy.init(null); | ||||
|  | ||||
|         this._proxy.connect('g-signal', Lang.bind(this, this._onSignal)); | ||||
|         this._proxy.connect('notify::g-name-owner', Lang.bind(this, this._onNameOwnerChanged)); | ||||
|     }, | ||||
|  | ||||
|     _onNameOwnerChanged: function(object, pspec) { | ||||
|         if (this._proxy.g_name_owner) | ||||
|             [this._locked] = this._proxy.call_sync('GetActive', null, | ||||
|                                                    Gio.DBusCallFlags.NONE, -1, null).deep_unpack(); | ||||
|         else | ||||
|             this._locked = false; | ||||
|  | ||||
|         this.emit('lock-status-changed', this._locked); | ||||
|     }, | ||||
|  | ||||
|     _onSignal: function(proxy, senderName, signalName, params) { | ||||
|         if (signalName == 'ActiveChanged') { | ||||
|             [this._locked] = params.deep_unpack(); | ||||
|             this.emit('lock-status-changed', this._locked); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     get locked() { | ||||
|         return this._locked; | ||||
|     }, | ||||
|  | ||||
|     lock: function() { | ||||
|         this._proxy.call('Lock', null, Gio.DBusCallFlags.NONE, -1, null, | ||||
|                          Lang.bind(this, function(proxy, result) { | ||||
|                              proxy.call_finish(result); | ||||
|  | ||||
|                              this.emit('lock-screen-shown'); | ||||
|                          })); | ||||
|     }, | ||||
|  | ||||
|     unlock: function() { | ||||
|         this._proxy.call('SetActive', GLib.Variant.new('(b)', false), | ||||
|                          Gio.DBusCallFlags.NONE, -1, null, null); | ||||
|     }, | ||||
| }); | ||||
| Signals.addSignalMethods(ScreenShieldFallback.prototype); | ||||
|   | ||||
							
								
								
									
										114
									
								
								js/ui/search.js
									
									
									
									
									
								
							
							
						
						| @@ -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. | ||||
| @@ -49,7 +51,7 @@ const SearchResultDisplay = new Lang.Class({ | ||||
|      * Remove all results from this display. | ||||
|      */ | ||||
|     clear: function() { | ||||
|         this.actor.destroy_all_children(); | ||||
|         this.actor.get_children().forEach(function (actor) { actor.destroy(); }); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
| @@ -139,6 +141,19 @@ const SearchProvider = new Lang.Class({ | ||||
|         throw new Error('Not implemented'); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * createResultContainer: | ||||
|      * | ||||
|      * Search providers may optionally override this to render their | ||||
|      * results in a custom fashion.  The default implementation | ||||
|      * will create a vertical list. | ||||
|      * | ||||
|      * Returns: An instance of SearchResultDisplay. | ||||
|      */ | ||||
|     createResultContainerActor: function() { | ||||
|         return null; | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * createResultActor: | ||||
|      * @resultMeta: Object with result metadata | ||||
| @@ -167,6 +182,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', | ||||
|  | ||||
| @@ -246,7 +354,7 @@ const SearchSystem = new Lang.Class({ | ||||
|                     results.push([provider, []]); | ||||
|                     provider.getSubsearchResultSet(previousResults, terms); | ||||
|                 } catch (error) { | ||||
|                     log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message); | ||||
|                     global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message); | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
| @@ -256,7 +364,7 @@ const SearchSystem = new Lang.Class({ | ||||
|                     results.push([provider, []]); | ||||
|                     provider.getInitialResultSet(terms); | ||||
|                 } catch (error) { | ||||
|                     log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message); | ||||
|                     global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -5,7 +5,6 @@ const Lang = imports.lang; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Meta = imports.gi.Meta; | ||||
| const St = imports.gi.St; | ||||
| const Atk = imports.gi.Atk; | ||||
|  | ||||
| const DND = imports.ui.dnd; | ||||
| const IconGrid = imports.ui.iconGrid; | ||||
| @@ -34,13 +33,12 @@ const SearchResult = new Lang.Class({ | ||||
|             content = new St.Bin({ style_class: 'search-result-content', | ||||
|                                    reactive: true, | ||||
|                                    can_focus: true, | ||||
|                                    track_hover: true, | ||||
|                                    accessible_role: Atk.Role.PUSH_BUTTON }); | ||||
|                                    track_hover: true }); | ||||
|             let icon = new IconGrid.BaseIcon(this.metaInfo['name'], | ||||
|                                              { createIcon: this.metaInfo['createIcon'] }); | ||||
|             content.set_child(icon.actor); | ||||
|             this._dragActorSource = icon.icon; | ||||
|             content.label_actor = icon.label; | ||||
|             this.actor.label_actor = icon.label; | ||||
|         } else { | ||||
|             if (content._delegate && content._delegate.getDragActorSource) | ||||
|                 this._dragActorSource = content._delegate.getDragActorSource(); | ||||
| @@ -131,7 +129,7 @@ const GridSearchResults = new Lang.Class({ | ||||
|  | ||||
|     getResultsForDisplay: function() { | ||||
|         let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount(); | ||||
|         let canDisplay = this._grid.childrenInRow(this._width) * this._grid.getRowLimit() | ||||
|         let canDisplay = this._grid.childrenInRow(this._width) * MAX_SEARCH_RESULTS_ROWS | ||||
|                          - alreadyVisible; | ||||
|  | ||||
|         let numResults = Math.min(this._notDisplayedResult.length, canDisplay); | ||||
| @@ -173,9 +171,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 +189,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,32 +204,79 @@ 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, | ||||
|                                             y_fill: true }); | ||||
|         providerBox.add(resultDisplayBin, { expand: true }); | ||||
|         let resultDisplay = new GridSearchResults(provider); | ||||
|         let resultDisplay = provider.createResultContainerActor(); | ||||
|         if (resultDisplay == null) { | ||||
|             resultDisplay = new GridSearchResults(provider); | ||||
|         } | ||||
|         resultDisplayBin.set_child(resultDisplay.actor); | ||||
|  | ||||
|         this._providerMeta.push({ provider: provider, | ||||
| @@ -266,18 +312,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 +348,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 +371,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,160 +0,0 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Lang = imports.lang; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const Params = imports.misc.params; | ||||
|  | ||||
| const DEFAULT_MODE = 'restrictive'; | ||||
|  | ||||
| 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': { | ||||
|         allowKeybindingsWhenModal: true, | ||||
|         hasNotifications: true, | ||||
|         isGreeter: true, | ||||
|         isPrimary: true, | ||||
|         unlockDialog: imports.gdm.loginDialog.LoginDialog, | ||||
|         components: ['polkitAgent'], | ||||
|         panel: { | ||||
|             left: [], | ||||
|             center: ['dateMenu'], | ||||
|             right: ['a11y', 'display', 'keyboard', | ||||
|                     'volume', 'battery', 'powerMenu'] | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     'lock-screen': { | ||||
|         isLocked: true, | ||||
|         isGreeter: undefined, | ||||
|         unlockDialog: undefined, | ||||
|         components: ['polkitAgent', 'telepathyClient'], | ||||
|         panel: { | ||||
|             left: ['userMenu'], | ||||
|             center: [], | ||||
|             right: ['lockScreen'] | ||||
|         }, | ||||
|     }, | ||||
|  | ||||
|     'unlock-dialog': { | ||||
|         isLocked: true, | ||||
|         unlockDialog: undefined, | ||||
|         components: ['polkitAgent', 'telepathyClient'], | ||||
|         panel: { | ||||
|             left: ['userMenu'], | ||||
|             center: [], | ||||
|             right: ['a11y', 'keyboard', 'lockScreen'] | ||||
|         }, | ||||
|     }, | ||||
|  | ||||
|     'initial-setup': { | ||||
|         isPrimary: true, | ||||
|         components: ['keyring'], | ||||
|         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'], | ||||
|         panel: { | ||||
|             left: ['activities', 'appMenu'], | ||||
|             center: ['dateMenu'], | ||||
|             right: ['a11y', 'keyboard', 'volume', 'bluetooth', | ||||
|                     'network', 'battery', 'userMenu'] | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|  | ||||
| function listModes() { | ||||
|     let modes = Object.getOwnPropertyNames(_modes); | ||||
|     for (let i = 0; i < modes.length; i++) | ||||
|         if (_modes[modes[i]].isPrimary) | ||||
|             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(); | ||||
|     }, | ||||
|  | ||||
|     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.emit('updated'); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(SessionMode.prototype); | ||||
| @@ -7,7 +7,6 @@ const Shell = imports.gi.Shell; | ||||
|  | ||||
| const Config = imports.misc.config; | ||||
| const ExtensionSystem = imports.ui.extensionSystem; | ||||
| const ExtensionDownloader = imports.ui.extensionDownloader; | ||||
| const ExtensionUtils = imports.misc.extensionUtils; | ||||
| const Flashspot = imports.ui.flashspot; | ||||
| const Main = imports.ui.main; | ||||
| @@ -18,6 +17,17 @@ const GnomeShellIface = <interface name="org.gnome.Shell"> | ||||
|     <arg type="b" direction="out" name="success" /> | ||||
|     <arg type="s" direction="out" name="result" /> | ||||
| </method> | ||||
| <method name="ListExtensions"> | ||||
|     <arg type="a{sa{sv}}" direction="out" name="extensions" /> | ||||
| </method> | ||||
| <method name="GetExtensionInfo"> | ||||
|     <arg type="s" direction="in" name="extension" /> | ||||
|     <arg type="a{sv}" direction="out" name="info" /> | ||||
| </method> | ||||
| <method name="GetExtensionErrors"> | ||||
|     <arg type="s" direction="in" name="extension" /> | ||||
|     <arg type="as" direction="out" name="errors" /> | ||||
| </method> | ||||
| <method name="ScreenshotArea"> | ||||
|     <arg type="i" direction="in" name="x"/> | ||||
|     <arg type="i" direction="in" name="y"/> | ||||
| @@ -46,21 +56,30 @@ const GnomeShellIface = <interface name="org.gnome.Shell"> | ||||
|     <arg type="i" direction="in" name="width"/> | ||||
|     <arg type="i" direction="in" name="height"/> | ||||
| </method> | ||||
| <method name="EnableExtension"> | ||||
|     <arg type="s" direction="in" name="uuid"/> | ||||
| </method> | ||||
| <method name="DisableExtension"> | ||||
|     <arg type="s" direction="in" name="uuid"/> | ||||
| </method> | ||||
| <method name="InstallRemoteExtension"> | ||||
|     <arg type="s" direction="in" name="uuid"/> | ||||
|     <arg type="s" direction="in" name="version"/> | ||||
| </method> | ||||
| <method name="UninstallExtension"> | ||||
|     <arg type="s" direction="in" name="uuid"/> | ||||
|     <arg type="b" direction="out" name="success"/> | ||||
| </method> | ||||
| <method name="LaunchExtensionPrefs"> | ||||
|     <arg type="s" direction="in" name="uuid"/> | ||||
| </method> | ||||
| <property name="OverviewActive" type="b" access="readwrite" /> | ||||
| <property name="ApiVersion" type="i" access="read" /> | ||||
| <property name="ShellVersion" type="s" access="read" /> | ||||
| </interface>; | ||||
|  | ||||
| const ScreenSaverIface = <interface name="org.gnome.ScreenSaver"> | ||||
| <method name="Lock"> | ||||
| </method> | ||||
| <method name="GetActive"> | ||||
|     <arg name="active" direction="out" type="b" /> | ||||
| </method> | ||||
| <method name="SetActive"> | ||||
|     <arg name="value" direction="in" type="b" /> | ||||
| </method> | ||||
| <signal name="ActiveChanged"> | ||||
|     <arg name="new_value" type="b" /> | ||||
| <signal name="ExtensionStatusChanged"> | ||||
|     <arg type="s" name="uuid"/> | ||||
|     <arg type="i" name="state"/> | ||||
|     <arg type="s" name="error"/> | ||||
| </signal> | ||||
| </interface>; | ||||
|  | ||||
| @@ -70,8 +89,8 @@ const GnomeShell = new Lang.Class({ | ||||
|     _init: function() { | ||||
|         this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellIface, this); | ||||
|         this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell'); | ||||
|  | ||||
|         this._extensionsSerivce = new GnomeShellExtensions(); | ||||
|         ExtensionSystem.connect('extension-state-changed', | ||||
|                                 Lang.bind(this, this._extensionStateChanged)); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
| @@ -183,67 +202,6 @@ const GnomeShell = new Lang.Class({ | ||||
|         flashspot.fire(); | ||||
|     }, | ||||
|  | ||||
|     get OverviewActive() { | ||||
|         return Main.overview.visible; | ||||
|     }, | ||||
|  | ||||
|     set OverviewActive(visible) { | ||||
|         if (visible) | ||||
|             Main.overview.show(); | ||||
|         else | ||||
|             Main.overview.hide(); | ||||
|     }, | ||||
|  | ||||
|     ShellVersion: Config.PACKAGE_VERSION | ||||
| }); | ||||
|  | ||||
| const GnomeShellExtensionsIface = <interface name="org.gnome.Shell.Extensions"> | ||||
| <method name="ListExtensions"> | ||||
|     <arg type="a{sa{sv}}" direction="out" name="extensions" /> | ||||
| </method> | ||||
| <method name="GetExtensionInfo"> | ||||
|     <arg type="s" direction="in" name="extension" /> | ||||
|     <arg type="a{sv}" direction="out" name="info" /> | ||||
| </method> | ||||
| <method name="GetExtensionErrors"> | ||||
|     <arg type="s" direction="in" name="extension" /> | ||||
|     <arg type="as" direction="out" name="errors" /> | ||||
| </method> | ||||
| <signal name="ExtensionStatusChanged"> | ||||
|     <arg type="s" name="uuid"/> | ||||
|     <arg type="i" name="state"/> | ||||
|     <arg type="s" name="error"/> | ||||
| </signal> | ||||
| <method name="InstallRemoteExtension"> | ||||
|     <arg type="s" direction="in" name="uuid"/> | ||||
|     <arg type="s" direction="out" name="result"/> | ||||
| </method> | ||||
| <method name="UninstallExtension"> | ||||
|     <arg type="s" direction="in" name="uuid"/> | ||||
|     <arg type="b" direction="out" name="success"/> | ||||
| </method> | ||||
| <method name="LaunchExtensionPrefs"> | ||||
|     <arg type="s" direction="in" name="uuid"/> | ||||
| </method> | ||||
| <method name="ReloadExtension"> | ||||
|     <arg type="s" direction="in" name="uuid"/> | ||||
| </method> | ||||
| <method name="CheckForUpdates"> | ||||
| </method> | ||||
| <property name="ShellVersion" type="s" access="read" /> | ||||
| </interface>; | ||||
|  | ||||
| const GnomeShellExtensions = new Lang.Class({ | ||||
|     Name: 'GnomeShellExtensionsDBus', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellExtensionsIface, this); | ||||
|         this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell'); | ||||
|         ExtensionSystem.connect('extension-state-changed', | ||||
|                                 Lang.bind(this, this._extensionStateChanged)); | ||||
|     }, | ||||
|  | ||||
|  | ||||
|     ListExtensions: function() { | ||||
|         let out = {}; | ||||
|         for (let uuid in ExtensionUtils.extensions) { | ||||
| @@ -302,12 +260,26 @@ const GnomeShellExtensions = new Lang.Class({ | ||||
|         return extension.errors; | ||||
|     }, | ||||
|  | ||||
|     InstallRemoteExtensionAsync: function([uuid], invocation) { | ||||
|         return ExtensionDownloader.installExtension(uuid, invocation); | ||||
|     EnableExtension: function(uuid) { | ||||
|         let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY); | ||||
|         if (enabledExtensions.indexOf(uuid) == -1) | ||||
|             enabledExtensions.push(uuid); | ||||
|         global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions); | ||||
|     }, | ||||
|  | ||||
|     DisableExtension: function(uuid) { | ||||
|         let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY); | ||||
|         while (enabledExtensions.indexOf(uuid) != -1) | ||||
|             enabledExtensions.splice(enabledExtensions.indexOf(uuid), 1); | ||||
|         global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions); | ||||
|     }, | ||||
|  | ||||
|     InstallRemoteExtension: function(uuid, version_tag) { | ||||
|         ExtensionSystem.installExtensionFromUUID(uuid, version_tag); | ||||
|     }, | ||||
|  | ||||
|     UninstallExtension: function(uuid) { | ||||
|         return ExtensionDownloader.uninstallExtension(uuid); | ||||
|         return ExtensionSystem.uninstallExtensionFromUUID(uuid); | ||||
|     }, | ||||
|  | ||||
|     LaunchExtensionPrefs: function(uuid) { | ||||
| @@ -317,18 +289,19 @@ const GnomeShellExtensions = new Lang.Class({ | ||||
|                    ['extension:///' + uuid], -1, null); | ||||
|     }, | ||||
|  | ||||
|     ReloadExtension: function(uuid) { | ||||
|         let extension = ExtensionUtils.extensions[uuid]; | ||||
|         if (!extension) | ||||
|             return; | ||||
|  | ||||
|         ExtensionSystem.reloadExtension(extension); | ||||
|     get OverviewActive() { | ||||
|         return Main.overview.visible; | ||||
|     }, | ||||
|  | ||||
|     CheckForUpdates: function() { | ||||
|         ExtensionDownloader.checkForUpdates(); | ||||
|     set OverviewActive(visible) { | ||||
|         if (visible) | ||||
|             Main.overview.show(); | ||||
|         else | ||||
|             Main.overview.hide(); | ||||
|     }, | ||||
|  | ||||
|     ApiVersion: ExtensionSystem.API_VERSION, | ||||
|  | ||||
|     ShellVersion: Config.PACKAGE_VERSION, | ||||
|  | ||||
|     _extensionStateChanged: function(_, newState) { | ||||
| @@ -336,42 +309,3 @@ const GnomeShellExtensions = new Lang.Class({ | ||||
|                                    GLib.Variant.new('(sis)', [newState.uuid, newState.state, newState.error])); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const ScreenSaverDBus = new Lang.Class({ | ||||
|     Name: 'ScreenSaverDBus', | ||||
|  | ||||
|     _init: function(screenShield) { | ||||
|         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])); | ||||
|         })); | ||||
|  | ||||
|         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); | ||||
|     }, | ||||
|  | ||||
|     SetActive: function(active) { | ||||
|         if (active) | ||||
|             this._screenShield.lock(true); | ||||
|         else | ||||
|             this._screenShield.unlock(); | ||||
|     }, | ||||
|  | ||||
|     GetActive: function() { | ||||
|         return this._screenShield.locked; | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -7,7 +7,7 @@ const Main = imports.ui.main; | ||||
| const Params = imports.misc.params; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
|  | ||||
| const EntryMenu = new Lang.Class({ | ||||
| const _EntryMenu = new Lang.Class({ | ||||
|     Name: 'ShellEntryMenu', | ||||
|     Extends: PopupMenu.PopupMenu, | ||||
|  | ||||
| @@ -34,37 +34,18 @@ const EntryMenu = new Lang.Class({ | ||||
|         this._pasteItem = item; | ||||
|  | ||||
|         this._passwordItem = null; | ||||
|         if (params.isPassword) | ||||
| 	    this._makePasswordItem(); | ||||
|         if (params.isPassword) { | ||||
|             item = new PopupMenu.PopupMenuItem(''); | ||||
|             item.connect('activate', Lang.bind(this, | ||||
|                                                this._onPasswordActivated)); | ||||
|             this.addMenuItem(item); | ||||
|             this._passwordItem = item; | ||||
|         } | ||||
|  | ||||
|         Main.uiGroup.add_actor(this.actor); | ||||
|         this.actor.hide(); | ||||
|     }, | ||||
|  | ||||
|     _makePasswordItem: function() { | ||||
|         let item = new PopupMenu.PopupMenuItem(''); | ||||
|         item.connect('activate', Lang.bind(this, | ||||
|                                            this._onPasswordActivated)); | ||||
|         this.addMenuItem(item); | ||||
|         this._passwordItem = item; | ||||
|     }, | ||||
|  | ||||
|     get isPassword() { | ||||
| 	return this._passwordItem != null; | ||||
|     }, | ||||
|  | ||||
|     set isPassword(v) { | ||||
| 	if (v == this.isPassword) | ||||
| 	    return; | ||||
|  | ||||
| 	if (v) | ||||
| 	    this._makePasswordItem(); | ||||
| 	else { | ||||
| 	    this._passwordItem.destroy(); | ||||
| 	    this._passwordItem = null; | ||||
| 	} | ||||
|     }, | ||||
|  | ||||
|     open: function() { | ||||
|         this._updatePasteItem(); | ||||
|         this._updateCopyItem(); | ||||
| @@ -76,12 +57,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() { | ||||
| @@ -129,42 +104,65 @@ const EntryMenu = new Lang.Class({ | ||||
| function _setMenuAlignment(entry, stageX) { | ||||
|     let [success, entryX, entryY] = entry.transform_stage_point(stageX, 0); | ||||
|     if (success) | ||||
|         entry.menu.setSourceAlignment(entryX / entry.width); | ||||
|         entry._menu.setSourceAlignment(entryX / entry.width); | ||||
| }; | ||||
|  | ||||
| function _onButtonPressEvent(actor, event, entry) { | ||||
|     if (entry.menu.isOpen) { | ||||
|         entry.menu.close(); | ||||
|         return true; | ||||
|     } else if (event.get_button() == 3) { | ||||
|         let [stageX, stageY] = event.get_coords(); | ||||
| function _onClicked(action, actor) { | ||||
|     let entry = actor._menu ? actor : actor.get_parent(); | ||||
|  | ||||
|     if (entry._menu.isOpen) { | ||||
|         entry._menu.close(); | ||||
|     } else if (action.get_button() == 3) { | ||||
|         let [stageX, stageY] = action.get_coords(); | ||||
|         _setMenuAlignment(entry, stageX); | ||||
|         entry.menu.open(); | ||||
|         return true; | ||||
|         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 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); | ||||
|     entry.menu.open(); | ||||
|         entry._menu.setSourceAlignment(textX / entry.width); | ||||
|     entry._menu.open(); | ||||
| }; | ||||
|  | ||||
| function addContextMenu(entry, params) { | ||||
|     if (entry.menu) | ||||
|     if (entry._menu) | ||||
|         return; | ||||
|  | ||||
|     entry.menu = new EntryMenu(entry, params); | ||||
|     entry._menu = new _EntryMenu(entry, params); | ||||
|     entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry }); | ||||
|     entry._menuManager.addMenu(entry.menu); | ||||
|     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); | ||||
| } | ||||
|   | ||||
| @@ -1,21 +1,17 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Lang = imports.lang; | ||||
| const Signals = imports.signals; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Pango = imports.gi.Pango; | ||||
| const St = imports.gi.St; | ||||
| const Shell = imports.gi.Shell; | ||||
|  | ||||
| const CheckBox = imports.ui.checkBox; | ||||
| const Main = imports.ui.main; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
| const Params = imports.misc.params; | ||||
| const ShellEntry = imports.ui.shellEntry; | ||||
|  | ||||
| const LIST_ITEM_ICON_SIZE = 48; | ||||
|  | ||||
| @@ -52,11 +48,6 @@ function _setLabelsForMessage(dialog, message) { | ||||
|         _setLabelText(dialog.descriptionLabel, labels[1]); | ||||
| } | ||||
|  | ||||
| function _createIcon(gicon) { | ||||
|     return new St.Icon({ gicon: gicon, | ||||
|                          style_class: 'shell-mount-operation-icon' }) | ||||
| } | ||||
|  | ||||
| /* -------------------------------------------------------- */ | ||||
|  | ||||
| const ListItem = new Lang.Class({ | ||||
| @@ -100,11 +91,11 @@ const ShellMountOperation = new Lang.Class({ | ||||
|     Name: 'ShellMountOperation', | ||||
|  | ||||
|     _init: function(source, params) { | ||||
|         params = Params.parse(params, { existingDialog: null }); | ||||
|         params = Params.parse(params, { reaskPassword: false }); | ||||
|  | ||||
|         this._reaskPassword = params.reaskPassword; | ||||
|  | ||||
|         this._dialog = null; | ||||
|         this._dialogId = 0; | ||||
|         this._existingDialog = params.existingDialog; | ||||
|         this._processesDialog = null; | ||||
|  | ||||
|         this.mountOp = new Shell.MountOperation(); | ||||
| @@ -116,177 +107,99 @@ const ShellMountOperation = new Lang.Class({ | ||||
|         this.mountOp.connect('show-processes-2', | ||||
|                              Lang.bind(this, this._onShowProcesses2)); | ||||
|         this.mountOp.connect('aborted', | ||||
|                              Lang.bind(this, this.close)); | ||||
|         this.mountOp.connect('show-unmount-progress', | ||||
|                              Lang.bind(this, this._onShowUnmountProgress)); | ||||
|                              Lang.bind(this, this._onAborted)); | ||||
|  | ||||
|         this._gicon = source.get_icon(); | ||||
|     }, | ||||
|  | ||||
|     _closeExistingDialog: function() { | ||||
|         if (!this._existingDialog) | ||||
|             return; | ||||
|  | ||||
|         this._existingDialog.close(); | ||||
|         this._existingDialog = null; | ||||
|         this._icon = new St.Icon({ gicon: source.get_icon(), | ||||
|                                    style_class: 'shell-mount-operation-icon' }); | ||||
|     }, | ||||
|  | ||||
|     _onAskQuestion: function(op, message, choices) { | ||||
|         this._closeExistingDialog(); | ||||
|         this._dialog = new ShellMountQuestionDialog(this._gicon); | ||||
|         this._dialog = new ShellMountQuestionDialog(this._icon); | ||||
|  | ||||
|         this._dialogId = this._dialog.connect('response', Lang.bind(this, | ||||
|             function(object, choice) { | ||||
|                 this.mountOp.set_choice(choice); | ||||
|                 this.mountOp.reply(Gio.MountOperationResult.HANDLED); | ||||
|         this._dialog.connect('response', | ||||
|                                Lang.bind(this, function(object, choice) { | ||||
|                                    this.mountOp.set_choice(choice); | ||||
|                                    this.mountOp.reply(Gio.MountOperationResult.HANDLED); | ||||
|  | ||||
|                 this.close(); | ||||
|             })); | ||||
|                                    this._dialog.close(global.get_current_time()); | ||||
|                                    this._dialog = null; | ||||
|                                })); | ||||
|  | ||||
|         this._dialog.update(message, choices); | ||||
|         this._dialog.open(); | ||||
|         this._dialog.open(global.get_current_time()); | ||||
|     }, | ||||
|  | ||||
|     _onAskPassword: function(op, message, defaultUser, defaultDomain, flags) { | ||||
|         if (this._existingDialog) { | ||||
|             this._dialog = this._existingDialog; | ||||
|             this._dialog.reaskPassword(); | ||||
|         } else { | ||||
|             this._dialog = new ShellMountPasswordDialog(message, this._gicon, flags); | ||||
|         } | ||||
|     _onAskPassword: function(op, message) { | ||||
|         this._notificationShowing = true; | ||||
|         this._source = new ShellMountPasswordSource(message, this._icon, this._reaskPassword); | ||||
|  | ||||
|         this._dialogId = this._dialog.connect('response', Lang.bind(this, | ||||
|             function(object, choice, password, remember) { | ||||
|                 if (choice == -1) { | ||||
|                     this.mountOp.reply(Gio.MountOperationResult.ABORTED); | ||||
|                 } else { | ||||
|                     if (remember) | ||||
|                         this.mountOp.set_password_save(Gio.PasswordSave.PERMANENTLY); | ||||
|                     else | ||||
|                         this.mountOp.set_password_save(Gio.PasswordSave.NEVER); | ||||
|         this._source.connect('password-ready', | ||||
|                              Lang.bind(this, function(source, password) { | ||||
|                                  this.mountOp.set_password(password); | ||||
|                                  this.mountOp.reply(Gio.MountOperationResult.HANDLED); | ||||
|  | ||||
|                     this.mountOp.set_password(password); | ||||
|                     this.mountOp.reply(Gio.MountOperationResult.HANDLED); | ||||
|                 } | ||||
|             })); | ||||
|         this._dialog.open(); | ||||
|                                  this._notificationShowing = false; | ||||
|                                  this._source.destroy(); | ||||
|                              })); | ||||
|  | ||||
|         this._source.connect('destroy', | ||||
|                              Lang.bind(this, function() { | ||||
|                                  if (!this._notificationShowing) | ||||
|                                      return; | ||||
|  | ||||
|                                  this._notificationShowing = false; | ||||
|                                  this.mountOp.reply(Gio.MountOperationResult.ABORTED); | ||||
|                              })); | ||||
|     }, | ||||
|  | ||||
|     close: function(op) { | ||||
|         this._closeExistingDialog(); | ||||
|         this._processesDialog = null; | ||||
|     _onAborted: function(op) { | ||||
|         if (!this._dialog) | ||||
|             return; | ||||
|  | ||||
|         if (this._dialog) { | ||||
|             this._dialog.close(); | ||||
|             this._dialog = null; | ||||
|         } | ||||
|  | ||||
|         if (this._notifier) { | ||||
|             this._notifier.done(); | ||||
|             this._notifier = null; | ||||
|         } | ||||
|         this._dialog.close(global.get_current_time()); | ||||
|         this._dialog = null; | ||||
|     }, | ||||
|  | ||||
|     _onShowProcesses2: function(op) { | ||||
|         this._closeExistingDialog(); | ||||
|  | ||||
|         let processes = op.get_show_processes_pids(); | ||||
|         let choices = op.get_show_processes_choices(); | ||||
|         let message = op.get_show_processes_message(); | ||||
|  | ||||
|         if (!this._processesDialog) { | ||||
|             this._processesDialog = new ShellProcessesDialog(this._gicon); | ||||
|             this._processesDialog = new ShellProcessesDialog(this._icon); | ||||
|             this._dialog = this._processesDialog; | ||||
|  | ||||
|             this._dialogId = this._processesDialog.connect('response', Lang.bind(this, | ||||
|                 function(object, choice) { | ||||
|                     if (choice == -1) { | ||||
|                         this.mountOp.reply(Gio.MountOperationResult.ABORTED); | ||||
|                     } else { | ||||
|                         this.mountOp.set_choice(choice); | ||||
|                         this.mountOp.reply(Gio.MountOperationResult.HANDLED); | ||||
|                     } | ||||
|             this._processesDialog.connect('response',  | ||||
|                                           Lang.bind(this, function(object, choice) { | ||||
|                                               if (choice == -1) { | ||||
|                                                   this.mountOp.reply(Gio.MountOperationResult.ABORTED); | ||||
|                                               } else { | ||||
|                                                   this.mountOp.set_choice(choice); | ||||
|                                                   this.mountOp.reply(Gio.MountOperationResult.HANDLED); | ||||
|                                               } | ||||
|  | ||||
|                     this.close(); | ||||
|                 })); | ||||
|             this._processesDialog.open(); | ||||
|                                               this._processesDialog.close(global.get_current_time()); | ||||
|                                               this._dialog = null; | ||||
|                                           })); | ||||
|             this._processesDialog.open(global.get_current_time()); | ||||
|         } | ||||
|  | ||||
|         this._processesDialog.update(message, processes, choices); | ||||
|     }, | ||||
|  | ||||
|     _onShowUnmountProgress: function(op, message, timeLeft, bytesLeft) { | ||||
|         if (!this._notifier) | ||||
|             this._notifier = new ShellUnmountNotifier(); | ||||
|              | ||||
|         if (bytesLeft == 0) | ||||
|             this._notifier.done(message); | ||||
|         else | ||||
|             this._notifier.show(message); | ||||
|     }, | ||||
|  | ||||
|     borrowDialog: function() { | ||||
|         if (this._dialogId != 0) { | ||||
|             this._dialog.disconnect(this._dialogId); | ||||
|             this._dialogId = 0; | ||||
|         } | ||||
|  | ||||
|         return this._dialog; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const ShellUnmountNotifier = new Lang.Class({ | ||||
|     Name: 'ShellUnmountNotifier', | ||||
|     Extends: MessageTray.Source, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent('', 'media-removable'); | ||||
|  | ||||
|         this._notification = null; | ||||
|         Main.messageTray.add(this); | ||||
|     }, | ||||
|  | ||||
|     show: function(message) { | ||||
|         let [header, text] = message.split('\n', 2); | ||||
|  | ||||
|         if (!this._notification) { | ||||
|             this._notification = new MessageTray.Notification(this, header, text); | ||||
|             this._notification.setTransient(true); | ||||
|             this._notification.setUrgency(MessageTray.Urgency.CRITICAL); | ||||
|         } else { | ||||
|             this._notification.update(header, text); | ||||
|         } | ||||
|  | ||||
|         this.notify(this._notification); | ||||
|     }, | ||||
|  | ||||
|     done: function(message) { | ||||
|         if (this._notification) { | ||||
|             this._notification.destroy(); | ||||
|             this._notification = null; | ||||
|         } | ||||
|  | ||||
|         if (message) { | ||||
|             let notification = new MessageTray.Notification(this, message, null); | ||||
|             notification.setTransient(true); | ||||
|  | ||||
|             this.notify(notification); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const ShellMountQuestionDialog = new Lang.Class({ | ||||
|     Name: 'ShellMountQuestionDialog', | ||||
|     Extends: ModalDialog.ModalDialog, | ||||
|  | ||||
|     _init: function(gicon) { | ||||
|     _init: function(icon) { | ||||
|         this.parent({ styleClass: 'mount-question-dialog' }); | ||||
|  | ||||
|         let mainContentLayout = new St.BoxLayout(); | ||||
|         this.contentLayout.add(mainContentLayout, { x_fill: true, | ||||
|                                                     y_fill: false }); | ||||
|  | ||||
|         this._iconBin = new St.Bin({ child: _createIcon(gicon) }); | ||||
|         this._iconBin = new St.Bin({ child: icon }); | ||||
|         mainContentLayout.add(this._iconBin, | ||||
|                               { x_fill:  true, | ||||
|                                 y_fill:  false, | ||||
| @@ -321,108 +234,63 @@ const ShellMountQuestionDialog = new Lang.Class({ | ||||
| }); | ||||
| Signals.addSignalMethods(ShellMountQuestionDialog.prototype); | ||||
|  | ||||
| const ShellMountPasswordDialog = new Lang.Class({ | ||||
|     Name: 'ShellMountPasswordDialog', | ||||
|     Extends: ModalDialog.ModalDialog, | ||||
| const ShellMountPasswordSource = new Lang.Class({ | ||||
|     Name: 'ShellMountPasswordSource', | ||||
|     Extends: MessageTray.Source, | ||||
|  | ||||
|     _init: function(message, gicon, flags) { | ||||
|     _init: function(message, icon, reaskPassword) { | ||||
|         let strings = message.split('\n'); | ||||
|         this.parent({ styleClass: 'prompt-dialog' }); | ||||
|         this.parent(strings[0]); | ||||
|  | ||||
|         let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout', | ||||
|                                                 vertical: false }); | ||||
|         this.contentLayout.add(mainContentBox); | ||||
|         this._notification = new ShellMountPasswordNotification(this, strings, icon, reaskPassword); | ||||
|         this._setSummaryIcon(icon); | ||||
|  | ||||
|         let icon = _createIcon(gicon); | ||||
|         mainContentBox.add(icon, | ||||
|                            { x_fill:  true, | ||||
|                              y_fill:  false, | ||||
|                              x_align: St.Align.END, | ||||
|                              y_align: St.Align.START }); | ||||
|         // add ourselves as a source, and popup the notification | ||||
|         Main.messageTray.add(this); | ||||
|         this.notify(this._notification); | ||||
|     }, | ||||
| }); | ||||
| Signals.addSignalMethods(ShellMountPasswordSource.prototype); | ||||
|  | ||||
|         this._messageBox = new St.BoxLayout({ style_class: 'prompt-dialog-message-layout', | ||||
|                                               vertical: true }); | ||||
|         mainContentBox.add(this._messageBox, | ||||
|                            { y_align: St.Align.START, expand: true, x_fill: true, y_fill: true }); | ||||
| const ShellMountPasswordNotification = new Lang.Class({ | ||||
|     Name: 'ShellMountPasswordNotification', | ||||
|     Extends: MessageTray.Notification, | ||||
|  | ||||
|         let subject = new St.Label({ style_class: 'prompt-dialog-headline' }); | ||||
|         this._messageBox.add(subject, | ||||
|                              { y_fill:  false, | ||||
|                                y_align: St.Align.START }); | ||||
|         if (strings[0]) | ||||
|             subject.set_text(strings[0]); | ||||
|     _init: function(source, strings, icon, reaskPassword) { | ||||
|         this.parent(source, strings[0], null, { customContent: true, icon: icon }); | ||||
|  | ||||
|         // set the notification to transient and urgent, so that it | ||||
|         // expands out | ||||
|         this.setTransient(true); | ||||
|         this.setUrgency(MessageTray.Urgency.CRITICAL); | ||||
|  | ||||
|         let description = new St.Label({ style_class: 'prompt-dialog-description' }); | ||||
|         description.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         description.clutter_text.line_wrap = true; | ||||
|         this._messageBox.add(description, | ||||
|                             { y_fill:  true, | ||||
|                               y_align: St.Align.START }); | ||||
|         if (strings[1]) | ||||
|             description.set_text(strings[1]); | ||||
|             this.addBody(strings[1]); | ||||
|  | ||||
|         this._passwordBox = new St.BoxLayout({ vertical: false, style_class: 'prompt-dialog-password-box' }); | ||||
|         this._messageBox.add(this._passwordBox); | ||||
|         if (reaskPassword) { | ||||
|             let label = new St.Label({ style_class: 'mount-password-reask', | ||||
|                                        text: _("Wrong password, please try again") }); | ||||
|  | ||||
|         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 }); | ||||
|  | ||||
|         this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry', | ||||
|                                              text: "", | ||||
|                                              can_focus: true}); | ||||
|         ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true }); | ||||
|         this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate)); | ||||
|         this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE | ||||
|         this._passwordBox.add(this._passwordEntry, {expand: true }); | ||||
|         this.setInitialKeyFocus(this._passwordEntry); | ||||
|  | ||||
|         this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label', | ||||
|                                                  text: _("Sorry, that didn\'t work. Please try again.") }); | ||||
|         this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         this._errorMessageLabel.clutter_text.line_wrap = true; | ||||
|         this._errorMessageLabel.hide(); | ||||
|         this._messageBox.add(this._errorMessageLabel); | ||||
|  | ||||
|         if (flags & Gio.AskPasswordFlags.SAVING_SUPPORTED) { | ||||
|             this._rememberChoice = new CheckBox.CheckBox(); | ||||
|             this._rememberChoice.getLabelActor().text = _("Remember Password"); | ||||
|             this._rememberChoice.actor.checked = true; | ||||
|             this._messageBox.add(this._rememberChoice.actor); | ||||
|         } else { | ||||
|             this._rememberChoice = null; | ||||
|             this.addActor(label); | ||||
|         } | ||||
|  | ||||
|         let buttons = [{ label: _("Cancel"), | ||||
|                          action: Lang.bind(this, this._onCancelButton), | ||||
|                          key:    Clutter.Escape | ||||
|                        }, | ||||
|                        { label: _("Unlock"), | ||||
|                          action: Lang.bind(this, this._onUnlockButton), | ||||
|                          default: true | ||||
|                        }]; | ||||
|         this._responseEntry = new St.Entry({ style_class: 'mount-password-entry', | ||||
|                                              can_focus: true }); | ||||
|         this.setActionArea(this._responseEntry); | ||||
|  | ||||
|         this.setButtons(buttons); | ||||
|         this._responseEntry.clutter_text.connect('activate', | ||||
|                                                  Lang.bind(this, this._onEntryActivated)); | ||||
|         this._responseEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE | ||||
|  | ||||
|         this._responseEntry.grab_key_focus(); | ||||
|     }, | ||||
|  | ||||
|     reaskPassword: function() { | ||||
|         this._passwordEntry.set_text(''); | ||||
|         this._errorMessageLabel.show(); | ||||
|     }, | ||||
|     _onEntryActivated: function() { | ||||
|         let text = this._responseEntry.get_text(); | ||||
|         if (text == '') | ||||
|             return; | ||||
|  | ||||
|     _onCancelButton: function() { | ||||
|         this.emit('response', -1, '', false); | ||||
|     }, | ||||
|  | ||||
|     _onUnlockButton: function() { | ||||
|         this._onEntryActivate(); | ||||
|     }, | ||||
|  | ||||
|     _onEntryActivate: function() { | ||||
|         this.emit('response', 1, | ||||
|             this._passwordEntry.get_text(), | ||||
|             this._rememberChoice && | ||||
|             this._rememberChoice.actor.checked); | ||||
|         this.source.emit('password-ready', text); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -430,14 +298,14 @@ const ShellProcessesDialog = new Lang.Class({ | ||||
|     Name: 'ShellProcessesDialog', | ||||
|     Extends: ModalDialog.ModalDialog, | ||||
|  | ||||
|     _init: function(gicon) { | ||||
|     _init: function(icon) { | ||||
|         this.parent({ styleClass: 'show-processes-dialog' }); | ||||
|  | ||||
|         let mainContentLayout = new St.BoxLayout(); | ||||
|         this.contentLayout.add(mainContentLayout, { x_fill: true, | ||||
|                                                     y_fill: false }); | ||||
|  | ||||
|         this._iconBin = new St.Bin({ child: _createIcon(gicon) }); | ||||
|         this._iconBin = new St.Bin({ child: icon }); | ||||
|         mainContentLayout.add(this._iconBin, | ||||
|                               { x_fill:  true, | ||||
|                                 y_fill:  false, | ||||
| @@ -471,17 +339,21 @@ const ShellProcessesDialog = new Lang.Class({ | ||||
|         scrollView.hide(); | ||||
|  | ||||
|         this._applicationList = new St.BoxLayout({ vertical: true }); | ||||
|         scrollView.add_actor(this._applicationList); | ||||
|         scrollView.add_actor(this._applicationList, | ||||
|                              { x_fill:  true, | ||||
|                                y_fill:  true, | ||||
|                                x_align: St.Align.START, | ||||
|                                y_align: St.Align.MIDDLE }); | ||||
|  | ||||
|         this._applicationList.connect('actor-added', | ||||
|                                       Lang.bind(this, function() { | ||||
|                                           if (this._applicationList.get_n_children() == 1) | ||||
|                                           if (this._applicationList.get_children().length == 1) | ||||
|                                               scrollView.show(); | ||||
|                                       })); | ||||
|  | ||||
|         this._applicationList.connect('actor-removed', | ||||
|                                       Lang.bind(this, function() { | ||||
|                                           if (this._applicationList.get_n_children() == 0) | ||||
|                                           if (this._applicationList.get_children().length == 0) | ||||
|                                               scrollView.hide(); | ||||
|                                       })); | ||||
|     }, | ||||
| @@ -515,253 +387,3 @@ const ShellProcessesDialog = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(ShellProcessesDialog.prototype); | ||||
|  | ||||
| const GnomeShellMountOpIface = <interface name="org.Gtk.MountOperationHandler"> | ||||
| <method name="AskPassword"> | ||||
|     <arg type="s" direction="in" name="object_id"/> | ||||
|     <arg type="s" direction="in" name="message"/> | ||||
|     <arg type="s" direction="in" name="icon_name"/> | ||||
|     <arg type="s" direction="in" name="default_user"/> | ||||
|     <arg type="s" direction="in" name="default_domain"/> | ||||
|     <arg type="u" direction="in" name="flags"/> | ||||
|     <arg type="u" direction="out" name="response"/> | ||||
|     <arg type="a{sv}" direction="out" name="response_details"/> | ||||
| </method> | ||||
| <method name="AskQuestion"> | ||||
|     <arg type="s" direction="in" name="object_id"/> | ||||
|     <arg type="s" direction="in" name="message"/> | ||||
|     <arg type="s" direction="in" name="icon_name"/> | ||||
|     <arg type="as" direction="in" name="choices"/> | ||||
|     <arg type="u" direction="out" name="response"/> | ||||
|     <arg type="a{sv}" direction="out" name="response_details"/> | ||||
| </method> | ||||
| <method name="ShowProcesses"> | ||||
|     <arg type="s" direction="in" name="object_id"/> | ||||
|     <arg type="s" direction="in" name="message"/> | ||||
|     <arg type="s" direction="in" name="icon_name"/> | ||||
|     <arg type="ai" direction="in" name="application_pids"/> | ||||
|     <arg type="as" direction="in" name="choices"/> | ||||
|     <arg type="u" direction="out" name="response"/> | ||||
|     <arg type="a{sv}" direction="out" name="response_details"/> | ||||
| </method> | ||||
| <method name="Close"/> | ||||
| </interface>; | ||||
|  | ||||
| const ShellMountOperationType = { | ||||
|     NONE: 0, | ||||
|     ASK_PASSWORD: 1, | ||||
|     ASK_QUESTION: 2, | ||||
|     SHOW_PROCESSES: 3 | ||||
| }; | ||||
|  | ||||
| const GnomeShellMountOpHandler = new Lang.Class({ | ||||
|     Name: 'GnomeShellMountOpHandler', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellMountOpIface, this); | ||||
|         this._dbusImpl.export(Gio.DBus.session, '/org/gtk/MountOperationHandler'); | ||||
|         Gio.bus_own_name_on_connection(Gio.DBus.session, 'org.gtk.MountOperationHandler', | ||||
|                                        Gio.BusNameOwnerFlags.REPLACE, null, null); | ||||
|  | ||||
|         this._dialog = null; | ||||
|         this._volumeMonitor = Gio.VolumeMonitor.get(); | ||||
|  | ||||
|         this._ensureEmptyRequest(); | ||||
|     }, | ||||
|  | ||||
|     _ensureEmptyRequest: function() { | ||||
|         this._currentId = null; | ||||
|         this._currentInvocation = null; | ||||
|         this._currentType = ShellMountOperationType.NONE; | ||||
|     }, | ||||
|  | ||||
|     _clearCurrentRequest: function(response, details) { | ||||
|         if (this._currentInvocation) { | ||||
|             this._currentInvocation.return_value( | ||||
|                 GLib.Variant.new('(ua{sv})', [response, details])); | ||||
|         } | ||||
|  | ||||
|         this._ensureEmptyRequest(); | ||||
|     }, | ||||
|  | ||||
|     _setCurrentRequest: function(invocation, id, type) { | ||||
|         let oldId = this._currentId; | ||||
|         let oldType = this._currentType; | ||||
|         let requestId = id + '@' + invocation.get_sender(); | ||||
|  | ||||
|         this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {}); | ||||
|  | ||||
|         this._currentInvocation = invocation; | ||||
|         this._currentId = requestId; | ||||
|         this._currentType = type; | ||||
|  | ||||
|         if (this._dialog && (oldId == requestId) && (oldType == type)) | ||||
|             return true; | ||||
|  | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     _closeDialog: function() { | ||||
|         if (this._dialog) { | ||||
|             this._dialog.close(); | ||||
|             this._dialog = null; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _createGIcon: function(iconName) { | ||||
|         let realIconName = iconName ? iconName : 'drive-harddisk'; | ||||
|         return new Gio.ThemedIcon({ name: realIconName, | ||||
|                                     use_default_fallbacks: true }); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * AskPassword: | ||||
|      * @id: an opaque ID identifying the object for which the operation is requested | ||||
|      *      The ID must be unique in the context of the calling process. | ||||
|      * @message: the message to display | ||||
|      * @icon_name: the name of an icon to display | ||||
|      * @default_user: the default username for display | ||||
|      * @default_domain: the default domain for display | ||||
|      * @flags: a set of GAskPasswordFlags | ||||
|      * @response: a GMountOperationResult | ||||
|      * @response_details: a dictionary containing the response details as | ||||
|      * entered by the user. The dictionary MAY contain the following properties: | ||||
|      *   - "password" -> (s): a password to be used to complete the mount operation | ||||
|      *   - "password_save" -> (u): a GPasswordSave | ||||
|      * | ||||
|      * The dialog will stay visible until clients call the Close() method, or | ||||
|      * another dialog becomes visible. | ||||
|      * Calling AskPassword again for the same id will have the effect to clear | ||||
|      * the existing dialog and update it with a message indicating the previous | ||||
|      * attempt went wrong. | ||||
|      */ | ||||
|     AskPasswordAsync: function(params, invocation) { | ||||
|         let [id, message, iconName, defaultUser, defaultDomain, flags] = params; | ||||
|  | ||||
|         if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_PASSWORD)) { | ||||
|             this._dialog.reaskPassword(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._closeDialog(); | ||||
|  | ||||
|         this._dialog = new ShellMountPasswordDialog(message, this._createGIcon(iconName), flags); | ||||
|         this._dialog.connect('response', Lang.bind(this, | ||||
|             function(object, choice, password, remember) { | ||||
|                 let details = {}; | ||||
|                 let response; | ||||
|  | ||||
|                 if (choice == -1) { | ||||
|                     response = Gio.MountOperationResult.ABORTED; | ||||
|                 } else { | ||||
|                     response = Gio.MountOperationResult.HANDLED; | ||||
|  | ||||
|                     let passSave = remember ? Gio.PasswordSave.PERMANENTLY : Gio.PasswordSave.NEVER; | ||||
|                     details['password_save'] = GLib.Variant.new('u', passSave); | ||||
|                     details['password'] = GLib.Variant.new('s', password); | ||||
|                 } | ||||
|  | ||||
|                 this._clearCurrentRequest(response, details); | ||||
|             })); | ||||
|         this._dialog.open(); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * AskQuestion: | ||||
|      * @id: an opaque ID identifying the object for which the operation is requested | ||||
|      *      The ID must be unique in the context of the calling process. | ||||
|      * @message: the message to display | ||||
|      * @icon_name: the name of an icon to display | ||||
|      * @choices: an array of choice strings | ||||
|      * GetResponse: | ||||
|      * @response: a GMountOperationResult | ||||
|      * @response_details: a dictionary containing the response details as | ||||
|      * entered by the user. The dictionary MAY contain the following properties: | ||||
|      *   - "choice" -> (i): the chosen answer among the array of strings passed in | ||||
|      * | ||||
|      * The dialog will stay visible until clients call the Close() method, or | ||||
|      * another dialog becomes visible. | ||||
|      * Calling AskQuestion again for the same id will have the effect to clear | ||||
|      * update the dialog with the new question. | ||||
|      */ | ||||
|     AskQuestionAsync: function(params, invocation) { | ||||
|         let [id, message, iconName, choices] = params; | ||||
|  | ||||
|         if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_QUESTION)) { | ||||
|             this._dialog.update(message, choices); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._closeDialog(); | ||||
|  | ||||
|         this._dialog = new ShellMountQuestionDialog(this._createGIcon(iconName), message); | ||||
|         this._dialog.connect('response', Lang.bind(this, | ||||
|             function(object, choice) { | ||||
|                 this._clearCurrentRequest(Gio.MountOperationResult.HANDLED, | ||||
|                                           { choice: GLib.Variant.new('i', choice) }); | ||||
|             })); | ||||
|  | ||||
|         this._dialog.update(message, choices); | ||||
|         this._dialog.open(); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * ShowProcesses: | ||||
|      * @id: an opaque ID identifying the object for which the operation is requested | ||||
|      *      The ID must be unique in the context of the calling process. | ||||
|      * @message: the message to display | ||||
|      * @icon_name: the name of an icon to display | ||||
|      * @application_pids: the PIDs of the applications to display | ||||
|      * @choices: an array of choice strings | ||||
|      * @response: a GMountOperationResult | ||||
|      * @response_details: a dictionary containing the response details as | ||||
|      * entered by the user. The dictionary MAY contain the following properties: | ||||
|      *   - "choice" -> (i): the chosen answer among the array of strings passed in | ||||
|      * | ||||
|      * The dialog will stay visible until clients call the Close() method, or | ||||
|      * another dialog becomes visible. | ||||
|      * Calling ShowProcesses again for the same id will have the effect to clear | ||||
|      * the existing dialog and update it with the new message and the new list | ||||
|      * of processes. | ||||
|      */ | ||||
|     ShowProcessesAsync: function(params, invocation) { | ||||
|         let [id, message, iconName, applicationPids, choices] = params; | ||||
|  | ||||
|         if (this._setCurrentRequest(invocation, id, ShellMountOperationType.SHOW_PROCESSES)) { | ||||
|             this._dialog.update(message, applicationPids, choices); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._closeDialog(); | ||||
|  | ||||
|         this._dialog = new ShellProcessesDialog(this._createGIcon(iconName)); | ||||
|         this._dialog.connect('response', Lang.bind(this, | ||||
|             function(object, choice) { | ||||
|                 let response; | ||||
|                 let details = {}; | ||||
|  | ||||
|                 if (choice == -1) { | ||||
|                     response = Gio.MountOperationResult.ABORTED; | ||||
|                 } else { | ||||
|                     response = Gio.MountOperationResult.HANDLED; | ||||
|                     details['choice'] = GLib.Variant.new('i', choice); | ||||
|                 } | ||||
|  | ||||
|                 this._clearCurrentRequest(response, details); | ||||
|             })); | ||||
|  | ||||
|         this._dialog.update(message, applicationPids, choices); | ||||
|         this._dialog.open(); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * Close: | ||||
|      * | ||||
|      * Closes a dialog previously opened by AskPassword, AskQuestion or ShowProcesses. | ||||
|      * If no dialog is open, does nothing. | ||||
|      */ | ||||
|     Close: function(params, invocation) { | ||||
|         this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {}); | ||||
|         this._closeDialog(); | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -1,10 +1,18 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const GDesktopEnums = imports.gi.GDesktopEnums; | ||||
| const Gio = imports.gi.Gio; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Signals = imports.signals; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const PanelMenu = imports.ui.panelMenu; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| const Util = imports.misc.util; | ||||
|  | ||||
| const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard'; | ||||
| const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable'; | ||||
| @@ -36,7 +44,7 @@ const ATIndicator = new Lang.Class({ | ||||
|     Extends: PanelMenu.SystemStatusButton, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent('preferences-desktop-accessibility-symbolic', _("Accessibility")); | ||||
|         this.parent('preferences-desktop-accessibility', _("Accessibility")); | ||||
|  | ||||
|         let highContrast = this._buildHCItem(); | ||||
|         this.menu.addMenuItem(highContrast); | ||||
| @@ -48,9 +56,9 @@ const ATIndicator = new Lang.Class({ | ||||
|         let textZoom = this._buildFontItem(); | ||||
|         this.menu.addMenuItem(textZoom); | ||||
|  | ||||
|         let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA, | ||||
|                                                                'screen-reader-enabled'); | ||||
|         this.menu.addMenuItem(screenReader); | ||||
| //        let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA, | ||||
| //                                                               'screen-reader-enabled'); | ||||
| //        this.menu.addMenuItem(screenReader); | ||||
|  | ||||
|         let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA, | ||||
|                                                                    'screen-keyboard-enabled'); | ||||
|   | ||||
| @@ -1,11 +1,15 @@ | ||||
| // -*- 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 GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet; | ||||
| const GnomeBluetooth = imports.gi.GnomeBluetooth; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const St = imports.gi.St; | ||||
| const Shell = imports.gi.Shell; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| @@ -24,7 +28,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(); | ||||
|  | ||||
| @@ -32,11 +36,11 @@ const Indicator = new Lang.Class({ | ||||
|         this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch)); | ||||
|         this._killswitch.connect('toggled', Lang.bind(this, function() { | ||||
|             let current_state = this._applet.killswitch_state; | ||||
|             if (current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED && | ||||
|                 current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER) { | ||||
|             if (current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED && | ||||
|                 current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER) { | ||||
|                 this._applet.killswitch_state = this._killswitch.state ? | ||||
|                     GnomeBluetooth.KillswitchState.UNBLOCKED: | ||||
|                     GnomeBluetooth.KillswitchState.SOFT_BLOCKED; | ||||
|                     GnomeBluetoothApplet.KillswitchState.UNBLOCKED: | ||||
|                     GnomeBluetoothApplet.KillswitchState.SOFT_BLOCKED; | ||||
|             } else | ||||
|                 this._killswitch.setToggleState(false); | ||||
|         })); | ||||
| @@ -90,10 +94,10 @@ const Indicator = new Lang.Class({ | ||||
|  | ||||
|     _updateKillswitch: function() { | ||||
|         let current_state = this._applet.killswitch_state; | ||||
|         let on = current_state == GnomeBluetooth.KillswitchState.UNBLOCKED; | ||||
|         let has_adapter = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER; | ||||
|         let can_toggle = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER && | ||||
|                          current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED; | ||||
|         let on = current_state == GnomeBluetoothApplet.KillswitchState.UNBLOCKED; | ||||
|         let has_adapter = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER; | ||||
|         let can_toggle = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER && | ||||
|                          current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED; | ||||
|  | ||||
|         this._killswitch.setToggleState(on); | ||||
|         if (can_toggle) | ||||
| @@ -106,10 +110,10 @@ const Indicator = new Lang.Class({ | ||||
|  | ||||
|         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 +305,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 +460,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) { | ||||
|   | ||||
| @@ -1,196 +1,45 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GdkPixbuf = imports.gi.GdkPixbuf; | ||||
| const Gkbd = imports.gi.Gkbd; | ||||
| const Gio = imports.gi.Gio; | ||||
| 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 { | ||||
|     var IBus = imports.gi.IBus; | ||||
|     if (!('new_async' in IBus.Bus)) | ||||
|         throw "IBus version is too old"; | ||||
|     const IBusCandidatePopup = imports.ui.ibusCandidatePopup; | ||||
| } catch (e) { | ||||
|     var IBus = null; | ||||
|     log(e); | ||||
| } | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| const PanelMenu = imports.ui.panelMenu; | ||||
| const Util = imports.misc.util; | ||||
|  | ||||
| const DESKTOP_INPUT_SOURCES_SCHEMA = 'org.gnome.desktop.input-sources'; | ||||
| const KEY_CURRENT_INPUT_SOURCE = 'current'; | ||||
| const KEY_INPUT_SOURCES = 'sources'; | ||||
|  | ||||
| const INPUT_SOURCE_TYPE_XKB = 'xkb'; | ||||
| const INPUT_SOURCE_TYPE_IBUS = 'ibus'; | ||||
|  | ||||
| const IBusManager = new Lang.Class({ | ||||
|     Name: 'IBusManager', | ||||
|  | ||||
|     _init: function(readyCallback) { | ||||
|         if (!IBus) | ||||
|             return; | ||||
|  | ||||
|         IBus.init(); | ||||
|  | ||||
|         this._readyCallback = readyCallback; | ||||
|         this._candidatePopup = new IBusCandidatePopup.CandidatePopup(); | ||||
|  | ||||
|         this._ibus = null; | ||||
|         this._panelService = null; | ||||
|         this._engines = {}; | ||||
|         this._ready = false; | ||||
|         this._registerPropertiesId = 0; | ||||
|  | ||||
|         this._nameWatcherId = Gio.DBus.session.watch_name(IBus.SERVICE_IBUS, | ||||
|                                                           Gio.BusNameWatcherFlags.NONE, | ||||
|                                                           Lang.bind(this, this._onNameAppeared), | ||||
|                                                           Lang.bind(this, this._clear)); | ||||
|     }, | ||||
|  | ||||
|     _clear: function() { | ||||
|         if (this._panelService) | ||||
|             this._panelService.destroy(); | ||||
|         if (this._ibus) | ||||
|             this._ibus.destroy(); | ||||
|  | ||||
|         this._ibus = null; | ||||
|         this._panelService = null; | ||||
|         this._candidatePopup.setPanelService(null); | ||||
|         this._engines = {}; | ||||
|         this._ready = false; | ||||
|         this._registerPropertiesId = 0; | ||||
|     }, | ||||
|  | ||||
|     _onNameAppeared: function() { | ||||
|         this._ibus = IBus.Bus.new_async(); | ||||
|         this._ibus.connect('connected', Lang.bind(this, this._onConnected)); | ||||
|     }, | ||||
|  | ||||
|     _onConnected: function() { | ||||
|         this._ibus.list_engines_async(-1, null, Lang.bind(this, this._initEngines)); | ||||
|         this._ibus.request_name_async(IBus.SERVICE_PANEL, | ||||
|                                       IBus.BusNameFlag.REPLACE_EXISTING, | ||||
|                                       -1, null, | ||||
|                                       Lang.bind(this, this._initPanelService)); | ||||
|         this._ibus.connect('disconnected', Lang.bind(this, this._clear)); | ||||
|     }, | ||||
|  | ||||
|     _initEngines: function(ibus, result) { | ||||
|         let enginesList = this._ibus.list_engines_async_finish(result); | ||||
|         if (enginesList) { | ||||
|             for (let i = 0; i < enginesList.length; ++i) { | ||||
|                 let name = enginesList[i].get_name(); | ||||
|                 this._engines[name] = enginesList[i]; | ||||
|             } | ||||
|         } else { | ||||
|             this._clear(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._updateReadiness(); | ||||
|     }, | ||||
|  | ||||
|     _initPanelService: function(ibus, result) { | ||||
|         let success = this._ibus.request_name_async_finish(result); | ||||
|         if (success) { | ||||
|             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; | ||||
|         } | ||||
|  | ||||
|         this._updateReadiness(); | ||||
|     }, | ||||
|  | ||||
|     _updateReadiness: function() { | ||||
|         this._ready = (Object.keys(this._engines).length > 0 && | ||||
|                        this._panelService != null); | ||||
|  | ||||
|         if (this._ready && this._readyCallback) | ||||
|             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; | ||||
|  | ||||
|         return this._engines[id]; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(IBusManager.prototype); | ||||
|  | ||||
| const LayoutMenuItem = new Lang.Class({ | ||||
|     Name: 'LayoutMenuItem', | ||||
|     Extends: PopupMenu.PopupBaseMenuItem, | ||||
|  | ||||
|     _init: function(displayName, shortName) { | ||||
|     _init: function(config, id, indicator, long_name) { | ||||
|         this.parent(); | ||||
|  | ||||
|         this.label = new St.Label({ text: displayName }); | ||||
|         this.indicator = new St.Label({ text: shortName }); | ||||
|         this._config = config; | ||||
|         this._id = id; | ||||
|         this.label = new St.Label({ text: long_name }); | ||||
|         this.indicator = indicator; | ||||
|         this.addActor(this.label); | ||||
|         this.addActor(this.indicator); | ||||
|         this.actor.label_actor = this.label; | ||||
|     }, | ||||
|  | ||||
|     activate: function(event) { | ||||
|         this.parent(event); | ||||
|  | ||||
|         this._config.lock_group(this._id); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const InputSourceIndicator = new Lang.Class({ | ||||
|     Name: 'InputSourceIndicator', | ||||
| const XKBIndicator = new Lang.Class({ | ||||
|     Name: 'XKBIndicator', | ||||
|     Extends: PanelMenu.Button, | ||||
|  | ||||
|     _propertiesWhitelist: [ | ||||
|         'InputMode', | ||||
|         'TypingMode', | ||||
|         'DictMode' | ||||
|     ], | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent(0.0, _("Keyboard")); | ||||
|  | ||||
| @@ -201,325 +50,122 @@ const InputSourceIndicator = new Lang.Class({ | ||||
|         this.actor.add_actor(this._container); | ||||
|         this.actor.add_style_class_name('panel-status-button'); | ||||
|  | ||||
|         this._labelActors = {}; | ||||
|         this._layoutItems = {}; | ||||
|         this._iconActor = new St.Icon({ icon_name: 'keyboard', icon_type: St.IconType.SYMBOLIC, style_class: 'system-status-icon' }); | ||||
|         this._container.add_actor(this._iconActor); | ||||
|         this._labelActors = [ ]; | ||||
|         this._layoutItems = [ ]; | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA }); | ||||
|         this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged)); | ||||
|         this._settings.connect('changed::' + KEY_INPUT_SOURCES, Lang.bind(this, this._inputSourcesChanged)); | ||||
|         this._showFlags = false; | ||||
|         this._config = Gkbd.Configuration.get(); | ||||
|         this._config.connect('changed', Lang.bind(this, this._syncConfig)); | ||||
|         this._config.connect('group-changed', Lang.bind(this, this._syncGroup)); | ||||
|         this._config.start_listen(); | ||||
|  | ||||
|         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._syncConfig(); | ||||
|  | ||||
|         if (global.session_type == Shell.SessionType.USER) { | ||||
|             this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|             this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, function() { | ||||
|                 Main.overview.hide(); | ||||
|                 Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]); | ||||
|             })); | ||||
|         } | ||||
|         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; | ||||
|     }, | ||||
|     _adjustGroupNames: function(names) { | ||||
|         // Disambiguate duplicate names with a subscript | ||||
|         // This is O(N^2) to avoid sorting names | ||||
|         // but N <= 4 so who cares? | ||||
|  | ||||
|     _currentInputSourceChanged: function() { | ||||
|         let nVisibleSources = Object.keys(this._layoutItems).length; | ||||
|         let newCurrentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE); | ||||
|         let newLayoutItem = this._layoutItems[newCurrentSourceIndex]; | ||||
|         let hasProperties; | ||||
|  | ||||
|         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. | ||||
|             this.menu.close(); | ||||
|             this.actor.hide(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         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._currentSourceIndex = newCurrentSourceIndex; | ||||
|     }, | ||||
|  | ||||
|     _inputSourcesChanged: function() { | ||||
|         let sources = this._settings.get_value(KEY_INPUT_SOURCES); | ||||
|         let nSources = sources.n_children(); | ||||
|  | ||||
|         for (let i in this._layoutItems) | ||||
|             this._layoutItems[i].destroy(); | ||||
|  | ||||
|         for (let i in this._labelActors) | ||||
|             this._labelActors[i].destroy(); | ||||
|  | ||||
|         this._layoutItems = {}; | ||||
|         this._labelActors = {}; | ||||
|  | ||||
|         let infos = []; | ||||
|         let infosByShortName = {}; | ||||
|  | ||||
|         for (let i = 0; i < nSources; i++) { | ||||
|             let info = { exists: false }; | ||||
|             let [type, id] = sources.get_child_value(i).deep_unpack(); | ||||
|  | ||||
|             if (type == INPUT_SOURCE_TYPE_XKB) { | ||||
|                 [info.exists, info.displayName, info.shortName, , ] = | ||||
|                     this._xkbInfo.get_layout_info(id); | ||||
|             } 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; | ||||
|         for (let i = 0; i < names.length; i++) { | ||||
|             let name = names[i]; | ||||
|             let cnt = 0; | ||||
|             for (let j = i + 1; j < names.length; j++) { | ||||
|                 if (names[j] == name) { | ||||
|                     cnt++; | ||||
|                     // U+2081 SUBSCRIPT ONE | ||||
|                     names[j] = name + String.fromCharCode(0x2081 + cnt); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (!info.exists) | ||||
|                 continue; | ||||
|  | ||||
|             info.sourceIndex = i; | ||||
|  | ||||
|             if (!(info.shortName in infosByShortName)) | ||||
|                 infosByShortName[info.shortName] = []; | ||||
|             infosByShortName[info.shortName].push(info); | ||||
|             infos.push(info); | ||||
|             if (cnt != 0) | ||||
|                 names[i] = name + '\u2081'; | ||||
|         } | ||||
|  | ||||
|         for (let i = 0; i < infos.length; i++) { | ||||
|             let info = infos[i]; | ||||
|             if (infosByShortName[info.shortName].length > 1) { | ||||
|                 let sub = infosByShortName[info.shortName].indexOf(info) + 1; | ||||
|                 info.shortName += String.fromCharCode(0x2080 + sub); | ||||
|             } | ||||
|         return names; | ||||
|     }, | ||||
|  | ||||
|             let item = new LayoutMenuItem(info.displayName, info.shortName); | ||||
|             item.ibusEngineId = info.ibusEngineId; | ||||
|             this._layoutItems[info.sourceIndex] = item; | ||||
|     _syncConfig: function() { | ||||
|         this._showFlags = this._config.if_flags_shown(); | ||||
|         if (this._showFlags) { | ||||
|             this._container.set_skip_paint(this._iconActor, false); | ||||
|         } else { | ||||
|             this._container.set_skip_paint(this._iconActor, true); | ||||
|         } | ||||
|  | ||||
|         let groups = this._config.get_group_names(); | ||||
|         if (groups.length > 1) { | ||||
|             this.actor.show(); | ||||
|         } else { | ||||
|             this.menu.close(); | ||||
|             this.actor.hide(); | ||||
|         } | ||||
|  | ||||
|         for (let i = 0; i < this._layoutItems.length; i++) | ||||
|             this._layoutItems[i].destroy(); | ||||
|  | ||||
|         for (let i = 0; i < this._labelActors.length; i++) | ||||
|             this._labelActors[i].destroy(); | ||||
|  | ||||
|         let short_names = this._adjustGroupNames(this._config.get_short_group_names()); | ||||
|  | ||||
|         this._selectedLayout = null; | ||||
|         this._layoutItems = [ ]; | ||||
|         this._selectedLabel = null; | ||||
|         this._labelActors = [ ]; | ||||
|         for (let i = 0; i < groups.length; i++) { | ||||
|             let icon_name = this._config.get_group_name(i); | ||||
|             let actor; | ||||
|             if (this._showFlags) | ||||
|                 actor = new St.Icon({ icon_name: icon_name, icon_type: St.IconType.SYMBOLIC, style_class: 'popup-menu-icon' }); | ||||
|             else | ||||
|                 actor = new St.Label({ text: short_names[i] }); | ||||
|             let item = new LayoutMenuItem(this._config, i, actor, groups[i]); | ||||
|             item._short_group_name = short_names[i]; | ||||
|             item._icon_name = icon_name; | ||||
|             this._layoutItems.push(item); | ||||
|             this.menu.addMenuItem(item, i); | ||||
|             item.connect('activate', Lang.bind(this, function() { | ||||
|                 this._settings.set_value(KEY_CURRENT_INPUT_SOURCE, | ||||
|                                          GLib.Variant.new_uint32(info.sourceIndex)); | ||||
|             })); | ||||
|  | ||||
|             let shortLabel = new St.Label({ text: info.shortName }); | ||||
|             this._labelActors[info.sourceIndex] = shortLabel; | ||||
|             let shortLabel = new St.Label({ text: short_names[i] }); | ||||
|             this._labelActors.push(shortLabel); | ||||
|             this._container.add_actor(shortLabel); | ||||
|             this._container.set_skip_paint(shortLabel, true); | ||||
|         } | ||||
|  | ||||
|         this._currentInputSourceChanged(); | ||||
|         this._syncGroup(); | ||||
|     }, | ||||
|  | ||||
|     _showLayout: function() { | ||||
|         Main.overview.hide(); | ||||
|     _syncGroup: function() { | ||||
|         let selected = this._config.get_current_group(); | ||||
|  | ||||
|         let sources = this._settings.get_value(KEY_INPUT_SOURCES); | ||||
|         let current = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE); | ||||
|         let [type, id] = sources.get_child_value(current).deep_unpack(); | ||||
|         let xkbLayout = ''; | ||||
|         let xkbVariant = ''; | ||||
|  | ||||
|         if (type == INPUT_SOURCE_TYPE_XKB) { | ||||
|             [, , , xkbLayout, xkbVariant] = this._xkbInfo.get_layout_info(id); | ||||
|         } else if (type == INPUT_SOURCE_TYPE_IBUS) { | ||||
|             let engineDesc = this._ibusManager.getEngineDesc(id); | ||||
|             if (engineDesc) { | ||||
|                 xkbLayout = engineDesc.get_layout(); | ||||
|                 xkbVariant = ''; | ||||
|             } | ||||
|         if (this._selectedLayout) { | ||||
|             this._selectedLayout.setShowDot(false); | ||||
|             this._selectedLayout = null; | ||||
|         } | ||||
|  | ||||
|         if (!xkbLayout || xkbLayout.length == 0) | ||||
|             return; | ||||
|  | ||||
|         let description = xkbLayout; | ||||
|         if (xkbVariant.length > 0) | ||||
|             description = description + '\t' + xkbVariant; | ||||
|  | ||||
|         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; | ||||
|         if (this._selectedLabel) { | ||||
|             this._container.set_skip_paint(this._selectedLabel, true); | ||||
|             this._selectedLabel = null; | ||||
|         } | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     _ibusPropertiesRegistered: function(im, props) { | ||||
|         this._properties = props; | ||||
|         this._buildPropSection(); | ||||
|     }, | ||||
|         let item = this._layoutItems[selected]; | ||||
|         item.setShowDot(true); | ||||
|  | ||||
|     _ibusPropertyUpdated: function(im, prop) { | ||||
|         if (!this._propertyWhitelisted(prop)) | ||||
|             return; | ||||
|         this._iconActor.icon_name = item._icon_name; | ||||
|         this._selectedLabel = this._labelActors[selected]; | ||||
|         this._container.set_skip_paint(this._selectedLabel, this._showFlags); | ||||
|  | ||||
|         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); | ||||
|         } | ||||
|         this._selectedLayout = item; | ||||
|     }, | ||||
|  | ||||
|     _containerGetPreferredWidth: function(container, for_height, alloc) { | ||||
| @@ -527,11 +173,15 @@ const InputSourceIndicator = new Lang.Class({ | ||||
|         // for the height of all children, but we ignore the results | ||||
|         // for those we don't actually display. | ||||
|         let max_min_width = 0, max_natural_width = 0; | ||||
|         if (this._showFlags) | ||||
|             [max_min_width, max_natural_width] = this._iconActor.get_preferred_width(for_height); | ||||
|  | ||||
|         for (let i in this._labelActors) { | ||||
|         for (let i = 0; i < this._labelActors.length; i++) { | ||||
|             let [min_width, natural_width] = this._labelActors[i].get_preferred_width(for_height); | ||||
|             max_min_width = Math.max(max_min_width, min_width); | ||||
|             max_natural_width = Math.max(max_natural_width, natural_width); | ||||
|             if (!this._showFlags) { | ||||
|                 max_min_width = Math.max(max_min_width, min_width); | ||||
|                 max_natural_width = Math.max(max_natural_width, natural_width); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         alloc.min_size = max_min_width; | ||||
| @@ -540,11 +190,15 @@ const InputSourceIndicator = new Lang.Class({ | ||||
|  | ||||
|     _containerGetPreferredHeight: function(container, for_width, alloc) { | ||||
|         let max_min_height = 0, max_natural_height = 0; | ||||
|  | ||||
|         for (let i in this._labelActors) { | ||||
|         if (this._showFlags) | ||||
|             [max_min_height, max_natural_height] = this._iconActor.get_preferred_height(for_width); | ||||
|          | ||||
|         for (let i = 0; i < this._labelActors.length; i++) { | ||||
|             let [min_height, natural_height] = this._labelActors[i].get_preferred_height(for_width); | ||||
|             max_min_height = Math.max(max_min_height, min_height); | ||||
|             max_natural_height = Math.max(max_natural_height, natural_height); | ||||
|             if (!this._showFlags) { | ||||
|                 max_min_height = Math.max(max_min_height, min_height); | ||||
|                 max_natural_height = Math.max(max_natural_height, natural_height); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         alloc.min_size = max_min_height; | ||||
| @@ -558,7 +212,8 @@ const InputSourceIndicator = new Lang.Class({ | ||||
|         box.y2 -= box.y1; | ||||
|         box.y1 = 0; | ||||
|  | ||||
|         for (let i in this._labelActors) | ||||
|         this._iconActor.allocate_align_fill(box, 0.5, 0, false, false, flags); | ||||
|         for (let i = 0; i < this._labelActors.length; i++) | ||||
|             this._labelActors[i].allocate_align_fill(box, 0.5, 0, false, false, flags); | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -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); | ||||
|         } | ||||
|     } | ||||
| }); | ||||