Compare commits
	
		
			63 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					b2a6286c65 | ||
| 
						 | 
					0656328beb | ||
| 
						 | 
					fdad3a7f18 | ||
| 
						 | 
					877145809a | ||
| 
						 | 
					7bd0b6de52 | ||
| 
						 | 
					aa3d8d6796 | ||
| 
						 | 
					84284c04d0 | ||
| 
						 | 
					70e5f184a8 | ||
| 
						 | 
					dca19085c0 | ||
| 
						 | 
					742a0bca52 | ||
| 
						 | 
					d606ce6776 | ||
| 
						 | 
					7ff63a26b7 | ||
| 
						 | 
					f8c980cb22 | ||
| 
						 | 
					74ed3646ad | ||
| 
						 | 
					80dfda17cd | ||
| 
						 | 
					3e98d47687 | ||
| 
						 | 
					3d3494a15b | ||
| 
						 | 
					3bf11dbd31 | ||
| 
						 | 
					626e962e03 | ||
| 
						 | 
					759de873b5 | ||
| 
						 | 
					552ae78557 | ||
| 
						 | 
					d85a16589b | ||
| 
						 | 
					136c76dcbe | ||
| 
						 | 
					2d95201af2 | ||
| 
						 | 
					039c683bac | ||
| 
						 | 
					d9ab2320d5 | ||
| 
						 | 
					fa515328eb | ||
| 
						 | 
					0cfedcaad6 | ||
| 
						 | 
					65230bfd7f | ||
| 
						 | 
					a5324462c3 | ||
| 
						 | 
					7e654ab3ca | ||
| 
						 | 
					9601908fca | ||
| 
						 | 
					ab4c160f9d | ||
| 
						 | 
					234882cbc6 | ||
| 
						 | 
					e890674e49 | ||
| 
						 | 
					cd01e2fb25 | ||
| 
						 | 
					62027beb8e | ||
| 
						 | 
					ee4d861a29 | ||
| 
						 | 
					3a3e26c1bd | ||
| 
						 | 
					bf2ad9d4e8 | ||
| 
						 | 
					b053d9c84f | ||
| 
						 | 
					4afa8bebdb | ||
| 
						 | 
					caf3d5add0 | ||
| 
						 | 
					7cf038474a | ||
| 
						 | 
					c7bcc8150c | ||
| 
						 | 
					6d58397508 | ||
| 
						 | 
					49d0fc178d | ||
| 
						 | 
					b83b8f64ff | ||
| 
						 | 
					6cc8a42720 | ||
| 
						 | 
					50a2d285eb | ||
| 
						 | 
					fdf05310e9 | ||
| 
						 | 
					7baa393b1b | ||
| 
						 | 
					113694c44b | ||
| 
						 | 
					0c2f51b27f | ||
| 
						 | 
					04cb84de2a | ||
| 
						 | 
					9475c16e80 | ||
| 
						 | 
					6f41a9e10c | ||
| 
						 | 
					be3b939a24 | ||
| 
						 | 
					04fb3a5a7c | ||
| 
						 | 
					697b6dbfff | ||
| 
						 | 
					bb4639e423 | ||
| 
						 | 
					69db63b4cd | ||
| 
						 | 
					f9d17b214e | 
							
								
								
									
										24
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -16,43 +16,23 @@ 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
 | 
			
		||||
data/gnome-shell-extension-prefs.desktop.in
 | 
			
		||||
data/gschemas.compiled
 | 
			
		||||
data/org.gnome.shell.gschema.xml
 | 
			
		||||
data/org.gnome.shell.gschema.valid
 | 
			
		||||
data/org.gnome.shell.evolution.calendar.gschema.xml
 | 
			
		||||
data/org.gnome.shell.evolution.calendar.gschema.valid
 | 
			
		||||
docs/reference/*/*.args
 | 
			
		||||
docs/reference/*/*.bak
 | 
			
		||||
docs/reference/*/*.hierarchy
 | 
			
		||||
docs/reference/*/*.interfaces
 | 
			
		||||
docs/reference/*/*.prerequisites
 | 
			
		||||
docs/reference/*/*.sgml
 | 
			
		||||
docs/reference/*/*.signals
 | 
			
		||||
docs/reference/*/*.stamp
 | 
			
		||||
docs/reference/*/*.txt
 | 
			
		||||
docs/reference/*/*.types
 | 
			
		||||
docs/reference/*/html/
 | 
			
		||||
docs/reference/*/xml/
 | 
			
		||||
gtk-doc.make
 | 
			
		||||
js/misc/config.js
 | 
			
		||||
intltool-extract.in
 | 
			
		||||
intltool-merge.in
 | 
			
		||||
intltool-update.in
 | 
			
		||||
libtool
 | 
			
		||||
m4/
 | 
			
		||||
man/gnome-shell.1
 | 
			
		||||
omf.make
 | 
			
		||||
po/*.gmo
 | 
			
		||||
po/gnome-shell.pot
 | 
			
		||||
po/*.header
 | 
			
		||||
po/*.sed
 | 
			
		||||
po/*.sin
 | 
			
		||||
po/.intltool-merge-cache
 | 
			
		||||
po/Makefile.in.in
 | 
			
		||||
po/Makevars.template
 | 
			
		||||
po/POTFILES
 | 
			
		||||
@@ -65,17 +45,13 @@ 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
 | 
			
		||||
src/gnome-shell-extension-tool
 | 
			
		||||
src/gnome-shell-extension-prefs
 | 
			
		||||
src/gnome-shell-hotplug-sniffer
 | 
			
		||||
src/gnome-shell-jhbuild
 | 
			
		||||
src/gnome-shell-perf-helper
 | 
			
		||||
src/gnome-shell-perf-tool
 | 
			
		||||
src/gnome-shell-real
 | 
			
		||||
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
 | 
			
		||||
src/run-js-test
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										331
									
								
								HACKING
									
									
									
									
									
								
							
							
						
						@@ -1,331 +0,0 @@
 | 
			
		||||
Coding guide
 | 
			
		||||
============
 | 
			
		||||
 | 
			
		||||
Our goal is to have all JavaScript code in GNOME follow a consistent style. In
 | 
			
		||||
a dynamic language like JavaScript, it is essential to be rigorous about style
 | 
			
		||||
(and unit tests), or you rapidly end up with a spaghetti-code mess.
 | 
			
		||||
 | 
			
		||||
A quick note
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
Life isn't fun if you can't break the rules. If a rule seems unnecessarily
 | 
			
		||||
restrictive while you're coding, ignore it, and let the patch reviewer decide
 | 
			
		||||
what to do.
 | 
			
		||||
 | 
			
		||||
Indentation and whitespace
 | 
			
		||||
--------------------------
 | 
			
		||||
 | 
			
		||||
Use four-space indents. Braces are on the same line as their associated
 | 
			
		||||
statements.  You should only omit braces if *both* sides of the statement are
 | 
			
		||||
on one line.
 | 
			
		||||
 | 
			
		||||
* One space after the `function` keyword.  No space between the function name
 | 
			
		||||
* in a declaration or a call.  One space before the parens in the `if`
 | 
			
		||||
* statements, or `while`, or `for` loops.
 | 
			
		||||
 | 
			
		||||
    function foo(a, b) {
 | 
			
		||||
        let bar;
 | 
			
		||||
 | 
			
		||||
        if (a > b)
 | 
			
		||||
            bar = do_thing(a);
 | 
			
		||||
        else
 | 
			
		||||
            bar = do_thing(b);
 | 
			
		||||
 | 
			
		||||
        if (var == 5) {
 | 
			
		||||
            for (let i = 0; i < 10; i++) {
 | 
			
		||||
                print(i);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            print(20);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
Semicolons
 | 
			
		||||
----------
 | 
			
		||||
 | 
			
		||||
JavaScript allows omitting semicolons at the end of lines, but don't. Always
 | 
			
		||||
end statements with a semicolon.
 | 
			
		||||
 | 
			
		||||
js2-mode
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
If using Emacs, do not use js2-mode. It is outdated and hasn't worked for a
 | 
			
		||||
while. emacs now has a built-in JavaScript mode, js-mode, based on
 | 
			
		||||
espresso-mode. It is the de facto emacs mode for JavaScript.
 | 
			
		||||
 | 
			
		||||
File naming and creation
 | 
			
		||||
------------------------
 | 
			
		||||
 | 
			
		||||
For JavaScript files, use lowerCamelCase-style names, with a `.js` extension.
 | 
			
		||||
 | 
			
		||||
We only use C where gjs/gobject-introspection is not available for the task, or
 | 
			
		||||
where C would be cleaner. To work around limitations in
 | 
			
		||||
gjs/gobject-introspection itself, add a new method in `shell-util.[ch]`.
 | 
			
		||||
 | 
			
		||||
Like many other GNOME projects, we prefix our C source filenames with the
 | 
			
		||||
library name followed by a dash, e.g. `shell-app-system.c`. Create a
 | 
			
		||||
`-private.h` header when you want to share code internally in the
 | 
			
		||||
library. These headers are not installed, distributed or introspected.
 | 
			
		||||
 | 
			
		||||
Imports
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
Use UpperCamelCase when importing modules to distinguish them from ordinary
 | 
			
		||||
variables, e.g.
 | 
			
		||||
 | 
			
		||||
    const GLib = imports.gi.GLib;
 | 
			
		||||
 | 
			
		||||
Imports should be categorized into one of two places. The top-most import block
 | 
			
		||||
should contain only "environment imports". These are either modules from
 | 
			
		||||
gobject-introspection or modules added by gjs itself.
 | 
			
		||||
 | 
			
		||||
The second block of imports should contain only "application imports". These
 | 
			
		||||
are the JS code that is in the gnome-shell codebase,
 | 
			
		||||
e.g. `imports.ui.popupMenu`.
 | 
			
		||||
 | 
			
		||||
Each import block should be sorted alphabetically. Don't import modules you
 | 
			
		||||
don't use.
 | 
			
		||||
 | 
			
		||||
    const GLib = imports.gi.GLib;
 | 
			
		||||
    const Gio = imports.gi.Gio;
 | 
			
		||||
    const Lang = imports.lang;
 | 
			
		||||
    const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
    const Main = imports.ui.main;
 | 
			
		||||
    const Params = imports.misc.params;
 | 
			
		||||
    const Tweener = imports.ui.tweener;
 | 
			
		||||
    const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
The alphabetical ordering should be done independently of the location of the
 | 
			
		||||
location. Never reference `imports` in actual code.
 | 
			
		||||
 | 
			
		||||
Constants
 | 
			
		||||
---------
 | 
			
		||||
 | 
			
		||||
We use CONSTANTS_CASE to define constants. All constants should be directly
 | 
			
		||||
under the imports:
 | 
			
		||||
 | 
			
		||||
    const MY_DBUS_INTERFACE = 'org.my.Interface';
 | 
			
		||||
 | 
			
		||||
Variable declaration
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
Always use either `const` or `let` when defining a variable.
 | 
			
		||||
 | 
			
		||||
    // Iterating over an array
 | 
			
		||||
    for (let i = 0; i < arr.length; ++i) {
 | 
			
		||||
        let item = arr[i];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Iterating over an object's properties
 | 
			
		||||
    for (let prop in someobj) {
 | 
			
		||||
        ...
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
If you use "var" then the variable is added to function scope, not block scope.
 | 
			
		||||
See [What's new in JavaScript 1.7](https://developer.mozilla.org/en/JavaScript/New_in_JavaScript/1.7#Block_scope_with_let_%28Merge_into_let_Statement%29)
 | 
			
		||||
 | 
			
		||||
Classes
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
There are many approaches to classes in JavaScript. We use our own class framework
 | 
			
		||||
(sigh), which is built in gjs. The advantage is that it supports inheriting from
 | 
			
		||||
GObjects, although this feature isn't used very often in the Shell itself.
 | 
			
		||||
 | 
			
		||||
    const IconLabelMenuItem = new Lang.Class({
 | 
			
		||||
        Name: 'IconLabelMenuItem',
 | 
			
		||||
        Extends: PopupMenu.PopupMenuBaseItem,
 | 
			
		||||
 | 
			
		||||
        _init: function(icon, label) {
 | 
			
		||||
            this.parent({ reactive: false });
 | 
			
		||||
            this.addActor(icon);
 | 
			
		||||
            this.addActor(label);
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        open: function() {
 | 
			
		||||
            log("menu opened!");
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
* 'Name' is required. 'Extends' is optional. If you leave it out, you will
 | 
			
		||||
  automatically inherit from Object.
 | 
			
		||||
 | 
			
		||||
* Leave a blank line between the "class header" (Name, Extends, and other
 | 
			
		||||
  things)  and the "class body" (methods). Leave a blank line between each
 | 
			
		||||
  method.
 | 
			
		||||
 | 
			
		||||
* No space before the colon, one space after.
 | 
			
		||||
 | 
			
		||||
* No trailing comma after the last item.
 | 
			
		||||
 | 
			
		||||
* Make sure to use a semicolon after the closing paren to the class. It's
 | 
			
		||||
  still a giant function call, even though it may resemble a more
 | 
			
		||||
  conventional syntax.
 | 
			
		||||
 | 
			
		||||
GObject Introspection
 | 
			
		||||
---------------------
 | 
			
		||||
 | 
			
		||||
GObject Introspection is a powerful feature that allows us to have native
 | 
			
		||||
bindings for almost any library built around GObject. If a library requires
 | 
			
		||||
you to inherit from a type to use it, you can do so:
 | 
			
		||||
 | 
			
		||||
    const MyClutterActor = new Lang.Class({
 | 
			
		||||
        Name: 'MyClutterActor',
 | 
			
		||||
        Extends: Clutter.Actor,
 | 
			
		||||
 | 
			
		||||
        vfunc_get_preferred_width: function(actor, forHeight) {
 | 
			
		||||
             return [100, 100];
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        vfunc_get_preferred_height: function(actor, forWidth) {
 | 
			
		||||
             return [100, 100];
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        vfunc_paint: function(actor) {
 | 
			
		||||
             let alloc = this.get_allocation_box();
 | 
			
		||||
             Cogl.set_source_color4ub(255, 0, 0, 255);
 | 
			
		||||
             Cogl.rectangle(alloc.x1, alloc.y1,
 | 
			
		||||
                            alloc.x2, alloc.y2);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
Translatable strings, `environment.js`
 | 
			
		||||
--------------------------------------
 | 
			
		||||
 | 
			
		||||
We use gettext to translate the GNOME Shell into all the languages that GNOME
 | 
			
		||||
supports. The `gettext` function is aliased globally as `_`, you do not need to
 | 
			
		||||
explicitly import it. This is done through some magic in the
 | 
			
		||||
[environment.js](http://git.gnome.org/browse/gnome-shell/tree/js/ui/environment.js)
 | 
			
		||||
file. If you can't find a method that's used, it's probably either in gjs itself
 | 
			
		||||
or installed on the global object from the Environment.
 | 
			
		||||
 | 
			
		||||
Use 'single quotes' for programming strings that should not be translated
 | 
			
		||||
and "double quotes" for strings that the user may see. This allows us to
 | 
			
		||||
quickly find untranslated or mistranslated strings by grepping through the
 | 
			
		||||
sources for double quotes without a gettext call around them.
 | 
			
		||||
 | 
			
		||||
`actor` and `_delegate`
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
gjs allows us to set so-called "expando properties" on introspected objects,
 | 
			
		||||
allowing us to treat them like any other. Because the Shell was built before
 | 
			
		||||
you could inherit from GTypes natively in JS, we usually have a wrapper class
 | 
			
		||||
that has a property called `actor`. We call this wrapper class the "delegate".
 | 
			
		||||
 | 
			
		||||
We sometimes use expando properties to set a property called `_delegate` on
 | 
			
		||||
the actor itself:
 | 
			
		||||
 | 
			
		||||
    const MyClass = new Lang.Class({
 | 
			
		||||
        Name: 'MyClass',
 | 
			
		||||
 | 
			
		||||
        _init: function() {
 | 
			
		||||
            this.actor = new St.Button({ text: "This is a button" });
 | 
			
		||||
            this.actor._delegate = this;
 | 
			
		||||
 | 
			
		||||
            this.actor.connect('clicked', Lang.bind(this, this._onClicked));
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        _onClicked: function(actor) {
 | 
			
		||||
            actor.set_label("You clicked the button!");
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
The 'delegate' property is important for anything which trying to get the
 | 
			
		||||
delegate object from an associated actor. For instance, the drag and drop
 | 
			
		||||
system calls the `handleDragOver` function on the delegate of a "drop target"
 | 
			
		||||
when the user drags an item over it. If you do not set the `_delegate`
 | 
			
		||||
property, your actor will not be able to be dropped onto.
 | 
			
		||||
 | 
			
		||||
Functional style
 | 
			
		||||
----------------
 | 
			
		||||
 | 
			
		||||
JavaScript Array objects offer a lot of common functional programming
 | 
			
		||||
capabilities such as forEach, map, filter and so on. You can use these when
 | 
			
		||||
they make sense, but please don't have a spaghetti mess of function programming
 | 
			
		||||
messed in a procedural style. Use your best judgment.
 | 
			
		||||
 | 
			
		||||
Closures
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
`this` will not be captured in a closure, it is relative to how the closure is
 | 
			
		||||
invoked, not to the value of this where the closure is created, because "this"
 | 
			
		||||
is a keyword with a value passed in at function invocation time, it is not a
 | 
			
		||||
variable that can be captured in closures.
 | 
			
		||||
 | 
			
		||||
All closures should be wrapped with a Lang.bind.
 | 
			
		||||
 | 
			
		||||
    const Lang = imports.lang;
 | 
			
		||||
 | 
			
		||||
    let closure = Lang.bind(this, function() { this._fnorbate(); });
 | 
			
		||||
 | 
			
		||||
A more realistic example would be connecting to a signal on a method of a
 | 
			
		||||
prototype:
 | 
			
		||||
 | 
			
		||||
    const Lang = imports.lang;
 | 
			
		||||
    const FnorbLib = imports.fborbLib;
 | 
			
		||||
 | 
			
		||||
    const MyClass = new Lang.Class({
 | 
			
		||||
        _init: function() {
 | 
			
		||||
            let fnorb = new FnorbLib.Fnorb();
 | 
			
		||||
            fnorb.connect('frobate', Lang.bind(this, this._onFnorbFrobate));
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        _onFnorbFrobate: function(fnorb) {
 | 
			
		||||
            this._updateFnorb();
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
Object literal syntax
 | 
			
		||||
---------------------
 | 
			
		||||
 | 
			
		||||
In JavaScript, these are equivalent:
 | 
			
		||||
 | 
			
		||||
    foo = { 'bar': 42 };
 | 
			
		||||
    foo = { bar: 42 };
 | 
			
		||||
 | 
			
		||||
and so are these:
 | 
			
		||||
 | 
			
		||||
    var b = foo['bar'];
 | 
			
		||||
    var b = foo.bar;
 | 
			
		||||
 | 
			
		||||
If your usage of an object is like an object, then you're defining "member
 | 
			
		||||
variables." For member variables, use the no-quotes no-brackets syntax: `{ bar:
 | 
			
		||||
42 }` `foo.bar`.
 | 
			
		||||
 | 
			
		||||
If your usage of an object is like a hash table (and thus conceptually the keys
 | 
			
		||||
can have special chars in them), don't use quotes, but use brackets: `{ bar: 42
 | 
			
		||||
}`, `foo['bar']`.
 | 
			
		||||
 | 
			
		||||
Getters, setters, and Tweener
 | 
			
		||||
-----------------------------
 | 
			
		||||
 | 
			
		||||
Getters and setters should be used when you are dealing with an API that is
 | 
			
		||||
designed around setting properties, like Tweener. If you want to animate an
 | 
			
		||||
arbitrary property, create a getter and setter, and use Tweener to animate the
 | 
			
		||||
property.
 | 
			
		||||
 | 
			
		||||
    const ANIMATION_TIME = 2000;
 | 
			
		||||
 | 
			
		||||
    const MyClass = new Lang.Class({
 | 
			
		||||
        Name: 'MyClass',
 | 
			
		||||
 | 
			
		||||
        _init: function() {
 | 
			
		||||
            this.actor = new St.BoxLayout();
 | 
			
		||||
            this._position = 0;
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        get position() {
 | 
			
		||||
            return this._position;
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        set position(value) {
 | 
			
		||||
            this._position = value;
 | 
			
		||||
            this.actor.set_position(value, value);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let myThing = new MyClass();
 | 
			
		||||
    Tweener.addTween(myThing,
 | 
			
		||||
                     { position: 100,
 | 
			
		||||
                       time: ANIMATION_TIME,
 | 
			
		||||
                       transition: 'easeOutQuad' });
 | 
			
		||||
@@ -1,11 +1,7 @@
 | 
			
		||||
# Point to our macro directory and pick up user flags from the environment
 | 
			
		||||
ACLOCAL_AMFLAGS  = -I m4 ${ACLOCAL_FLAGS}
 | 
			
		||||
 | 
			
		||||
SUBDIRS = data js src browser-plugin tests po docs
 | 
			
		||||
 | 
			
		||||
if ENABLE_MAN
 | 
			
		||||
SUBDIRS += man
 | 
			
		||||
endif
 | 
			
		||||
SUBDIRS = data js src browser-plugin tests po man
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST =		\
 | 
			
		||||
	.project	\
 | 
			
		||||
@@ -17,12 +13,9 @@ EXTRA_DIST =		\
 | 
			
		||||
DIST_EXCLUDE =					\
 | 
			
		||||
	.gitignore				\
 | 
			
		||||
	gnome-shell.doap			\
 | 
			
		||||
	HACKING					\
 | 
			
		||||
	MAINTAINERS				\
 | 
			
		||||
	tools/build/*
 | 
			
		||||
 | 
			
		||||
distcheck-hook:
 | 
			
		||||
	@echo "Checking disted files against files in git"
 | 
			
		||||
	@$(srcdir)/tools/check-for-missing.py $(srcdir) $(distdir) $(DIST_EXCLUDE)
 | 
			
		||||
 | 
			
		||||
DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc --enable-man
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										967
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						@@ -1,936 +1,53 @@
 | 
			
		||||
3.7.2.1
 | 
			
		||||
3.2.2.1
 | 
			
		||||
=======
 | 
			
		||||
* Revert 490206b to not depend on NMGTK-0.9.7, which hasn't been released yet
 | 
			
		||||
* Fix several regressions with tray icons introduced in 3.2.2 [Owen; #664138]
 | 
			
		||||
 | 
			
		||||
3.7.2
 | 
			
		||||
Contributors:
 | 
			
		||||
 Owen Taylor
 | 
			
		||||
 | 
			
		||||
3.2.2
 | 
			
		||||
=====
 | 
			
		||||
* Enforce RTL in he for messages that might end up as LTR [Florian; #686630]
 | 
			
		||||
* gdm: Move logo into the panel [Florian; #685852]
 | 
			
		||||
* Hide notifications when closed button is clicked [Jasper, Florian; #682237]
 | 
			
		||||
* Tweak screenShield animations [Rui; #686745]
 | 
			
		||||
* Restore Fittsability of summary items in message tray [Florian; #686474]
 | 
			
		||||
* Save screencasts as recent item [Ray; #680647]
 | 
			
		||||
* overview: Resize window captions on content change [Giovanni, Alex; #620874]
 | 
			
		||||
* App search: Match GenericName too [Matthias; #687121]
 | 
			
		||||
* runDialog: Better match style of other modal dialogs [Florian, Allan; #687127]
 | 
			
		||||
* Improve the button insensitive style [Stéphane; #687110]
 | 
			
		||||
* network: Don't use a global switch for all VPN connections [Giovanni; #682929]
 | 
			
		||||
* appMenu: Update on icon theme changes [Florian; #687224]
 | 
			
		||||
* Show 'Log out' in more situations [Matthias; #686736]
 | 
			
		||||
* Add a setting to force the 'Log out' menuitem [Matthias; #686057]
 | 
			
		||||
* overview: Improve styling of search box [Stéphane; #686479]
 | 
			
		||||
* Implement 'disable-user-list' in login screen [Ray; #660660]
 | 
			
		||||
* Fix auto-scroll to bottom in chat notifications [Sjoerd; #686571]
 | 
			
		||||
* Show feedback notifications when user is busy [Stéphane; #662900]
 | 
			
		||||
* Disable login button when there is no input [Stéphane; #687112]
 | 
			
		||||
* Use non-linear overview shade for background [Giovanni, Pierre-Eric; #669798]
 | 
			
		||||
* Reduce blocking in compositor thread [Simon, Jasper; #687465]
 | 
			
		||||
* network: new country-specific type to gather providers [Aleksander; #687356]
 | 
			
		||||
* Update man page [Matthias; #680601]
 | 
			
		||||
* st-entry: Change the pointer cursor on enter/leave events [Thomas; #687130]
 | 
			
		||||
* screenShield: Blur and desaturate the background [Giovanni, Cosimo; #682536]
 | 
			
		||||
* Change height of chat notifications to have more context [Carlos; #665255]
 | 
			
		||||
* screenShield: Account for motion velocity when hiding [Giovanni; #682537]
 | 
			
		||||
* screenShield: hide the cursor while the lock screen is on [Giovanni; #682535]
 | 
			
		||||
* Support remote search provider settings [Cosimo; #687491]
 | 
			
		||||
* unlockDialog: Improve label of confirmation button [Stéphane; #687656]
 | 
			
		||||
* userMenu: Rename "System Settings" item to "Settings" [Elad; #687738]
 | 
			
		||||
* messageTray: Add keybinding to focus current notification [Stéphane; #652082]
 | 
			
		||||
* Remove shell-screen-grabber [Neil; #685915]
 | 
			
		||||
* main: Stop using Metacity's keybinding files [Florian; #687672]
 | 
			
		||||
* Bluetooth: Remove ObexFTP functionality [Bastien; #688160]
 | 
			
		||||
* a11y: Also set WM theme when HighContrast is switched on [Cosimo; #688256]
 | 
			
		||||
* network: Rework multiple NIC support [Giovanni; #677142]
 | 
			
		||||
* Rework keybindings to allow selective blocking/processing [Florian; #688202]
 | 
			
		||||
* recorder: Show indicator on primary monitor [Adel; #688470]
 | 
			
		||||
* recorder: Set frame duration to fix broken video headers [Adel; #688487]
 | 
			
		||||
* Misc. bugfixes and cleanups [Florian, Jasper, Giovanni, Matthew, Stéphane,
 | 
			
		||||
  Allan, Daiki, Owen, Alejandro, Jean-François, Cosimo, Sebastian, Adel, Alban;
 | 
			
		||||
  #686484, #686728, #686805, #686574, #686763, #682428, #687132, #685239,
 | 
			
		||||
  #687189, #687226, #658091, #670687, #687457, #687242, #687287, #687020,
 | 
			
		||||
  #686583, #661194, #687491, #657315, #687958, #683986, #688089, #687708,
 | 
			
		||||
  #686530, #684810, #688181, #688475, #688557, #688507, #638351]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Elad Alfassa, Matthew Barnes, Alban Browaeys, Giovanni Campagna,
 | 
			
		||||
  Cosimo Cecchi, Matthias Clasen, Allan Day, Stéphane Démurget,
 | 
			
		||||
  Jean-François Fortin Tam, Adel Gadllah, Alex Hultman, Sebastian Keller,
 | 
			
		||||
  Rui Matos, Simon McVittie, Aleksander Morgado, Florian Müllner,
 | 
			
		||||
  Bastien Nocera, Pierre-Eric Pelloux-Prayer, Alejandro Piñeiro, Neil Roberts,
 | 
			
		||||
  Sjoerd Simons, Carlos Soriano Sánchez, Jasper St. Pierre, Ray Strode,
 | 
			
		||||
  Owen Taylor, Daiki Ueno, Thomas Wood
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Dušan Kazik [sk], Pavol Klačanský [sk], Piotr Drąg [pl], Yuri Myasoedov [ru],
 | 
			
		||||
  Marek Černocký [cs], Kjartan Maraas [nb], Wolfgang Stöggl [de],
 | 
			
		||||
  Yaron Shahrabani [he], Fran Diéguez [gl], Mattias Põldaru [et]
 | 
			
		||||
 | 
			
		||||
3.7.1
 | 
			
		||||
=====
 | 
			
		||||
* Add shortcut to open application view directly [Jeremy; #685738]
 | 
			
		||||
* Expose '<Super>F10' shortcut in System Settings [Florian; #672909]
 | 
			
		||||
* Clean up timestamp format in chat notifications [Carlos; #680989]
 | 
			
		||||
* loginScreen: Add support for 'disable-restart-buttons' [Florian; #686247]
 | 
			
		||||
* Update textures automatically on file changes [Florian; #679268]
 | 
			
		||||
* Implement org.gnome.ScreenSaver.GetActiveTime [Giovanni; #686064]
 | 
			
		||||
* Add missing translations for GSetting schema [Giovanni; #686413]
 | 
			
		||||
* Hide workspace switcher completely when it's not necessary [Seif; #686483]
 | 
			
		||||
* Explicitly load gnome-screensaver when not running GDM [Tim; #683060]
 | 
			
		||||
* Port to GnomeIdleMonitor [Jasper; #682224]
 | 
			
		||||
* Set Empathy as preferred handler when delegating channels [Xavier; #686296]
 | 
			
		||||
* Allow testing GDM login dialog from the session [Giovanni; #683725]
 | 
			
		||||
* Use all available space for windows in window picker [Jasper, Pierre-Eric;
 | 
			
		||||
  #582650]
 | 
			
		||||
* Use logind for suspend if available [Florian; #686482]
 | 
			
		||||
* Misc. fixes and cleanups [Jasper, Florian, Adel, Rui; #677426, #680426,
 | 
			
		||||
  #686233, #686241, #686318, #686240, #686484, #686002, #684650, #686487]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Jeremy Bicha, Giovanni Campagna, Xavier Claessens, Adel Gadllah, Seif Lotfy,
 | 
			
		||||
  Tim Lunn, Rui Matos, Florian Müllner, Pierre-Eric Pelloux-Prayer,
 | 
			
		||||
  Carlos Soriano, Jasper St. Pierre
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Andika Triwidada [id], Matej Urbančič [sl], Ihar Hrachyshka [be],
 | 
			
		||||
  Daniel Mustieles [es], Fran Diéguez [gl], Takayuki KUSANO [ja],
 | 
			
		||||
  Мирослав Николић [sr, sr@latin], Dušan Kazik [sk], Tom Tryfonidis [el]
 | 
			
		||||
 | 
			
		||||
3.6.1
 | 
			
		||||
=====
 | 
			
		||||
* dash: Make padding even on the top/bottom of the dash [Jasper; #684619]
 | 
			
		||||
* Fix a crash when dragging search results [Jasper; #684888]
 | 
			
		||||
* workspaceThumbnail: Fix dragging with static workspaces [Jasper; #684641]
 | 
			
		||||
* Really hide 'Show Keyboard Layout' on the lock screen [Matthias]
 | 
			
		||||
* Misc. improvements to jhbuild setup [Owen; #685352, #685353, #685354, #685355]
 | 
			
		||||
* Show message tray in Ctrl+Alt+Tab outside of the overview [Jasper, Florian;
 | 
			
		||||
  #684633, #685914]
 | 
			
		||||
* Disable hotplug sniffer on remote filesystems [Jasper; #684093]
 | 
			
		||||
* userMenu: Remove 'Switch Session' item [Florian; #685062]
 | 
			
		||||
* unlockDialog: Make prompt entry insensitive while logging in [Jasper; #685444]
 | 
			
		||||
* messageTray: Don't animate desktop clone for failed grabs [Jasper; #685342]
 | 
			
		||||
* Fix crash on dragging windows between workspaces [Ryan; #681399]
 | 
			
		||||
* userMenu: Ignore 'lock-enabled' setting for user switching [Florian; #685536]
 | 
			
		||||
* gdm: Fix key-focus on first user [Adel; #684650]
 | 
			
		||||
* Make grid button insensitive when dragging non-favorites [Jasper; #685313]
 | 
			
		||||
* Calendar: hide all actions when on the login screen [Matthias; #685142]
 | 
			
		||||
* Adapt unlock dialog layout for the login screen [Florian; #685201]
 | 
			
		||||
* Make focus-follows-mouse work better with Shell UI [Florian; #678169]
 | 
			
		||||
* Improve look of screen shield [Jasper; #685919]
 | 
			
		||||
* Fix keynav in the login screen [Florian; #684730]
 | 
			
		||||
* dateMenu: Hide "Open Calendar" item if calendar unavailable [Florian; #686050]
 | 
			
		||||
* unlockDialog: Reset UI on verification failure [Giovanni; #685441]
 | 
			
		||||
* Show unlock dialog on primary monitor when using keynav [Giovanni; #685855]
 | 
			
		||||
* Fix height changes of entries when entering text [Florian; #685534]
 | 
			
		||||
* Fix show-apps label after successful drags [Florian; #684627]
 | 
			
		||||
* Misc. bugfixes and cleanups [Jasper, Olivier, Florian, Owen, Adel, Tanner, Tim, Matthias; #685434, #685511, #685466, #685341, #685156, #681159, #673189, #686016, 684869, #686079, #686063
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
Jasper St. Pierre
 | 
			
		||||
Matthias Clasen
 | 
			
		||||
Owen Taylor
 | 
			
		||||
Olivier Blin
 | 
			
		||||
Florian Müllner
 | 
			
		||||
Ryan Lortie
 | 
			
		||||
Adel Gadllah
 | 
			
		||||
Tanner Doshier
 | 
			
		||||
Tim Lunn
 | 
			
		||||
Giovanni Campagna
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Tobias Endrigkeit [de], Rudolfs Mazurs [lv], Ask H. Larsen [da],
 | 
			
		||||
  Shankar Prasad [kn], Changwoo Ryu [ko], Chris Leonard [en_GB],
 | 
			
		||||
  Arash Mousavi [fa], Theppitak Karoonboonyanan [th], Seán de Búrca [ga],
 | 
			
		||||
  Yaron Shahrabani [he], Alexander Shopov [bg], Žygimantas Beručka [lt],
 | 
			
		||||
  Milo Casagrande [it], Kjartan Maraas [nb], Kris Thomsen [da],
 | 
			
		||||
  Aurimas Černius [lt], Yuri Myasoedov [ru], Мирослав Николић [sr],
 | 
			
		||||
  Marek Černocký [cs], Gabor Kelemen [hu], Ihar Hrachyshka [be],
 | 
			
		||||
  Chao-Hsiung Liao [zh_HK, zh_TW], Eleanor Chen [zh_CN],
 | 
			
		||||
  Carles Ferrando [ca@valencia], Vicent Cubells [ca], Daniel Korostil [uk],
 | 
			
		||||
  Alexandre Franke [fr], Piotr Drąg [pl]
 | 
			
		||||
 | 
			
		||||
3.6.0
 | 
			
		||||
=====
 | 
			
		||||
* keyboard: Make input source items accessible [Florian; #684462]
 | 
			
		||||
* Don't show network dialogs in the lock screen [Giovanni; #684384]
 | 
			
		||||
* popupMenu: Fix initial visibility of settings items [Florian; #684473]
 | 
			
		||||
* userMenu: Close menu immediately on user/session switch [Florian; #684459]
 | 
			
		||||
* Fix alignment of search section headers in RTL locales [Florian; #684379]
 | 
			
		||||
* screenShield: Fix unlock animation [Florian; #684591]
 | 
			
		||||
* Don't open the tray from a dwell while in a modal grab [Jasper; #684458]
 | 
			
		||||
* userMenu: Fix texture updates on icon changes [Florian; #679268]
 | 
			
		||||
* Fix a11y support in the login screen [Florian, Ray; #684727, #684728, #684748]
 | 
			
		||||
* Make On-Screen-Keyboard usable with new message tray [Giovanni, Florian;
 | 
			
		||||
  #683546]
 | 
			
		||||
* Fix initial visibility of input volume in lock-screen [Florian; #684611]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Florian Müllner, Jasper St. Pierre, Ray Strode
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Matej Urbančič [sl], Dr.T.Vasudevan [ta], Piotr Drąg [pl], A S Alam [pa],
 | 
			
		||||
  Alexander Shopov [bg], Nilamdyuti Goswami [as], Chandan Kumar [hi],
 | 
			
		||||
  Khaled Hosny [ar], Ibrahim Saed [ar], Sandeep Sheshrao Shedmake [mr],
 | 
			
		||||
  Tom Tryfonidis [el], Theppitak Karoonboonyanan [th], Alexandre Franke [fr],
 | 
			
		||||
  Fran Diéguez [gl], Gabor Kelemen [hu], Ani Peter [ml], Daniel Mustieles [es],
 | 
			
		||||
  Мирослав Николић [sr, sr@latin], Duarte Loreto [pt], ManojKumar Giri [or],
 | 
			
		||||
  Ihar Hrachyshka [be], Aurimas Černius [lt], Djavan Fagundes [pt_BR],
 | 
			
		||||
  Changwoo Ryu [ko], Bruce Cowan [en_GB], Kris Thomsen [da], Gil Forcada [ca],
 | 
			
		||||
  Yaron Shahrabani [he], Milo Casagrande [it], Ville-Pekka Vainio [fi],
 | 
			
		||||
  YunQiang Su [zh_CN], Carles Ferrando [ca@valencia], Mario Blättermann [de],
 | 
			
		||||
  Rajesh Ranjan [hi], Yuri Myasoedov [ru], Rūdolfs Mazurs [lv],
 | 
			
		||||
  Jiro Matsuzawa [ja], Mattias Põldaru [et], Timur Zhamakeev [ky],
 | 
			
		||||
  Petr Kovar [cs], Chao-Hsiung Liao [zh_HK,zh_TW], Andika Triwidada [id]
 | 
			
		||||
 | 
			
		||||
3.5.92
 | 
			
		||||
======
 | 
			
		||||
* Login/UnlockDialog: Don't reset immediately if auth fails [Giovanni; #682544]
 | 
			
		||||
* Allow changing session mode at runtime [Jasper, Giovanni; #683156]
 | 
			
		||||
* Add zoom out animation on login [Jasper; #683170]
 | 
			
		||||
* Bluetooth: don't restrict the length of non numeric PINs [Giovanni; #683356]
 | 
			
		||||
* Force chat notification to stay open when focusing entry [Debarshi; #682236]
 | 
			
		||||
* Make sure the screen is fully locked before suspending [Giovanni; #683448]
 | 
			
		||||
* st-texture-cache: Fix a case of distorted textures [Florian; #683483]
 | 
			
		||||
* popupSubMenu: Fix padding for non-scrolled submenus [Florian; #683009]
 | 
			
		||||
* popupMenu: Fix width changes on submenu open/close [Florian; #683485]
 | 
			
		||||
* boxpointer: Avoid malformed boxpointer arrow [Debarshi; #680077]
 | 
			
		||||
* Change stage background color to grey [Adel; #683514]
 | 
			
		||||
* messageTray: Update style of summary counters [Debarshi; #682891]
 | 
			
		||||
* Don't fail if a legacy tray icon has no WM_CLASS [Giovanni; #683724]
 | 
			
		||||
* PolkitAgent: Fix a crash if there is no avatar [Giovanni; #683707]
 | 
			
		||||
* Hide the a11y menu in the lock screen, but show it in the login screen
 | 
			
		||||
  [Giovanni; #682542]
 | 
			
		||||
* Fix show-apps button dropping off the dash [Florian; #683340]
 | 
			
		||||
* Fix committing strings to shell entries from input method [Florian; #658325]
 | 
			
		||||
* Make IBus display strings consistent with control-center [Rui; #683124]
 | 
			
		||||
* Fix missing short codes for some input sources [Rui; #683613]
 | 
			
		||||
* Remove support for long-press from entry context menus [Jasper; #683509]
 | 
			
		||||
* screenShield: Add box-shadow to the shield [Florian]
 | 
			
		||||
* Don't show a right-click menu for the hotplug source [Jasper; #683438]
 | 
			
		||||
* Fix extension styling [Giovanni; #682128]
 | 
			
		||||
* Fix on-screen keyboard not working with system-modal dialogs
 | 
			
		||||
  [Giovanni; #664309]
 | 
			
		||||
* Fix insensitive styling for popup menu items [Giovanni; #683988]
 | 
			
		||||
* Disable the message tray dwell when the user is interacting [Owen; #683811]
 | 
			
		||||
* Animate going from the unlock dialog to the lock screen [Giovanni; #681143]
 | 
			
		||||
* Autostart fprintd when necessary [Ray; #683131]
 | 
			
		||||
* UnlockDialog: Allow typing before the first PAM question [Giovanni; #681576]
 | 
			
		||||
* Make Return key dismiss screenshield [Ray; #683889]
 | 
			
		||||
* Fix keyboard navigation in the message tray [Florian; #682243]
 | 
			
		||||
* Remove the places & devices search provider [Giovanni; #683506]
 | 
			
		||||
* Enable hot corner while the message tray is up [Florian; #682255]
 | 
			
		||||
* Port screen recorder to new GStreamer vp8enc API [Adel; #684206]
 | 
			
		||||
* Fix fish flickering [Giovanni; #684154]
 | 
			
		||||
* Fix extension ordering with !important [Jasper; #684163]
 | 
			
		||||
* Allow the shell to run without the screenshield [Giovanni; #683060]
 | 
			
		||||
* Add menu items for IBus Anthy's InputMode, TypingMode [Rui; #682314]
 | 
			
		||||
* Improve transition to the login dialog [Jasper; #682428]
 | 
			
		||||
* Keep unlock dialog around until shield animation ends [Florian; #684342]
 | 
			
		||||
* Expose shell keybindings in System Settings [Florian; #671010]
 | 
			
		||||
* Misc. bugfixes and cleanups [Debarshi, Florian, Giovanni, Jasper, Rico, Rui;
 | 
			
		||||
  #672790, #677434, #683305, #683357, #683369, #683377, #683378, #683400,
 | 
			
		||||
  #683449, #683472, #683482, #683487, #683488, #683526, #683529, #683546,
 | 
			
		||||
  #683583, #683628, #683705, #683982, #683989, #684035, #684036, #684040,
 | 
			
		||||
  #684162, #684214, #684343]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Adel Gadllah, Rui Matos, Florian Müllner, Debarshi Ray,
 | 
			
		||||
  Jasper St. Pierre, Ray Strode, Owen Taylor, Rico Tzschichholz
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Gabor Kelemen [hu], Piotr Drąg [pl], Khaled Hosny [ar],
 | 
			
		||||
  Мирослав Николић [sr, sr@latin], Chao-Hsiung Liao [zh_HK, zh_TW],
 | 
			
		||||
  Bruce Cowan [en_GB], Dirgita [id], Tom Tryfonidis [el], Timo Jyrinki [fi],
 | 
			
		||||
  Adorilson Bezerra [pt_BR], Arash Mousavi [fa], Matej Urbančič [sl],
 | 
			
		||||
  Christian Kirbach [de], Yaron Shahrabani [he], Ihar Hrachyshka [be],
 | 
			
		||||
  Changwoo Ryu [ko], Duarte Loreto [pt], Theppitak Karoonboonyanan [th],
 | 
			
		||||
  Nilamdyuti Goswami [as], Sandeep Sheshrao Shedmake [mr],
 | 
			
		||||
  Alexandre Franke [fr], Ivaylo Valkov [bg], tuhaihe [zh_CN],
 | 
			
		||||
  Yuri Myasoedov [ru], Aurimas Černius [lt], Andika Triwidada [id],
 | 
			
		||||
  Rajesh Ranjan [hi], Sweta Kothari [gu], Daniel Mustieles [es],
 | 
			
		||||
  Fran Diéguez [gl], Praveen Illa [te]
 | 
			
		||||
 | 
			
		||||
3.5.91
 | 
			
		||||
======
 | 
			
		||||
* Improve modal dialog styling of network secret prompts [Jasper; #682412]
 | 
			
		||||
* Fix visibility of non-active workspaces during overview transition
 | 
			
		||||
  [Florian; #682002]
 | 
			
		||||
* Improve scrollbar theming [Cosimo; #682476]
 | 
			
		||||
* Make sure the app menu remains hidden in locked state [Florian; #682475]
 | 
			
		||||
* Add tooltip to show-applications icon [Jasper; #682445]
 | 
			
		||||
* Do not add duplicate remote search providers [Florian; #682470]
 | 
			
		||||
* Handle 'popup-menu' signal on summary items [Florian; #682486]
 | 
			
		||||
* Fix dwelling during mouse-down [Owen; #682385]
 | 
			
		||||
* Set label actor for endSessionDialog.ListItem [Alejandro; #677503]
 | 
			
		||||
* Don't match on comments when searching applications [Florian; #682529]
 | 
			
		||||
* Make workspace selector more similar to the mockup [Stefano; #662087]
 | 
			
		||||
* Fix extension installation and reloading [Jasper; #682578]
 | 
			
		||||
* Hide removable devices in the lock screen [Giovanni; #681143]
 | 
			
		||||
* Reset cancellable after hitting Escape on login screen [Alban; #681537]
 | 
			
		||||
* Fix suspend from the user menu [Giovanni; #682746]
 | 
			
		||||
* Set label actor for summary items in message tray [Alejandro; #677229]
 | 
			
		||||
* Set label for the "Show applications" dash button [Alejandro; #682366]
 | 
			
		||||
* Load extensions as late as possible [Jasper; #682822]
 | 
			
		||||
* Improve mount operation dialogs [Jon; #682645]
 | 
			
		||||
* Remove "Connect to ..." item from places search [Florian; #682817]
 | 
			
		||||
* Don't auto-expand notifications with actions [Giovanni; #682738]
 | 
			
		||||
* Add a new lock screen menu to combine volume network and power
 | 
			
		||||
  [Giovanni; #682540]
 | 
			
		||||
* Add support for pre-edit to StIMText [Daiki; #664041]
 | 
			
		||||
* Remove StIconType [Jasper, Florian, Rui, Giovanni, Debarshi; #682540]
 | 
			
		||||
* Use monitor geometry for dwelling [Florian; #683044]
 | 
			
		||||
* Add support for surrounding-text to StIMText [Daiki; #683015]
 | 
			
		||||
* Improve the placement and style of the "No results" text [Jasper; #683135]
 | 
			
		||||
* Remove broken network device activation policy [Giovanni; #683136]
 | 
			
		||||
* Hide power status icon when no battery is present [Tim; #683080]
 | 
			
		||||
* Ensure summary items are square and have spacing [Debarshi; #682248]
 | 
			
		||||
* Fix close buttons overlapping screen edge [Debarshi; #682343]
 | 
			
		||||
* Escape the tray when a legacy icon is clicked [Giovanni; #682244]
 | 
			
		||||
* Update arrow in the screen shield to match latest mockups [Giovanni; #682285]
 | 
			
		||||
* Allow lifting the screen shield with the mouse wheel [Giovanni; #683164]
 | 
			
		||||
* Make sure to show the app menu after unlocking the screen [Jasper; #683154]
 | 
			
		||||
* Misc bug fixes and cleanups [Debarshi, Florian, Giovanni, Jasper, Rui;
 | 
			
		||||
  #582650, #667439, #682238, #682268, #682429, #682455, #682544, #682546,
 | 
			
		||||
  #682683, #682710, #682998, #683073, #683137, #683156]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Alban Browaeys, Giovanni Campagna, Cosimo Cecchi, Stefano Facchini,
 | 
			
		||||
  Adel Gadllah, Tim Lunn, Rui Matos, William Jon McCann, Florian Müllner,
 | 
			
		||||
  Alejandro Piñeiro, Debarshi Ray, Jasper St. Pierre, Owen Taylor, Daiki Ueno
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Piotr Drąg [pl], Takayuki KUSANO [ja], Kjartan Maraas [nb],
 | 
			
		||||
  Aurimas Černius [lt], Daniel Mustieles [es], Yuri Myasoedov [ru],
 | 
			
		||||
  Khaled Hosny [ar], Yaron Shahrabani [he], Tom Tryfonidis [el],
 | 
			
		||||
  Nilamdyuti Goswami [as], Fran Diéguez [gl], Nguyễn Thái Ngọc Duy [vi],
 | 
			
		||||
  A S Alam [pa], Dr.T.Vasudevan [ta], Luca Ferretti [it]
 | 
			
		||||
 | 
			
		||||
3.5.90
 | 
			
		||||
======
 | 
			
		||||
* Use symbolic icons for workspace switch OSD [Jon; #680738]
 | 
			
		||||
* Lock screen improvements:
 | 
			
		||||
  - Hide user menu and a11y menu in the screen lock [Giovanni; #681143]
 | 
			
		||||
  - Bump the lock screen slightly when pressing a key [Giovanni; #681143]
 | 
			
		||||
  - Constrain vertical movement of the screen shield [Giovanni; #681143]
 | 
			
		||||
  - Return to lock screen on idle [Giovanni; #682041]
 | 
			
		||||
  - Unlock screen automatically after fast-user switching [Giovanni; #682096]
 | 
			
		||||
  - Fix "other user" label [Ray; #681750]
 | 
			
		||||
* Constrain content of system modals to primary monitor [#681743]
 | 
			
		||||
* Respect automatic lock setting on suspend/user-switch [Giovanni; #680231]
 | 
			
		||||
* Improve styling of keyring prompt [Jasper; #681821]
 | 
			
		||||
* Do not hard-code <super> as overlay-key [Florian; #665547]
 | 
			
		||||
* Update style of attached modal dialogs [Florian; #681601]
 | 
			
		||||
* a11y: allow navigation on non reactive items [Alejandro; #667439, #667439]
 | 
			
		||||
* Implement mode-less overview design [Joost, Florian; #682109]
 | 
			
		||||
* Implement message-tray redesign:
 | 
			
		||||
  - Restyle the message tray [Ana, Allan, Florian; #677213, #682342]
 | 
			
		||||
  - Move the desktop upwards when showing the tray [Debarshi; #681392]
 | 
			
		||||
  - Add a close button to notifications [Ana, Jasper; #682253]
 | 
			
		||||
  - Add a keybinding to toggle the tray [Debarshi; #681392]
 | 
			
		||||
  - Make the tray keyboard navigable [Debarshi; #681519]
 | 
			
		||||
  - Add dwelling at the bottom of the screen to open the tray [Owen; #682310]
 | 
			
		||||
  - Don't time out banners when the user is inactive [Marina, Jasper]
 | 
			
		||||
  - Misc fixes and cleanups [Jasper, Marina]
 | 
			
		||||
* Fix showing "Next Week" on Sundays [Sebastian; #682198]
 | 
			
		||||
* Delay restoring IM presence until the network comes up [Florian; #677982]
 | 
			
		||||
* Display enterprise login hint [Ray; #681975]
 | 
			
		||||
* Ignore unrecognized/irrelevant network devices/connections [Dan; #682364]
 | 
			
		||||
* Misc bug fixes and cleanups: [Dan, Florian, Jasper, Jiro, Piotr, Rico;
 | 
			
		||||
  #643687, #682045, #682189]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Allan Day, Piotr Drąg, William Jon McCann,
 | 
			
		||||
  Sebastian Keller, Jiro Matsuzawa, Florian Müllner, Alejandro Piñeiro,
 | 
			
		||||
  Debarshi Ray, Ana Risteska, Jasper St. Pierre, Ray Strode, Owen Taylor,
 | 
			
		||||
  Rico Tzschichholz, Joost Verdoorn, Dan Winship, Marina Zhurakhinskaya
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Nilamdyuti Goswami [as], Daniel Mustieles [es], Yaron Shahrabani [he],
 | 
			
		||||
  Chao-Hsiung Liao [zh_HK, zh_TW], Tobias Endrigkeit [de], A S Alam [pa],
 | 
			
		||||
  Sandeep Sheshrao Shedmake [mr], Fran Diéguez [gl],
 | 
			
		||||
  Мирослав Николић [sr, sr@latin]
 | 
			
		||||
 | 
			
		||||
3.5.5
 | 
			
		||||
=====
 | 
			
		||||
* Update style to match mockups [Allan]
 | 
			
		||||
  - improve calendar layout and legibility
 | 
			
		||||
  - update notifications and menus
 | 
			
		||||
  - use a common style for entries
 | 
			
		||||
  - update scrollbars to match GTK+
 | 
			
		||||
  - improve clock/unlock button in lock screen
 | 
			
		||||
  - update polkit dialogs [Jasper]
 | 
			
		||||
* Fix login dialog growing when selecting different users [Florian; #675076]
 | 
			
		||||
* Implement screen lock in the shell [Giovanni]
 | 
			
		||||
  - restructure login code to be shared with session unlock [#619955]
 | 
			
		||||
  - add initial screen shield / unlock dialog implementation [#619955]
 | 
			
		||||
  - implement (optional) notification list on lock shield [#619955]
 | 
			
		||||
  - update login dialog style to match lock screen [#619955]
 | 
			
		||||
  - filter notifications to only show new ones on the screen lock [#681143]
 | 
			
		||||
  - make notifications scrollable if necessary [#681143]
 | 
			
		||||
  - use correct application names in notifications [#681143]
 | 
			
		||||
  - allow to return to the shield by pressing Escape [#681143]
 | 
			
		||||
* Minor login dialog improvements [Florian]
 | 
			
		||||
  - update style to match the overall visuals [#660913]
 | 
			
		||||
  - indicate whether users are logged in [#658185]
 | 
			
		||||
* Add support for background-repeat CSS property [Jasper; #680801]
 | 
			
		||||
* Add :active pseudo class on scroll handles [Florian]
 | 
			
		||||
* Remove markup from translated strings [Matthias; #681270]
 | 
			
		||||
* Misc bug fixes and cleanups: [Alban, Florian, Giovanni, Jasper, Jeremy,
 | 
			
		||||
  Matthias, Piotr; #677893, #679944, #680064, #680170, #680216, #680426,
 | 
			
		||||
  #681101, #681382]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Jeremy Bicha, Alban Browaeys, Giovanni Campagna, Matthias Clasen, Allan Day,
 | 
			
		||||
  Piotr Drąg , Florian Müllner, Jasper St. Pierre
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Matej Urbančič [sl], Tom Tryfonidis [el], Yaron Shahrabani [he],
 | 
			
		||||
  Kjartan Maraas [nb], Baurzhan Muftakhidinov [kk], Praveen Illa [te],
 | 
			
		||||
  Khaled Hosny [ar], Daniel Mustieles [es], Gabor Kelemen [hu],
 | 
			
		||||
  Fran Diéguez [gl], Sweta Kothari [gu], Aleksej Kabanov [ru],
 | 
			
		||||
  Nilamdyuti Goswami [as], Arash Mousavi [fa], Мирослав Николић [sr, sr@latin]
 | 
			
		||||
 | 
			
		||||
3.5.4
 | 
			
		||||
=====
 | 
			
		||||
* Fix wrong result handling of remote calls [Florian; #678852]
 | 
			
		||||
* 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
 | 
			
		||||
  reentrancy in the icon loading code [Jasper; #673512]
 | 
			
		||||
* Don't show system and other disabled users in the GDM user list
 | 
			
		||||
  [Adel; #673784]
 | 
			
		||||
* Make gnome-shell-calendar-server initialize GTK+ so it can display
 | 
			
		||||
  password prompts if needed [#673608; Owen, Rico]
 | 
			
		||||
* Adapt to Mutter API change for keybinding addition [Florian; #673014]
 | 
			
		||||
* Fix crash when an extension was installed as both a user extension
 | 
			
		||||
  and a system extension [#673613; Jasper]
 | 
			
		||||
* Fix bug where chat entry could end up partially offscreen [Joost, 661944]
 | 
			
		||||
* Fix problem where icons weren't updating when theme was changed
 | 
			
		||||
  [#672941; Florian]
 | 
			
		||||
* Look for Evolution calendar settings in GSettings, not GConf [#673610; Owen]
 | 
			
		||||
* Add <super>F10 for the application menu [#672909; Florian]
 | 
			
		||||
* Fix %Id format characters to work in translations [#673106; Cosimo]
 | 
			
		||||
  (were already used in fa translation)
 | 
			
		||||
* Fix error when NetworkManager restarts [#673043; Giovanni]
 | 
			
		||||
* Improve efficiency of overview redraws by working around Clutter issue
 | 
			
		||||
  [Stefano; #670636]
 | 
			
		||||
* Misc bug fixes [Florian, Giovanni, Jasper, Rui, Stefano;
 | 
			
		||||
  #672592, #672641, #672719, #673187, #673233, #673656]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
 Giovanni Campagna, Cosimo Cecchi, Stefano Facchini, Adel Gadllah, Rui Matos,
 | 
			
		||||
 Florian Müllner, Jasper St. Pierre, Owen Taylor, Rico Tzschichholz,
 | 
			
		||||
 Joost Verdoorn
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
 Khaled Hosny [ar], Ihar Hrachyshka [be], Alexander Shopov [bg], Gil Forcada,
 | 
			
		||||
 Jordi Serratosa [ca], Petr Kovar [cs], Bruce Cowan [en_GB],
 | 
			
		||||
 Carles Ferrando [ca@valencia], Wolfgang Stöggl [de], Daniel Mustieles [es],
 | 
			
		||||
 Arash Mousavi [fa], Bruno Brouard [fr], Fran Diéguez [gl],
 | 
			
		||||
 Sweta Kothari [gu], Yaron Shahrabani [he], Gabor Kelemen [hu],
 | 
			
		||||
 Shankar Prasad [kn], Žygimantas Beručka [lt], Rudolfs Mazurs [lv],
 | 
			
		||||
 Sandeep Sheshrao Shedmake [mr], Kjartan Maraas [nb], Piotr Drąg [pl],
 | 
			
		||||
 Yuri Myasoedov [ru], Daniel Nylander [se], Matej Urbančič [sl],
 | 
			
		||||
 Miroslav Nikolić [sr], Sasi Bhushan, Praveen Illa [te], Yinghua Wang [zh_CN]
 | 
			
		||||
 | 
			
		||||
3.4.0
 | 
			
		||||
=====
 | 
			
		||||
* Don't crash when taking screenshots [Jasper; #672775]
 | 
			
		||||
* Fix dialog-resizing problem [Florian; #672543]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
 Florian Müllner, Jasper St. Pierre
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
 Khaled Hosny, Abderrahim Kitouni [ar], Ihar Hrachyshka [be],
 | 
			
		||||
 Alexander Shopov [bg], Marek Černocký [cs], Jiri Grönroos, Timo Jyrinki [fi],
 | 
			
		||||
 Bruno Brouard [fr], Fran Diéguez [gl], Yaron Shahrabani [he],
 | 
			
		||||
 Gabor Kelemen [hu], Jiro Matsuzawa [ja], Kenneth Nielsen [dk],
 | 
			
		||||
 Mattias Põldaru [et], Changwoo Ryu [ko], Rudolfs Mazurs [lv],
 | 
			
		||||
 Jonh Wendell [pt_BR], Yuri Myasoedov[ru], Daniel Korostil [uk],
 | 
			
		||||
 Nguyễn Thái Ngọc Duy [vi], Chao-Hsiung Liao [zh_HK, zh_TW]
 | 
			
		||||
 | 
			
		||||
3.3.92
 | 
			
		||||
======
 | 
			
		||||
* Add shell-dialogs for GNOME Keyring prompts [Stef; #652459, #652460, #671034]
 | 
			
		||||
* When the user returns from idle, bring up the message tray if there were
 | 
			
		||||
  messages while they were away [Marina; #643014]
 | 
			
		||||
* https://live.gnome.org/EveryDetailMatters
 | 
			
		||||
  - Make the workspace thumbnails clickable all the way to the edge of the
 | 
			
		||||
    screen [Stefano; #643319]
 | 
			
		||||
  - Don't slide out the workspace thumbnails if the mouse is over them when
 | 
			
		||||
    entering the overview [Joost, #651092]
 | 
			
		||||
  - Fix placeholder jumps while dragging a dash item [Joost; #651842]
 | 
			
		||||
  - Don't favorite apps if they are dropped back at the same position
 | 
			
		||||
    [Jean-Philippe; #656333]
 | 
			
		||||
  - To avoid confusion, don't allow removing running apps from favorites
 | 
			
		||||
    [Florian; #644853]
 | 
			
		||||
  - Fix creation of new workspaces by dragging application launchers
 | 
			
		||||
    [Stefano; #664202]
 | 
			
		||||
  - Make it easier to drag dash items without triggering the menu
 | 
			
		||||
    [Florian; #637103]
 | 
			
		||||
* Accessibility [Alejandro]
 | 
			
		||||
  - Add StWidget API for easily adding accessible states and setting roles,
 | 
			
		||||
    names [#668366, #667432, #671378]
 | 
			
		||||
  - Set accessibility information on UI elements
 | 
			
		||||
    [#644255, #667432, #668361, #672047, #670308, #670312, #670719, #671404]
 | 
			
		||||
* Improve key-navigation in the overview [Rui, Florian; #663901]
 | 
			
		||||
* Key navigation bug fixes [Rui, Florian; #662493, #663437, #665215, #671998]
 | 
			
		||||
* Honor a 'org.gnome.shell.overrides.dynamic-workspaces' setting that
 | 
			
		||||
  determines whether the workspace count is dynamic and unsaved in GSettings
 | 
			
		||||
  or static and saved. [Florian; #671568]
 | 
			
		||||
* Avoid saving user presence to GSettings when not necessary
 | 
			
		||||
  [Florian; #665701, #668214]
 | 
			
		||||
* Save screencasts in the users Videos/ directory [Adel; #670749]
 | 
			
		||||
  Use a "human readable" filename [Florian, Adel, Ray; #670753]
 | 
			
		||||
* Allow dragging from the empty part of the top panel to unmaximize a window
 | 
			
		||||
  [Florian; #666359]
 | 
			
		||||
* Fix hangs that could occur when switching away to a VT [Ray; #653833]
 | 
			
		||||
* Fix problems with installing from extensions.gnome.org [Giovanni; #671134]
 | 
			
		||||
* Fix locking the screen when suspending via menu [David, Gert; #670820]
 | 
			
		||||
* Fix browser plugin with Konqueror and Opera [Jasper]
 | 
			
		||||
* Fix shell restart not to bring up failure screen [Giovanni; #648384]
 | 
			
		||||
* Reorganize and clean up CSS theming [Allan; #668209]
 | 
			
		||||
* Improve appearance of modal dialogs [Allan, Florian; #670227, #668209]
 | 
			
		||||
* Update the calendar code to use ECalClient [Giovanni; #671177]
 | 
			
		||||
* Update jhbuild script to use the main moduleset [Owen, Will; #668440]
 | 
			
		||||
* StTextureCache: code cleanup, evict unused icons, merge together
 | 
			
		||||
  simulataneous requests for the same icon [Jasper; #670771, #671656, #672273]
 | 
			
		||||
* Clean up St for recent Clutter changes and fix bugs. StContainer and
 | 
			
		||||
  StGroup are removed [Jasper, Florian; #670034, #670640, #670904]
 | 
			
		||||
* Code cleanup [Adel, Jasper, Rui; #613194, #671086, #671103]
 | 
			
		||||
* Misc bug fixes
 | 
			
		||||
  [Adel, Colin G, Cosimo, Florian, Giovanni, Jasper, Marius, Rui, Stefano;
 | 
			
		||||
  #651130, #658946, #667552, #670076, #671001, #670979, #671410, #671411,
 | 
			
		||||
  #671556, #671656, #671657, #672011, #672024, #672240, #672265, #672270,
 | 
			
		||||
  #672321, #672326, #672413, #672471]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
 Jean-Philippe Braun, Giovanni Campagna, Cosimo Cecchi, Allan Day,
 | 
			
		||||
 Stefano Facchini, David Foerster, Adel Gadllah, Marius Gedminas,
 | 
			
		||||
 Colin Guthrie, Gert Michael Kulyk, William Lachance, Rui Matos,
 | 
			
		||||
 Florian Müllner, Alejandro Piñeiro, Jan Alexander Steffens,
 | 
			
		||||
 Jasper St. Pierre, Ray Strode, Owen Taylor, Joost Verdoorn, Stef Walter,
 | 
			
		||||
 Marina Zhurakhinskaya
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
 Nilamdyuti Goswami [as], Ihar Hrachyshka, Kasia Bondarava [be],
 | 
			
		||||
 Alexander Shopov, Ivaylo Valkov [bg], Gil Forcada [ca], Marek Černocký [cs],
 | 
			
		||||
 Mario Blättermann [de], Kris Thomsen [dk], Bruce Cowan [en_GB],
 | 
			
		||||
 Kristjan Schmidt [eo], Daniel Mustieles [es], Mattias Põldaru [et],
 | 
			
		||||
 Inaki Larranaga Murgoitio [eu], Arash Mousavi [fa], Timo Jyrinki [fi],
 | 
			
		||||
 Bruno Brouard [fr], Fran Diéguez [gl], Sweta Kothari [gu],
 | 
			
		||||
 Yaron Shahrabani [he], Gabor Kelemen [hu], Jiro Matsuzawa [ja],
 | 
			
		||||
 Baurzhan Muftakhidinov [kk], Seong-ho Cho [ko], Žygimantas Beručka [lt],
 | 
			
		||||
 Anita Reitere [lv], Anish A, Praveen Arimbrathodiyil, Mohammed Sadiq [ml],
 | 
			
		||||
 fKjartan Maraas [nb], Wouter Bolsterlee [nl], A S Alam [pa], Piotr Drąg [pl],
 | 
			
		||||
 Duarte Loreto [pt], Jonh Wendell [pt_BR], Yuri Myasoedov [ru],
 | 
			
		||||
 Matej Urbančič [sl], Miroslav Nikolić [sr], Tirumurti Vasudevan [ta],
 | 
			
		||||
 Sasi Bhushan, Krishnababu Krothapalli [te], Daniel Korostil [uk],
 | 
			
		||||
 Nguyễn Thái Ngọc Duy [vi], YunQiang Su, Yinghua Wang [zh_CN],
 | 
			
		||||
 Chao-Hsiung Liao [zh_HK, zh_TW]
 | 
			
		||||
 | 
			
		||||
3.3.90
 | 
			
		||||
======
 | 
			
		||||
 | 
			
		||||
* Allow other applications to implement search providers via D-Bus
 | 
			
		||||
  [Florian; #663125, #670148]
 | 
			
		||||
* Remove "Recent Items" search, as replaced by Documents search
 | 
			
		||||
  [Florian; #663125]
 | 
			
		||||
* Allow NetworkManager VPN plugins to use a shell-themed dialog
 | 
			
		||||
  [Giovanni; #658484]
 | 
			
		||||
* Port away from deprecated Clutter API [Jasper, Florian, Adel; #670034]
 | 
			
		||||
  - StTooltip is removed
 | 
			
		||||
  - StWidget is now a concrete class and can be instantiated
 | 
			
		||||
  - st_container_destroy_children(), st_box_layout_insert_actor(),
 | 
			
		||||
    and other functions removed in favor of new replacements in Clutter.
 | 
			
		||||
* Use systemd for console/session handling when available [Lennart]
 | 
			
		||||
* Visual improvements to contact search, padding, top panel, checkboxes
 | 
			
		||||
  [Allan, Florian, Jakub; #669489, #669811, #669993]
 | 
			
		||||
* Add a include_cursor parameter to Screenshot and ScreenshotWindow
 | 
			
		||||
  D-Bus methods [Adel; #670086]
 | 
			
		||||
* Add a "FlashArea" D-Bus method to do the screenshot flash without a
 | 
			
		||||
  screenshot [Adel; #669660]
 | 
			
		||||
* Build fixes [Adel, Giovanni, Jasper; #658484, #669637]
 | 
			
		||||
* Misc bug fixes [Adel, Florian, Giovanni, Guillaume, Jasper, Jeff,
 | 
			
		||||
  Marc-Antoine, Stef, Stefano, Will; #642135, #658484, #658908, #667694,
 | 
			
		||||
  #669098, #669921, #669662, #669694, #669776, #669887, #669921, #670005,
 | 
			
		||||
  #670006, #670319, #670418, #670489]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
 Giovanni Campagna, Cosimo Cecchi, Allan Day, Guillaume Desmottes, Jeff Epler,
 | 
			
		||||
 Stefano Facchini, Adel Gadllah, Florian Müllner, Marc-Antoine Perennou,
 | 
			
		||||
 Jasper St. Pierre, Lennart Poettering, Jakub Steiner, Jasper St. Pierre,
 | 
			
		||||
 Will Thompson, Stef Walter
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
 Ihar Hrachyshka [be], Marek Černocký, Adam Matoušek [cs],
 | 
			
		||||
 Kenneth Nielsen [dk], Daniel Mustieles [es], Mattias Põldaru [et],
 | 
			
		||||
 Fran Diéguez [gl], Yaron Shahrabani [he], Luca Ferretti [it],
 | 
			
		||||
 Baurzhan Muftakhidinov [kk], Aurimas Černius [lt], Kjartan Maraas [nb],
 | 
			
		||||
 A S Alam [pa], Matej Urbančič [sl], Miroslav Nikolić [sr],
 | 
			
		||||
 Praveen Illa [te], Chao-Hsiung Liao [zh_HK, zh_TW]
 | 
			
		||||
 | 
			
		||||
3.3.5
 | 
			
		||||
=====
 | 
			
		||||
 | 
			
		||||
* Extension system: [Jasper; #668429]
 | 
			
		||||
  http://blog.mecheye.net/2012/02/more-extension-api-breaks/
 | 
			
		||||
 - Add a 'gnome-shell-extension-prefs' application for displaying extension
 | 
			
		||||
   preferences as provided by the extension in a prefs.js file.
 | 
			
		||||
 - Allow launching gnome-shell-extension-prefs from extensions.gnome.org
 | 
			
		||||
   throuhg the browser plugin.
 | 
			
		||||
 - Add ExtensionUtils.getCurrentExtension() for an extension to get a
 | 
			
		||||
   handle to an extension object, to get local imports or paths.
 | 
			
		||||
 - Add an onshellrestart callback to the browser plugin [Jasper; #668517]
 | 
			
		||||
* Screenshots:
 | 
			
		||||
  - Move the screenshot "flash" to the shell [Cosimo; #668618]
 | 
			
		||||
  - Move saving screenshots to a thread [Adel; #652952]
 | 
			
		||||
  - Correctly screenshot rounded decorations [Jasper; #662486]
 | 
			
		||||
* Screen recorder:
 | 
			
		||||
  - Change the default pipeline to favor speed over quality [Owen; #669066]
 | 
			
		||||
  - Drop frames to keep from running the user out of memory [Owen; #669066]
 | 
			
		||||
* Work around a slow implementation of glReadPixels() in the Intel drivers,
 | 
			
		||||
  improving performance for screenshots and the screen recorder.
 | 
			
		||||
  [Owen; #669065]
 | 
			
		||||
* Use Keywords: field in desktop files when search for applications
 | 
			
		||||
  [Florian; #609702]
 | 
			
		||||
* Strip debian- when matching desktop file names [Jasper; #665647]
 | 
			
		||||
* Fix up various problems from CSS background size-addition
 | 
			
		||||
  [Florian, Jasper; #668430, #633462]
 | 
			
		||||
* UI tweaks and behavior fixes
 | 
			
		||||
  [Florian, Giovanni, Stefano; #643867, #666197, #664622]
 | 
			
		||||
* Some improvements to exported accessibility information  [Alejando; #667376]
 | 
			
		||||
* Don't show contacts without IM information as offline [Florian; #662685]
 | 
			
		||||
* Don't change status from hidden to extended_away when going idle
 | 
			
		||||
  [Florian; #642408]
 | 
			
		||||
* Cleanups [Emmanuele, Jasper; #662152, #669239]
 | 
			
		||||
* Misc bug fixes [Cosimo, Dan, Florian, Jasper, Rui, Stefano;
 | 
			
		||||
  #633462, #643867, #662213, #662386, #662747, #665000, #665017, #665322,
 | 
			
		||||
  #667371, #667860, #668020, #668517, #668541, #669236]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
 Emmanuele Bassi, Giovanni Campagna, Cosimo Cecchi, Stefano Facchini,
 | 
			
		||||
 Adel Gadllah, Rui Matos, Florian Müllner, Alejandro Piñeiro,
 | 
			
		||||
 Jasper St. Pierre, Owen Taylor, Dan Winship
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
 Daniel Mustieles [es], Timo Jyrinki [fi], Seán de Búrca [ga],
 | 
			
		||||
 Fran Diéguez [gl], Kjartan Maraas [nb], Wouter Bolsterlee [nl],
 | 
			
		||||
 Danishka Navin [si], Yaron Shahrabani [he], Matej Urbančič [sl],
 | 
			
		||||
 Chao-Hsiung Liao [zh_HK, zh_TW]
 | 
			
		||||
 | 
			
		||||
3.3.4
 | 
			
		||||
=====
 | 
			
		||||
* https://live.gnome.org/EveryDetailMatters
 | 
			
		||||
  - Add "browse" for labels for dash items - once a tooltip is
 | 
			
		||||
    showing, switch to other items without a delay [Seif; #666170]
 | 
			
		||||
  - Always scale down windows in the overview at least to 70% [Vit; #646704]
 | 
			
		||||
  - Fix the new-workspace drop indicator sometimes getting stuck
 | 
			
		||||
    [Stefano; #664201]
 | 
			
		||||
  - Delay rearranging windows in the overview as long as the pointer
 | 
			
		||||
    is over a window [Vit; #645325]
 | 
			
		||||
* Add a GConf => DConf migration file for overriden Mutter settings
 | 
			
		||||
  [Florian; #667636]
 | 
			
		||||
* When a VPN connection is active, show that as the network icon
 | 
			
		||||
  [Giovanni; #665115]
 | 
			
		||||
* Handle the "ExtendedAway" IM status as away, not offline [Guillaume; #667813]
 | 
			
		||||
* Improve the appearance of the labels in "Applications" [Alex; #642392]
 | 
			
		||||
* Adjust for GTK+ and Mutter API changes for application menu [Ryan; #668118]
 | 
			
		||||
* Add section label support to the application menu [Giovanni; #666681]
 | 
			
		||||
* Fix screenshot methods to work again [Cosimo; #667662]
 | 
			
		||||
* Fix several crashers related to updating workspace thumbnails [Owen; #667652]
 | 
			
		||||
* Fix crash when searching in the shell caused by .desktop files
 | 
			
		||||
  without Exec= lines (commonly, nautilus-pastebin) [Ray; #663815]
 | 
			
		||||
* Message Tray:
 | 
			
		||||
 - Fix bug that would cause confusion between notifications from different
 | 
			
		||||
   services running in the gnome-settings-daemon process
 | 
			
		||||
  [Marina, Jasper; #664138]
 | 
			
		||||
 - Don't steal the focus when popping up beneath the pointer [Rui; #661358]
 | 
			
		||||
 - Only pop up the message tray from a hot corner, rather than trapping
 | 
			
		||||
   clicks on the entire bottom row. [Rui; #663366]
 | 
			
		||||
* Browser plugin and extension.gnome.org integration
 | 
			
		||||
  - Fix multiple bugs that kept the plugin from working in WebKit-based browsers
 | 
			
		||||
    [Jasper, #663823, #666444]
 | 
			
		||||
  - Fix plugin crash when the shell is installed but not running [Jürg]
 | 
			
		||||
  - Fix problem that kept plugin uninstallation from working [Jasper]
 | 
			
		||||
* gnome-shell-extension-tool
 | 
			
		||||
  - Fix error that was printed after creating an extension [Jasper; #661623]
 | 
			
		||||
  - Use xdg-open rather than the deprecated gnome-open [Jasper]
 | 
			
		||||
* Don't recreate dash icons unnecessarily - this hopefully fixes various
 | 
			
		||||
  crashes. [Owen, Giovanni; #648450]
 | 
			
		||||
* Fix several bugs that caused errors and slowdowns when updating the
 | 
			
		||||
  network menu. [Giovanni; #651378, #659277, #663278]
 | 
			
		||||
* Fix several crashers related to updating workspace thumbnails
 | 
			
		||||
  [Owen; #667652]
 | 
			
		||||
* Fix memory management error causing gnome-shell-hotplug-sniffer to crash
 | 
			
		||||
  [Owen; #667378]
 | 
			
		||||
* Build fixes [Emmanuele, Rico; #667864]
 | 
			
		||||
* Code cleanups [Adel; #668087]
 | 
			
		||||
* Misc bug fixes [Colin, Florian, Giovanni, Owen, Xavier; #633028, #658817,
 | 
			
		||||
  #664138, #667881, #668048, #668050]
 | 
			
		||||
* Fix crash and misdrawing with shadowed elements [Owen; #668048, #668050]
 | 
			
		||||
* Fix several small memory leaks. [Jasper, Florian]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
 Emmanuele Bassi, Giovanni Campagna, Cosimo Cecchi, Xavier Claessens,
 | 
			
		||||
 Guillaume Desmottes, Stefano Facchini, Adel Gadllah, Alex Hultman,
 | 
			
		||||
 Ryan Lortie, Seif Lotfy, Florian Müllner, Vit Stanislav, Owen Taylor,
 | 
			
		||||
 Rico Tzschichholz, Colin Walters
 | 
			
		||||
 Jürg Billeter, Rui Matos, Florian Müllner, Jasper St. Pierre, Ray Strode,
 | 
			
		||||
 Owen Taylor, Marina Zhurakhinskaya
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
 Ihar Hrachyshka [be], Alexander Shopov [bg], Arash Mousavi [fa],
 | 
			
		||||
 Jiri Grönroos, Timo Jyrinki [fi], Fran Diéguez [gl], Kjartan Maraas [nb],
 | 
			
		||||
 Yuri Myasoedov [ru], Matej Urbančič [sl], Nguyễn Thái Ngọc Duy [vi]
 | 
			
		||||
 | 
			
		||||
3.3.3
 | 
			
		||||
=====
 | 
			
		||||
* https://live.gnome.org/EveryDetailMatters
 | 
			
		||||
  - Stop flashing the window labels on actions in overview [Zan; #644861]
 | 
			
		||||
  - Improve the look of window captions in the overview [Marc; #664487]
 | 
			
		||||
  - Move dash tooltips beside the icon [Seif, Stefano; #666166]
 | 
			
		||||
* Support application menus exported from applications via new GLib API
 | 
			
		||||
   and D-Bus protocol. [Giovanni, Colin, Matthias, Cosimo]
 | 
			
		||||
* For removable device prompts, show "Open with Rhythmbox], rather
 | 
			
		||||
  than "Open with Rhythmbox Music Player' [Florian; #664561]
 | 
			
		||||
* Switch to activating the message tray only with a hot corner rather
 | 
			
		||||
  than a full row of pixels, allowing mouse events to apps [Rui; #663366]
 | 
			
		||||
* Fully handle the case where the workspaces-only-on-primary
 | 
			
		||||
  GSetting is set to false [Florian; #652580]
 | 
			
		||||
* Add support for background-size CSS property to St [Quentin; #633462]
 | 
			
		||||
* Port to new GJS Lang.Class framework [Giovanni; #664436]
 | 
			
		||||
* Finish port to GDBus [Giovanni; #664436]
 | 
			
		||||
* Stop using the deprecated Clutter default stage [Florian, Jasper; #664052]
 | 
			
		||||
* Fix bugs that kept browser plugin from working in WebKit-based browser
 | 
			
		||||
  [Jasper; #666444]
 | 
			
		||||
* Fix typo that made uninstalling extensions not work [Jasper]
 | 
			
		||||
* Fix crash in browser plugin if shell is not run [Jürg]
 | 
			
		||||
* Reintroduce piscine paschal ovum [Giovanni; #666606]
 | 
			
		||||
* Network menu bug fixes
 | 
			
		||||
  Giovanni; #664124, #665194, #665680, #666429, #666614]
 | 
			
		||||
* Misc bug fixes [Florian, Jasper, Jonny, Marina, Ron; #647587, #659272,
 | 
			
		||||
  #664138, #665261, #666020, #666243]
 | 
			
		||||
* Build fixes [Owen]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
 Jürg Billeter, Giovanni Campagna, Stefano Candori, Cosimo Cecchi,
 | 
			
		||||
 Matthias Clasen, Zan Dobersek, Quentin Glidic, Jonny Lamb, Ryan Lortie,
 | 
			
		||||
 Seif Lotfy, Rui Matos, Florian Müllner, Bastien Nocera, Jasper St. Pierre,
 | 
			
		||||
 Marc Plano-Lesay, Owen Taylor, Colin Walters, Ron Yorsten,
 | 
			
		||||
 Marina Zhurakhinskaya
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
 Petr Kovar [cs], Kris Thomsen [dk], Daniel Mustieles [es],
 | 
			
		||||
 Ville-Pekka Vainio [fi], Yaron Shahrabani [he], Luca Ferretti [it],
 | 
			
		||||
 Hideki Yamane [ja], Žygimantas Beručka [lt], Jovan Naumovski [mk],
 | 
			
		||||
 Kjartan Maraas [nb], "Andreas N" [nn], Lucian Adrian Grijincu [ro],
 | 
			
		||||
 Matej Urbančič [sl], Praveen Illa [te], Muhammet Kara [tr],
 | 
			
		||||
 Daniel Korostil [uk], Aron Xu [zh_CN]
 | 
			
		||||
 | 
			
		||||
3.3.2
 | 
			
		||||
=====
 | 
			
		||||
* Port D-Bus usage in the shell to GDBus [Giovanni, Marc-Antoine, Florian,
 | 
			
		||||
  Jasper, Matthias; #648651, #658078, #663902, #663941]
 | 
			
		||||
* Message tray
 | 
			
		||||
  - Add right-click option to chats to mute the conversation [Ana; #659962]
 | 
			
		||||
  - Don't steal the focus when popping up under the pointer [Rui; #661358]
 | 
			
		||||
* Looking Glass
 | 
			
		||||
  - Add alt-Tab completion [Jason; #661054]
 | 
			
		||||
  - Show errors from extensions in the extensions tab [Jasper; #660546]
 | 
			
		||||
  - Allow switching tabs with <Control>PageUp/PageDown
 | 
			
		||||
  - Theme consistently with the rest of the shell [Jason; 650900]
 | 
			
		||||
* Extension system
 | 
			
		||||
  - Don't try to load disabled extensions at all [Jasper; #661815, #662704]
 | 
			
		||||
  - Enable and disable plugins in a consistent order [Jasper; #661815, #662704]
 | 
			
		||||
  - Add options to enable/disable extensions to gnome-shell-extension-tool
 | 
			
		||||
    [Jasper; #661815]
 | 
			
		||||
* Adapt to Mutter change to GSettings [Florian, Matthias; #663429]
 | 
			
		||||
* Allow creating a new workspace by dragging a window or launcher in the
 | 
			
		||||
  middle of two existing ones [Jasper; #646409]
 | 
			
		||||
* Allow using Alt-Tab while during drag-and-drop and other operations
 | 
			
		||||
  that grab the pointer [Adel; #660457]
 | 
			
		||||
* Do a better job of finding the right user to authenticate
 | 
			
		||||
  as when showing a PolKit dialog [Matthias; #651547]
 | 
			
		||||
* Control the D-Bus Eval() method by the developer-tools GSetting which
 | 
			
		||||
  is used for looking glass and screen recorder. [Jasper; #662891]
 | 
			
		||||
* Fix browser plugin to work under WebKit-based browser [Jasper; #663823]
 | 
			
		||||
* Fix certain stacking issues with alt-Tab [Jasper; #660650]
 | 
			
		||||
* Fixes for GLib deprecations [Jasper; #662011]p
 | 
			
		||||
* Fixes for GTK+ deprecations [Florian, Rico; #662245]p
 | 
			
		||||
* Fixes for Clutter deprecations [Jasper; #662627]
 | 
			
		||||
* Visual improvements and UI tweaks [Florian, Jakub, Jasper;
 | 
			
		||||
  #662800, #658096, #662226]
 | 
			
		||||
* Hard-code "Home" as the name for the home dir, rather than looking
 | 
			
		||||
  it up via GSettings; avoids schema dependency [Cosimo; #559895]
 | 
			
		||||
* Don't show "Switch User" on single user machines [Florian; #657011]
 | 
			
		||||
* Generate documentation for St toolkit [Florian]
 | 
			
		||||
* Improve marking of strings for translation [Matthias, Piotr; #658664]
 | 
			
		||||
* Networking menu bug fixes [Giovanni; #650007, #651378, #659277, #663278]
 | 
			
		||||
* Code cleanups and leak fixes to StTextureCache
 | 
			
		||||
  [Jasper, Florian; #660968, #662998]
 | 
			
		||||
* Code cleanups [Adel, Florian, Jasper; #662238, #663584]
 | 
			
		||||
* Build fixes [Adel, Colin, Florian, Ming Han]
 | 
			
		||||
* Misc bug fixes [Adel, Florian, "Fry", Jasper, Giovanni, Ray, Rui, Stefan;
 | 
			
		||||
  #660520, #661029, #661231, #661623, #661921, #662235, #662236, #662502,
 | 
			
		||||
  #662394, #662799, #662969, #663175, #663277, #663815, #663891, #662967]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
 Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Piotr Drąg, Adel Gadllah,
 | 
			
		||||
 Rui Matos, Florian Müllner, Marc-Antoine Perennou, Ana Risteska,
 | 
			
		||||
 Jason Siefken, Jakub Steiner, Ray Strode, Jasper St. Pierre, Ming Han Teh,
 | 
			
		||||
 Rico Tzschichholz, Colin Walters, Stefan Zwanenburg
 | 
			
		||||
 | 
			
		||||
Translation:
 | 
			
		||||
 Alexander Shopov [bg], Marek Černocký [cs], Mario Blättermann [de],
 | 
			
		||||
 Kostas Papadimas [el], Bruce Cowan [en_GB], Kristjan Schmidt [eo],
 | 
			
		||||
 Jorge González, Daniel Mustieles, Benjamín Valero Espinosa [es],
 | 
			
		||||
 Mattias Põldaru [et], Arash Mousavi [fa], Ville-Pekka Vainio [fi],
 | 
			
		||||
 Fran Diéguez [gl], Yaron Shahrabani [he], Hideki Yamane [ja],
 | 
			
		||||
 Algimantas Margevičius [lt], Kjartan Maraas [nb], Daniel Nylander [se],
 | 
			
		||||
 Matej Urbančič [sl], Praveen Illa [te], Muhammet Kara [tr],
 | 
			
		||||
 Nguyễn Thái Ngọc Duy [vi], Cheng-Chia Tseng [zh_HK, zh_TW]
 | 
			
		||||
 Alexander Shopov [bg], Gil Forcada [ca], Carles Ferrando [ca@valencia],
 | 
			
		||||
 Marek Černocký [cz], Kris Thomsen [dk], Bruce Cowan [en_GB],
 | 
			
		||||
 Kristjan Schmidt [eo], Daniel Mustieles [es], Arash Mousavi [fa],
 | 
			
		||||
 Ville-Pekka Vainio [fi], Bruno Brouard [fr], Hideki Yamane [ja],
 | 
			
		||||
 Žygimantas Beručka, Aurimas Černius [lt], Kjartan Maraas [nb],
 | 
			
		||||
 "Andreas N" [nn], Djavan Fagundes, Antonio Fernandes C. Neto [pt_BR],
 | 
			
		||||
 Daniel Nylander [se], Matej Urbančič [sl], Praveen Illa [te],
 | 
			
		||||
 Nguyễn Thái Ngọc Duy [vi]
 | 
			
		||||
 | 
			
		||||
3.2.1
 | 
			
		||||
=====
 | 
			
		||||
@@ -1025,7 +142,7 @@ Contributors:
 | 
			
		||||
Translations:
 | 
			
		||||
 Friedel Wolff [af], Nilamdyuti Goswami [as], Ihar Hrachyshka [be],
 | 
			
		||||
 Ivaylo Valkov [bg], Gil Forcada [ca], Carles Ferrando [ca@valencia],
 | 
			
		||||
 Petr Kovar [cs], Mario Blättermann [de], Kris Thomsen [dk],
 | 
			
		||||
 Petr Kovar [cz], Mario Blättermann [de], Kris Thomsen [dk],
 | 
			
		||||
 Tiffany Antopolski, Kristjan Schmidt [eo], Daniel Mustieles [es],
 | 
			
		||||
 Inaki Larranaga Murgoitio [eu], Tommi Vainikainen [fi], Bruno Brouard [fr],
 | 
			
		||||
 Fran Dieguez [gl], Yaron Shahrabani [he], Gabor Kelemen [hu],
 | 
			
		||||
 
 | 
			
		||||
@@ -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 1
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  GDBusProxy *proxy;
 | 
			
		||||
@@ -104,7 +104,7 @@ check_origin_and_protocol (NPP instance)
 | 
			
		||||
                          &location))
 | 
			
		||||
    goto out;
 | 
			
		||||
 | 
			
		||||
  if (!NPVARIANT_IS_OBJECT (location))
 | 
			
		||||
  if (!NPVARIANT_IS_OBJECT (document))
 | 
			
		||||
    goto out;
 | 
			
		||||
 | 
			
		||||
  hostname = get_string_property (instance,
 | 
			
		||||
@@ -161,7 +161,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;
 | 
			
		||||
}
 | 
			
		||||
@@ -223,7 +222,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)
 | 
			
		||||
@@ -263,14 +262,11 @@ NPP_Destroy(NPP           instance,
 | 
			
		||||
/* =================== scripting interface =================== */
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  NPObject     parent;
 | 
			
		||||
  NPP          instance;
 | 
			
		||||
  GDBusProxy  *proxy;
 | 
			
		||||
  GSettings   *settings;
 | 
			
		||||
  NPObject    *listener;
 | 
			
		||||
  NPObject    *restart_listener;
 | 
			
		||||
  gint         signal_id;
 | 
			
		||||
  guint        watch_name_id;
 | 
			
		||||
	NPObject     parent;
 | 
			
		||||
	NPP          instance;
 | 
			
		||||
	GDBusProxy  *proxy;
 | 
			
		||||
	NPObject    *listener;
 | 
			
		||||
	gint         signal_id;
 | 
			
		||||
} PluginObject;
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@@ -288,7 +284,7 @@ on_shell_signal (GDBusProxy *proxy,
 | 
			
		||||
      gint32 status;
 | 
			
		||||
      gchar *error;
 | 
			
		||||
      NPVariant args[3];
 | 
			
		||||
      NPVariant result = { NPVariantType_Void };
 | 
			
		||||
      NPVariant result;
 | 
			
		||||
 | 
			
		||||
      g_variant_get (parameters, "(sis)", &uuid, &status, &error);
 | 
			
		||||
      STRINGZ_TO_NPVARIANT (uuid, args[0]);
 | 
			
		||||
@@ -304,28 +300,6 @@ on_shell_signal (GDBusProxy *proxy,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
on_shell_appeared (GDBusConnection *connection,
 | 
			
		||||
                   const gchar     *name,
 | 
			
		||||
                   const gchar     *name_owner,
 | 
			
		||||
                   gpointer         user_data)
 | 
			
		||||
{
 | 
			
		||||
  PluginObject *obj = (PluginObject*) user_data;
 | 
			
		||||
 | 
			
		||||
  if (obj->restart_listener)
 | 
			
		||||
    {
 | 
			
		||||
      NPVariant result = { NPVariantType_Void };
 | 
			
		||||
 | 
			
		||||
      funcs.invokeDefault (obj->instance, obj->restart_listener,
 | 
			
		||||
                           NULL, 0, &result);
 | 
			
		||||
 | 
			
		||||
      funcs.releasevariantvalue (&result);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define SHELL_SCHEMA "org.gnome.shell"
 | 
			
		||||
#define ENABLED_EXTENSIONS_KEY "enabled-extensions"
 | 
			
		||||
 | 
			
		||||
static NPObject *
 | 
			
		||||
plugin_object_allocate (NPP      instance,
 | 
			
		||||
                        NPClass *klass)
 | 
			
		||||
@@ -335,18 +309,9 @@ 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);
 | 
			
		||||
 | 
			
		||||
  obj->watch_name_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
 | 
			
		||||
                                         "org.gnome.Shell",
 | 
			
		||||
                                         G_BUS_NAME_WATCHER_FLAGS_NONE,
 | 
			
		||||
                                         on_shell_appeared,
 | 
			
		||||
                                         NULL,
 | 
			
		||||
                                         obj,
 | 
			
		||||
                                         NULL);
 | 
			
		||||
 | 
			
		||||
  g_debug ("plugin object created");
 | 
			
		||||
 | 
			
		||||
  return (NPObject*)obj;
 | 
			
		||||
@@ -363,22 +328,41 @@ plugin_object_deallocate (NPObject *npobj)
 | 
			
		||||
  if (obj->listener)
 | 
			
		||||
    funcs.releaseobject (obj->listener);
 | 
			
		||||
 | 
			
		||||
  if (obj->watch_name_id)
 | 
			
		||||
    g_bus_unwatch_name (obj->watch_name_id);
 | 
			
		||||
 | 
			
		||||
  g_debug ("plugin object destroyed");
 | 
			
		||||
 | 
			
		||||
  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 get_errors_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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
@@ -442,73 +426,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;
 | 
			
		||||
@@ -532,159 +451,88 @@ 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))
 | 
			
		||||
  gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
 | 
			
		||||
  if (!uuid_is_valid (uuid_str))
 | 
			
		||||
    return FALSE;
 | 
			
		||||
 | 
			
		||||
  uuids = g_settings_get_strv (obj->settings, ENABLED_EXTENSIONS_KEY);
 | 
			
		||||
  length = g_strv_length (uuids);
 | 
			
		||||
 | 
			
		||||
  if (enabled)
 | 
			
		||||
    {
 | 
			
		||||
      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;
 | 
			
		||||
    }
 | 
			
		||||
  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)
 | 
			
		||||
    {
 | 
			
		||||
@@ -697,27 +545,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)
 | 
			
		||||
    {
 | 
			
		||||
@@ -730,26 +581,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);
 | 
			
		||||
@@ -760,29 +616,6 @@ plugin_get_errors (PluginObject    *obj,
 | 
			
		||||
  return jsonify_variant (res, result);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
plugin_launch_extension_prefs (PluginObject    *obj,
 | 
			
		||||
                               uint32_t         argc,
 | 
			
		||||
                               const NPVariant *argv,
 | 
			
		||||
                               NPVariant       *result)
 | 
			
		||||
{
 | 
			
		||||
  gchar *uuid;
 | 
			
		||||
 | 
			
		||||
  if (!parse_args ("u", argc, argv, &uuid))
 | 
			
		||||
    return FALSE;
 | 
			
		||||
 | 
			
		||||
  g_dbus_proxy_call (obj->proxy,
 | 
			
		||||
                     "LaunchExtensionPrefs",
 | 
			
		||||
                     g_variant_new ("(s)", uuid),
 | 
			
		||||
                     G_DBUS_CALL_FLAGS_NONE,
 | 
			
		||||
                     -1, /* timeout */
 | 
			
		||||
                     NULL, /* cancellable */
 | 
			
		||||
                     NULL, /* callback */
 | 
			
		||||
                     NULL /* user_data */);
 | 
			
		||||
 | 
			
		||||
  return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
plugin_get_api_version (PluginObject  *obj,
 | 
			
		||||
                        NPVariant     *result)
 | 
			
		||||
@@ -833,40 +666,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)
 | 
			
		||||
{
 | 
			
		||||
@@ -878,13 +681,53 @@ 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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
@@ -892,7 +735,6 @@ plugin_object_has_property (NPObject     *npobj,
 | 
			
		||||
                            NPIdentifier  name)
 | 
			
		||||
{
 | 
			
		||||
  return (name == onextension_changed_id ||
 | 
			
		||||
          name == onrestart_id ||
 | 
			
		||||
          name == api_version_id ||
 | 
			
		||||
          name == shell_version_id);
 | 
			
		||||
}
 | 
			
		||||
@@ -919,33 +761,6 @@ plugin_object_get_property (NPObject     *npobj,
 | 
			
		||||
      else
 | 
			
		||||
        NULL_TO_NPVARIANT (*result);
 | 
			
		||||
    }
 | 
			
		||||
  else if (name == onrestart_id)
 | 
			
		||||
    {
 | 
			
		||||
      if (obj->restart_listener)
 | 
			
		||||
        OBJECT_TO_NPVARIANT (obj->restart_listener, *result);
 | 
			
		||||
      else
 | 
			
		||||
        NULL_TO_NPVARIANT (*result);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
plugin_object_set_callback (NPObject        **listener,
 | 
			
		||||
                            const NPVariant  *value)
 | 
			
		||||
{
 | 
			
		||||
  if (!NPVARIANT_IS_OBJECT (*value) && !NPVARIANT_IS_NULL (*value))
 | 
			
		||||
    return FALSE;
 | 
			
		||||
 | 
			
		||||
  if (*listener)
 | 
			
		||||
    funcs.releaseobject (*listener);
 | 
			
		||||
  *listener = NULL;
 | 
			
		||||
 | 
			
		||||
  if (NPVARIANT_IS_OBJECT (*value))
 | 
			
		||||
    {
 | 
			
		||||
      *listener = NPVARIANT_TO_OBJECT (*value);
 | 
			
		||||
      funcs.retainobject (*listener);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  return TRUE;
 | 
			
		||||
}
 | 
			
		||||
@@ -957,13 +772,25 @@ plugin_object_set_property (NPObject        *npobj,
 | 
			
		||||
{
 | 
			
		||||
  PluginObject *obj;
 | 
			
		||||
 | 
			
		||||
  obj = (PluginObject *)npobj;
 | 
			
		||||
  if (!plugin_object_has_property (npobj, name))
 | 
			
		||||
    return FALSE;
 | 
			
		||||
 | 
			
		||||
  if (name == onextension_changed_id)
 | 
			
		||||
    return plugin_object_set_callback (&obj->listener, value);
 | 
			
		||||
    {
 | 
			
		||||
      obj = (PluginObject*) npobj;
 | 
			
		||||
      if (obj->listener)
 | 
			
		||||
        funcs.releaseobject (obj->listener);
 | 
			
		||||
 | 
			
		||||
  if (name == onrestart_id)
 | 
			
		||||
    return plugin_object_set_callback (&obj->restart_listener, value);
 | 
			
		||||
      obj->listener = NULL;
 | 
			
		||||
      if (NPVARIANT_IS_OBJECT (*value))
 | 
			
		||||
        {
 | 
			
		||||
          obj->listener = NPVARIANT_TO_OBJECT (*value);
 | 
			
		||||
          funcs.retainobject (obj->listener);
 | 
			
		||||
          return TRUE;
 | 
			
		||||
        }
 | 
			
		||||
      else if (NPVARIANT_IS_NULL (*value))
 | 
			
		||||
        return TRUE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  return FALSE;
 | 
			
		||||
}
 | 
			
		||||
@@ -997,9 +824,7 @@ init_methods_and_properties (void)
 | 
			
		||||
  install_extension_id = funcs.getstringidentifier ("installExtension");
 | 
			
		||||
  uninstall_extension_id = funcs.getstringidentifier ("uninstallExtension");
 | 
			
		||||
  get_errors_id = funcs.getstringidentifier ("getExtensionErrors");
 | 
			
		||||
  launch_extension_prefs_id = funcs.getstringidentifier ("launchExtensionPrefs");
 | 
			
		||||
 | 
			
		||||
  onrestart_id = funcs.getstringidentifier ("onshellrestart");
 | 
			
		||||
  onextension_changed_id = funcs.getstringidentifier ("onchange");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1028,12 +853,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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										143
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						@@ -1,5 +1,5 @@
 | 
			
		||||
AC_PREREQ(2.63)
 | 
			
		||||
AC_INIT([gnome-shell],[3.7.2.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
 | 
			
		||||
AC_INIT([gnome-shell],[3.2.2.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])
 | 
			
		||||
@@ -36,7 +36,9 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
 | 
			
		||||
 | 
			
		||||
PKG_PROG_PKG_CONFIG([0.22])
 | 
			
		||||
 | 
			
		||||
AC_PATH_PROG([XSLTPROC], [xsltproc])
 | 
			
		||||
# GConf stuff
 | 
			
		||||
AC_PATH_PROG(GCONFTOOL, gconftool-2, no)
 | 
			
		||||
AM_GCONF_SOURCE_2
 | 
			
		||||
 | 
			
		||||
GLIB_GSETTINGS
 | 
			
		||||
 | 
			
		||||
@@ -46,50 +48,46 @@ 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 gtk+-3.0"
 | 
			
		||||
   PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
 | 
			
		||||
   recorder_modules="gstreamer-0.10 gstreamer-base-0.10 x11"
 | 
			
		||||
   PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes)
 | 
			
		||||
else
 | 
			
		||||
   AC_MSG_RESULT(no)
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
 | 
			
		||||
 | 
			
		||||
CLUTTER_MIN_VERSION=1.11.11
 | 
			
		||||
CLUTTER_MIN_VERSION=1.7.5
 | 
			
		||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
 | 
			
		||||
GJS_MIN_VERSION=1.33.2
 | 
			
		||||
MUTTER_MIN_VERSION=3.7.2
 | 
			
		||||
GTK_MIN_VERSION=3.3.9
 | 
			
		||||
GIO_MIN_VERSION=2.35.0
 | 
			
		||||
LIBECAL_MIN_VERSION=3.5.3
 | 
			
		||||
LIBEDATASERVER_MIN_VERSION=3.5.3
 | 
			
		||||
LIBEDATASERVERUI_MIN_VERSION=3.5.3
 | 
			
		||||
TELEPATHY_GLIB_MIN_VERSION=0.17.5
 | 
			
		||||
GJS_MIN_VERSION=1.29.18
 | 
			
		||||
MUTTER_MIN_VERSION=3.2.1
 | 
			
		||||
FOLKS_MIN_VERSION=0.5.2
 | 
			
		||||
GTK_MIN_VERSION=3.0.0
 | 
			
		||||
GIO_MIN_VERSION=2.29.10
 | 
			
		||||
LIBECAL_MIN_VERSION=2.32.0
 | 
			
		||||
LIBEDATASERVER_MIN_VERSION=1.2.0
 | 
			
		||||
LIBEDATASERVERUI_MIN_VERSION=2.91.6
 | 
			
		||||
TELEPATHY_GLIB_MIN_VERSION=0.15.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.7.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
 | 
			
		||||
PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
 | 
			
		||||
                               gio-unix-2.0 dbus-glib-1 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 gconf-2.0
 | 
			
		||||
                               gdk-x11-3.0 libsoup-2.4
 | 
			
		||||
                               gl
 | 
			
		||||
			       clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
 | 
			
		||||
			       clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
 | 
			
		||||
                               libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
 | 
			
		||||
@@ -98,8 +96,7 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
 | 
			
		||||
                               telepathy-glib >= $TELEPATHY_GLIB_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)
 | 
			
		||||
                               libnm-glib libnm-util gnome-keyring-1)
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
 | 
			
		||||
 | 
			
		||||
@@ -107,10 +104,13 @@ 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])
 | 
			
		||||
JHBUILD_TYPELIBDIR="$INTROSPECTION_TYPELIBDIR"
 | 
			
		||||
AC_SUBST(JHBUILD_TYPELIBDIR)
 | 
			
		||||
 | 
			
		||||
saved_CFLAGS=$CFLAGS
 | 
			
		||||
saved_LIBS=$LIBS
 | 
			
		||||
@@ -120,18 +120,17 @@ AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
 | 
			
		||||
CFLAGS=$saved_CFLAGS
 | 
			
		||||
LIBS=$saved_LIBS
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
 | 
			
		||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
 | 
			
		||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.2 gnome-desktop-3.0 >= 2.90.0 x11)
 | 
			
		||||
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-3.0)
 | 
			
		||||
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.7.2)
 | 
			
		||||
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],
 | 
			
		||||
        [BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
 | 
			
		||||
	 BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
 | 
			
		||||
	 AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
 | 
			
		||||
	 AC_SUBST([BLUETOOTH_DIR],["$BLUETOOTH_DIR"])
 | 
			
		||||
	 AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library])
 | 
			
		||||
	 AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
 | 
			
		||||
	 AC_SUBST([HAVE_BLUETOOTH],[1])
 | 
			
		||||
@@ -140,37 +139,10 @@ PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
 | 
			
		||||
	 AC_SUBST([HAVE_BLUETOOTH],[0])
 | 
			
		||||
	 AC_MSG_RESULT([no])])
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION gio-2.0)
 | 
			
		||||
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION libedataserverui-3.0 >= $LIBEDATASERVERUI_MIN_VERSION gio-2.0)
 | 
			
		||||
AC_SUBST(CALENDAR_SERVER_CFLAGS)
 | 
			
		||||
AC_SUBST(CALENDAR_SERVER_LIBS)
 | 
			
		||||
 | 
			
		||||
AC_ARG_WITH(systemd,
 | 
			
		||||
            AS_HELP_STRING([--with-systemd],
 | 
			
		||||
                           [Add systemd support]),
 | 
			
		||||
            [with_systemd=$withval], [with_systemd=auto])
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(SYSTEMD,
 | 
			
		||||
                  [libsystemd-login libsystemd-daemon],
 | 
			
		||||
                  [have_systemd=yes], [have_systemd=no])
 | 
			
		||||
 | 
			
		||||
if test "x$with_systemd" = "xauto" ; then
 | 
			
		||||
        if test x$have_systemd = xno ; then
 | 
			
		||||
                use_systemd=no
 | 
			
		||||
        else
 | 
			
		||||
                use_systemd=yes
 | 
			
		||||
        fi
 | 
			
		||||
else
 | 
			
		||||
        use_systemd=$with_systemd
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if test "x$use_systemd" = "xyes"; then
 | 
			
		||||
        if test "x$have_systemd" = "xno"; then
 | 
			
		||||
                AC_MSG_ERROR([Systemd support explicitly required, but systemd not found])
 | 
			
		||||
        fi
 | 
			
		||||
 | 
			
		||||
        AC_DEFINE(WITH_SYSTEMD, 1, [systemd support])
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
 | 
			
		||||
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
 | 
			
		||||
AC_SUBST(MUTTER_GIR_DIR)
 | 
			
		||||
@@ -207,20 +179,6 @@ AC_SUBST(GIRDIR)
 | 
			
		||||
TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)"
 | 
			
		||||
AC_SUBST(TYPELIBDIR)
 | 
			
		||||
 | 
			
		||||
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(man,
 | 
			
		||||
              [AS_HELP_STRING([--enable-man],
 | 
			
		||||
                              [generate man pages [default=yes]])],,
 | 
			
		||||
              enable_man=yes)
 | 
			
		||||
if test "$enable_man" != no; then
 | 
			
		||||
  AC_PATH_PROG([XSLTPROC], [xsltproc])
 | 
			
		||||
  if test -z "$XSLTPROC"; then
 | 
			
		||||
    AC_MSG_ERROR([xsltproc is required for --enable-man])
 | 
			
		||||
  fi
 | 
			
		||||
fi
 | 
			
		||||
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
 | 
			
		||||
 | 
			
		||||
# Stay command-line compatible with the gnome-common configure option. Here
 | 
			
		||||
# minimum/yes/maximum are the same, however.
 | 
			
		||||
AC_ARG_ENABLE(compile_warnings,
 | 
			
		||||
@@ -241,7 +199,7 @@ if test "$enable_compile_warnings" != no ; then
 | 
			
		||||
    if test "$enable_compile_warnings" = error ; then
 | 
			
		||||
      case " $CFLAGS " in
 | 
			
		||||
      *[\ \	]-Werror[\ \	]*) ;;
 | 
			
		||||
      *) CFLAGS="$CFLAGS -Werror -Wno-error=deprecated-declarations" ;;
 | 
			
		||||
      *) CFLAGS="$CFLAGS -Werror" ;;
 | 
			
		||||
      esac
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
@@ -249,23 +207,42 @@ fi
 | 
			
		||||
changequote([,])dnl
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
  AS_HELP_STRING([--jhbuild-wrapper-script=yes],[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])
 | 
			
		||||
 | 
			
		||||
AC_CONFIG_FILES([
 | 
			
		||||
  Makefile
 | 
			
		||||
  data/Makefile
 | 
			
		||||
  docs/Makefile
 | 
			
		||||
  docs/reference/Makefile
 | 
			
		||||
  docs/reference/shell/Makefile
 | 
			
		||||
  docs/reference/shell/shell-docs.sgml
 | 
			
		||||
  docs/reference/st/Makefile
 | 
			
		||||
  docs/reference/st/st-docs.sgml
 | 
			
		||||
  js/Makefile
 | 
			
		||||
  src/calendar-server/evolution-calendar.desktop.in
 | 
			
		||||
  js/misc/config.js
 | 
			
		||||
  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,21 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" ?>
 | 
			
		||||
<KeyListEntries schema="org.gnome.shell.keybindings"
 | 
			
		||||
                group="system"
 | 
			
		||||
                _name="System"
 | 
			
		||||
                wm_name="GNOME Shell"
 | 
			
		||||
                package="gnome-shell">
 | 
			
		||||
 | 
			
		||||
	<KeyListEntry name="toggle-message-tray"
 | 
			
		||||
                      _description="Show the message tray"/>
 | 
			
		||||
 | 
			
		||||
	<KeyListEntry name="focus-active-notification"
 | 
			
		||||
                      _description="Focus the active notification"/>
 | 
			
		||||
 | 
			
		||||
	<KeyListEntry name="toggle-application-view"
 | 
			
		||||
                      _description="Show all applications"/>
 | 
			
		||||
 | 
			
		||||
	<KeyListEntry name="open-application-menu"
 | 
			
		||||
                      _description="Open the application menu"/>
 | 
			
		||||
 | 
			
		||||
</KeyListEntries>
 | 
			
		||||
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
desktopdir=$(datadir)/applications
 | 
			
		||||
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
 | 
			
		||||
desktop_DATA = gnome-shell.desktop
 | 
			
		||||
 | 
			
		||||
# We substitute in bindir so it works as an autostart
 | 
			
		||||
# file when built in a non-system prefix
 | 
			
		||||
@@ -8,20 +8,20 @@ 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 $@
 | 
			
		||||
 | 
			
		||||
introspectiondir = $(datadir)/dbus-1/interfaces
 | 
			
		||||
introspection_DATA = org.gnome.ShellSearchProvider.xml
 | 
			
		||||
searchprovidersdir = $(pkgdatadir)/search_providers
 | 
			
		||||
dist_searchproviders_DATA =				\
 | 
			
		||||
	search_providers/google.xml				\
 | 
			
		||||
	search_providers/wikipedia.xml
 | 
			
		||||
 | 
			
		||||
themedir = $(pkgdatadir)/theme
 | 
			
		||||
dist_theme_DATA =				\
 | 
			
		||||
	theme/calendar-arrow-left.svg		\
 | 
			
		||||
	theme/calendar-arrow-right.svg		\
 | 
			
		||||
	theme/calendar-today.svg		\
 | 
			
		||||
	theme/checkbox-focused.svg		\
 | 
			
		||||
	theme/checkbox-off-focused.svg		\
 | 
			
		||||
	theme/checkbox-off.svg			\
 | 
			
		||||
	theme/checkbox.svg			\
 | 
			
		||||
	theme/close-window.svg			\
 | 
			
		||||
	theme/close.svg				\
 | 
			
		||||
	theme/corner-ripple-ltr.png		\
 | 
			
		||||
@@ -29,39 +29,27 @@ 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-border.svg			\
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
keysdir = @GNOME_KEYBINDINGS_KEYSDIR@
 | 
			
		||||
keys_in_files =					\
 | 
			
		||||
	50-gnome-shell-screenshot.xml.in	\
 | 
			
		||||
	50-gnome-shell-system.xml.in		\
 | 
			
		||||
	$(NULL)
 | 
			
		||||
keys_DATA = $(keys_in_files:.xml.in=.xml)
 | 
			
		||||
	theme/ws-switch-arrow-up.svg		\
 | 
			
		||||
	theme/ws-switch-arrow-down.svg
 | 
			
		||||
 | 
			
		||||
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
 | 
			
		||||
 | 
			
		||||
@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
 | 
			
		||||
@@ -71,24 +59,30 @@ gschemas.compiled: $(gsettings_SCHEMAS:.xml=.valid)
 | 
			
		||||
 | 
			
		||||
all-local: gschemas.compiled
 | 
			
		||||
 | 
			
		||||
convertdir = $(datadir)/GConf/gsettings
 | 
			
		||||
convert_DATA = gnome-shell-overrides.convert
 | 
			
		||||
 | 
			
		||||
# GConf schemas: provide defaults for keys from Metacity we are overriding
 | 
			
		||||
gconfschemadir  = @GCONF_SCHEMA_FILE_DIR@
 | 
			
		||||
gconfschema_DATA = gnome-shell.schemas
 | 
			
		||||
 | 
			
		||||
shadersdir = $(pkgdatadir)/shaders
 | 
			
		||||
shaders_DATA = \
 | 
			
		||||
	shaders/dim-window.glsl
 | 
			
		||||
 | 
			
		||||
install-data-local:
 | 
			
		||||
	GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(srcdir)/$(gconfschema_DATA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST =						\
 | 
			
		||||
	gnome-shell.desktop.in.in			\
 | 
			
		||||
	gnome-shell-extension-prefs.desktop.in.in	\
 | 
			
		||||
	$(introspection_DATA)				\
 | 
			
		||||
	$(menu_DATA)					\
 | 
			
		||||
	$(convert_DATA)					\
 | 
			
		||||
	$(keys_in_files)				\
 | 
			
		||||
	org.gnome.shell.gschema.xml.in.in
 | 
			
		||||
	$(gconfschema_DATA)				\
 | 
			
		||||
	$(shaders_DATA)					\
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
[Desktop Entry]
 | 
			
		||||
Type=Application
 | 
			
		||||
_Name=GNOME Shell Extension Preferences
 | 
			
		||||
_Comment=Configure GNOME Shell Extensions
 | 
			
		||||
Exec=@bindir@/gnome-shell-extension-prefs %u
 | 
			
		||||
X-GNOME-Bugzilla-Bugzilla=GNOME
 | 
			
		||||
X-GNOME-Bugzilla-Product=gnome-shell
 | 
			
		||||
X-GNOME-Bugzilla-Component=extensions
 | 
			
		||||
X-GNOME-Bugzilla-Version=@VERSION@
 | 
			
		||||
Categories=GNOME;GTK;
 | 
			
		||||
OnlyShowIn=GNOME;
 | 
			
		||||
NoDisplay=true
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
[org.gnome.shell.overrides]
 | 
			
		||||
attach-modal-dialogs = /desktop/gnome/shell/windows/attach_modal_dialogs
 | 
			
		||||
button-layout = /desktop/gnome/shell/windows/button_layout
 | 
			
		||||
edge-tiling = /desktop/gnome/shell/windows/edge_tiling
 | 
			
		||||
workspaces-only-on-primary = /desktop/gnome/shell/windows/workspaces_only_on_primary
 | 
			
		||||
@@ -13,4 +13,4 @@ NoDisplay=true
 | 
			
		||||
X-GNOME-Autostart-Phase=WindowManager
 | 
			
		||||
X-GNOME-Provides=panel;windowmanager;
 | 
			
		||||
X-GNOME-Autostart-Notify=true
 | 
			
		||||
X-GNOME-AutoRestart=false
 | 
			
		||||
X-GNOME-AutoRestart=true
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										100
									
								
								data/gnome-shell.schemas
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,100 @@
 | 
			
		||||
<gconfschemafile>
 | 
			
		||||
    <schemalist>
 | 
			
		||||
 | 
			
		||||
      <!-- Metacity overrides -->
 | 
			
		||||
      <schema>
 | 
			
		||||
        <key>/schemas/desktop/gnome/shell/windows/attach_modal_dialogs</key>
 | 
			
		||||
        <applyto>/desktop/gnome/shell/windows/attach_modal_dialogs</applyto>
 | 
			
		||||
        <owner>gnome-shell</owner>
 | 
			
		||||
        <type>bool</type>
 | 
			
		||||
        <default>true</default>
 | 
			
		||||
        <locale name="C">
 | 
			
		||||
          <short>Attach modal dialog to the parent window</short>
 | 
			
		||||
          <long>
 | 
			
		||||
             This key overrides /apps/mutter/general/attach_modal_dialogs when
 | 
			
		||||
             running GNOME Shell.
 | 
			
		||||
          </long>
 | 
			
		||||
        </locale>
 | 
			
		||||
      </schema>
 | 
			
		||||
 | 
			
		||||
      <schema>
 | 
			
		||||
        <key>/schemas/desktop/gnome/shell/windows/button_layout</key>
 | 
			
		||||
        <applyto>/desktop/gnome/shell/windows/button_layout</applyto>
 | 
			
		||||
        <owner>gnome-shell</owner>
 | 
			
		||||
        <type>string</type>
 | 
			
		||||
        <default>:close</default>
 | 
			
		||||
        <locale name="C">
 | 
			
		||||
           <short>Arrangement of buttons on the titlebar</short>
 | 
			
		||||
           <long>
 | 
			
		||||
             Arrangement of buttons on the titlebar. The
 | 
			
		||||
             value should be a string, such as
 | 
			
		||||
             "menu:minimize,maximize,spacer,close"; the colon separates the
 | 
			
		||||
             left corner of the window from the right corner, and
 | 
			
		||||
             the button names are comma-separated. Duplicate buttons
 | 
			
		||||
             are not allowed. Unknown button names are silently ignored
 | 
			
		||||
             so that buttons can be added in future gnome-shell versions
 | 
			
		||||
             without breaking older versions.
 | 
			
		||||
             A special spacer tag can be used to insert some space between
 | 
			
		||||
             two adjacent buttons.
 | 
			
		||||
 | 
			
		||||
             This key overrides /apps/metacity/general/button_layout when
 | 
			
		||||
             running GNOME Shell.
 | 
			
		||||
           </long>
 | 
			
		||||
        </locale>
 | 
			
		||||
      </schema>
 | 
			
		||||
 | 
			
		||||
      <schema>
 | 
			
		||||
        <key>/schemas/desktop/gnome/shell/windows/edge_tiling</key>
 | 
			
		||||
        <applyto>/desktop/gnome/shell/windows/edge_tiling</applyto>
 | 
			
		||||
        <owner>gnome-shell</owner>
 | 
			
		||||
        <type>bool</type>
 | 
			
		||||
        <default>true</default>
 | 
			
		||||
        <locale name="C">
 | 
			
		||||
          <short>enable edge tiling when dropping windows on screen edges</short>
 | 
			
		||||
          <long>
 | 
			
		||||
             If enabled, dropping windows on vertical screen edges maximizes them
 | 
			
		||||
             vertically and resizes them horizontally to cover half of the
 | 
			
		||||
             available area. Dropping windows on the top screen edge maximizes them
 | 
			
		||||
             completely.
 | 
			
		||||
 | 
			
		||||
             This key overrides /apps/metacity/general/edge_tiling when
 | 
			
		||||
             running GNOME Shell.
 | 
			
		||||
          </long>
 | 
			
		||||
        </locale>
 | 
			
		||||
      </schema>
 | 
			
		||||
 | 
			
		||||
      <schema>
 | 
			
		||||
        <key>/schemas/desktop/gnome/shell/windows/theme</key>
 | 
			
		||||
        <applyto>/desktop/gnome/shell/windows/theme</applyto>
 | 
			
		||||
        <owner>gnome-shell</owner>
 | 
			
		||||
        <type>string</type>
 | 
			
		||||
        <default>Adwaita</default>
 | 
			
		||||
        <locale name="C">
 | 
			
		||||
          <short>Current theme</short>
 | 
			
		||||
          <long>
 | 
			
		||||
            The theme determines the appearance of window borders,
 | 
			
		||||
            titlebar, and so forth.
 | 
			
		||||
 | 
			
		||||
            This key overrides /apps/metacity/general/theme when
 | 
			
		||||
            running GNOME Shell.
 | 
			
		||||
          </long>
 | 
			
		||||
        </locale>
 | 
			
		||||
      </schema>
 | 
			
		||||
 | 
			
		||||
      <schema>
 | 
			
		||||
        <key>/schemas/desktop/gnome/shell/windows/workspaces_only_on_primary</key>
 | 
			
		||||
        <applyto>/desktop/gnome/shell/windows/workspaces_only_on_primary</applyto>
 | 
			
		||||
        <owner>gnome-shell</owner>
 | 
			
		||||
        <type>bool</type>
 | 
			
		||||
        <default>true</default>
 | 
			
		||||
        <locale name="C">
 | 
			
		||||
          <short>Workspaces only on primary monitor</short>
 | 
			
		||||
          <long>
 | 
			
		||||
             This key overrides /apps/mutter/general/workspaces_only_on_primary when
 | 
			
		||||
             running GNOME Shell.
 | 
			
		||||
          </long>
 | 
			
		||||
        </locale>
 | 
			
		||||
      </schema>
 | 
			
		||||
 | 
			
		||||
  </schemalist>
 | 
			
		||||
</gconfschemafile>
 | 
			
		||||
@@ -1,147 +0,0 @@
 | 
			
		||||
<!DOCTYPE node PUBLIC
 | 
			
		||||
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
 | 
			
		||||
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
 | 
			
		||||
<node>
 | 
			
		||||
  <interface name="org.gnome.Shell.SearchProvider">
 | 
			
		||||
    <doc:doc>
 | 
			
		||||
      <doc:description>
 | 
			
		||||
        <doc:para>
 | 
			
		||||
          The interface used for integrating into GNOME Shell's search
 | 
			
		||||
          interface.
 | 
			
		||||
        </doc:para>
 | 
			
		||||
      </doc:description>
 | 
			
		||||
    </doc:doc>
 | 
			
		||||
 | 
			
		||||
    <method name="GetInitialResultSet">
 | 
			
		||||
      <doc:doc>
 | 
			
		||||
        <doc:description>
 | 
			
		||||
          <doc:para>
 | 
			
		||||
            Called when the user first begins a search.
 | 
			
		||||
          </doc:para>
 | 
			
		||||
        </doc:description>
 | 
			
		||||
      </doc:doc>
 | 
			
		||||
      <arg type="as" direction="in">
 | 
			
		||||
        <doc:doc>
 | 
			
		||||
          <doc:summary>
 | 
			
		||||
            <doc:para>
 | 
			
		||||
              Array of search terms, which the provider should treat as
 | 
			
		||||
              logical AND.
 | 
			
		||||
            </doc:para>
 | 
			
		||||
          </doc:summary>
 | 
			
		||||
        </doc:doc>
 | 
			
		||||
      </arg>
 | 
			
		||||
      <arg type="as" direction="out">
 | 
			
		||||
        <doc:doc>
 | 
			
		||||
          <doc:summary>
 | 
			
		||||
            <doc:para>
 | 
			
		||||
              An array of result identifier strings representing items which
 | 
			
		||||
              match the given search terms. Identifiers must be unique within
 | 
			
		||||
              the provider's domain, but other than that may be chosen freely
 | 
			
		||||
              by the provider.
 | 
			
		||||
            </doc:para>
 | 
			
		||||
          </doc:summary>
 | 
			
		||||
        </doc:doc>
 | 
			
		||||
      </arg>
 | 
			
		||||
    </method>
 | 
			
		||||
 | 
			
		||||
    <method name="GetSubsearchResultSet">
 | 
			
		||||
      <doc:doc>
 | 
			
		||||
        <doc:description>
 | 
			
		||||
          <doc:para>
 | 
			
		||||
            Called when a search is performed which is a "subsearch" of
 | 
			
		||||
            the previous search, e.g. the method may return less results, but
 | 
			
		||||
            not more or different results.
 | 
			
		||||
 | 
			
		||||
            This allows search providers to only search through the previous
 | 
			
		||||
            result set, rather than possibly performing a full re-query.
 | 
			
		||||
          </doc:para>
 | 
			
		||||
        </doc:description>
 | 
			
		||||
      </doc:doc>
 | 
			
		||||
      <arg type="as" direction="in">
 | 
			
		||||
        <doc:doc>
 | 
			
		||||
          <doc:summary>
 | 
			
		||||
            <doc:para>
 | 
			
		||||
              Array of item identifiers
 | 
			
		||||
            </doc:para>
 | 
			
		||||
          </doc:summary>
 | 
			
		||||
        </doc:doc>
 | 
			
		||||
      </arg>
 | 
			
		||||
      <arg type="as" direction="in">
 | 
			
		||||
        <doc:doc>
 | 
			
		||||
          <doc:summary>
 | 
			
		||||
            <doc:para>
 | 
			
		||||
              Array of updated search terms, which the provider should treat as
 | 
			
		||||
              logical AND.
 | 
			
		||||
            </doc:para>
 | 
			
		||||
          </doc:summary>
 | 
			
		||||
        </doc:doc>
 | 
			
		||||
      </arg>
 | 
			
		||||
      <arg type="as" direction="out">
 | 
			
		||||
        <doc:doc>
 | 
			
		||||
          <doc:summary>
 | 
			
		||||
            <doc:para>
 | 
			
		||||
              An array of result identifier strings representing items which
 | 
			
		||||
              match the given search terms. Identifiers must be unique within
 | 
			
		||||
              the provider's domain, but other than that may be chosen freely
 | 
			
		||||
              by the provider.
 | 
			
		||||
            </doc:para>
 | 
			
		||||
          </doc:summary>
 | 
			
		||||
        </doc:doc>
 | 
			
		||||
      </arg>
 | 
			
		||||
    </method>
 | 
			
		||||
 | 
			
		||||
    <method name="GetResultMetas">
 | 
			
		||||
      <doc:doc>
 | 
			
		||||
        <doc:description>
 | 
			
		||||
          <doc:para>
 | 
			
		||||
            Return an array of meta data used to display each given result
 | 
			
		||||
          </doc:para>
 | 
			
		||||
        </doc:description>
 | 
			
		||||
      </doc:doc>
 | 
			
		||||
      <arg type="as" direction="in">
 | 
			
		||||
        <doc:doc>
 | 
			
		||||
          <doc:summary>
 | 
			
		||||
            <doc:para>
 | 
			
		||||
              An array of result identifiers as returned by
 | 
			
		||||
              GetInitialResultSet() or GetSubsearchResultSet()
 | 
			
		||||
            </doc:para>
 | 
			
		||||
          </doc:summary>
 | 
			
		||||
        </doc:doc>
 | 
			
		||||
      </arg>
 | 
			
		||||
      <arg type="aa{sv}" direction="out">
 | 
			
		||||
        <doc:doc>
 | 
			
		||||
          <doc:summary>
 | 
			
		||||
            <doc:para>
 | 
			
		||||
              A dictionary describing the given search result, containing
 | 
			
		||||
              'id', 'name' (both strings) and either 'icon' (a serialized
 | 
			
		||||
              GIcon) or 'icon-data' (raw image data as (iiibiiay) - width,
 | 
			
		||||
              height, rowstride, has-alpha, bits per sample, channels, data)
 | 
			
		||||
            </doc:para>
 | 
			
		||||
          </doc:summary>
 | 
			
		||||
        </doc:doc>
 | 
			
		||||
      </arg>
 | 
			
		||||
    </method>
 | 
			
		||||
 | 
			
		||||
    <method name="ActivateResult">
 | 
			
		||||
      <doc:doc>
 | 
			
		||||
        <doc:description>
 | 
			
		||||
          <doc:para>
 | 
			
		||||
            Called when the users chooses a given result. The result should
 | 
			
		||||
            be displayed in the application associated with the corresponding
 | 
			
		||||
            provider.
 | 
			
		||||
          </doc:para>
 | 
			
		||||
        </doc:description>
 | 
			
		||||
      </doc:doc>
 | 
			
		||||
      <arg type="s" direction="in">
 | 
			
		||||
        <doc:doc>
 | 
			
		||||
          <doc:summary>
 | 
			
		||||
            <doc:para>
 | 
			
		||||
              A result identifier as returned by GetInitialResultSet() or
 | 
			
		||||
              GetSubsearchResultSet()
 | 
			
		||||
            </doc:para>
 | 
			
		||||
          </doc:summary>
 | 
			
		||||
        </doc:doc>
 | 
			
		||||
      </arg>
 | 
			
		||||
    </method>
 | 
			
		||||
  </interface>
 | 
			
		||||
</node>
 | 
			
		||||
@@ -16,9 +16,8 @@
 | 
			
		||||
      <_summary>Uuids of extensions to enable</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        GNOME Shell extensions have a uuid property; this key lists extensions
 | 
			
		||||
        which should be loaded. Any extension that wants to be loaded needs
 | 
			
		||||
        to be in this list. You can also manipulate this list with the
 | 
			
		||||
        EnableExtension and DisableExtension DBus methods on org.gnome.Shell.
 | 
			
		||||
        which should be loaded.  disabled-extensions overrides this setting for
 | 
			
		||||
        extensions that appear in both lists.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="enable-app-monitoring" type="b">
 | 
			
		||||
@@ -39,6 +38,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>
 | 
			
		||||
@@ -49,25 +52,15 @@
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="saved-im-presence" type="i">
 | 
			
		||||
      <default>1</default>
 | 
			
		||||
      <_summary>Internally used to store the last IM presence explicitly set by the user. The
 | 
			
		||||
value here is from the TpConnectionPresenceType enumeration.</_summary>
 | 
			
		||||
      <_summary></_summary>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="saved-session-presence" type="i">
 | 
			
		||||
      <default>0</default>
 | 
			
		||||
      <_summary>Internally used to store the last session presence status for the user. The
 | 
			
		||||
value here is from the GsmPresenceStatus enumeration.</_summary>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="always-show-log-out" type="b">
 | 
			
		||||
      <default>false</default>
 | 
			
		||||
      <_summary>Always show the 'Log out' menuitem in the user menu.</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        This key overrides the automatic hiding of the 'Log out'
 | 
			
		||||
        menuitem in single-user, single-session situations.
 | 
			
		||||
      </_description>
 | 
			
		||||
      <_summary></_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"/>
 | 
			
		||||
    <child name="keyboard" schema="org.gnome.shell.keyboard"/>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
@@ -82,46 +75,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
 | 
			
		||||
      </key>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
  <schema id="org.gnome.shell.keybindings" path="/org/gnome/shell/keybindings/"
 | 
			
		||||
          gettext-domain="@GETTEXT_PACKAGE@">
 | 
			
		||||
    <key name="open-application-menu" type="as">
 | 
			
		||||
      <default>["<Super>F10"]</default>
 | 
			
		||||
      <_summary>Keybinding to open the application menu</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Keybinding to open the application menu.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="toggle-application-view" type="as">
 | 
			
		||||
      <default>["<Super>a"]</default>
 | 
			
		||||
      <_summary>Keybinding to open the "Show Applications" view</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Keybinding to open the "Show Applications" view of the Activities
 | 
			
		||||
        Overview.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="toggle-message-tray" type="as">
 | 
			
		||||
      <default>["<Super>m"]</default>
 | 
			
		||||
      <_summary>Keybinding to toggle the visibility of the message tray</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Keybinding to toggle the visibility of the message tray.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="focus-active-notification" type="as">
 | 
			
		||||
      <default>["<Super>n"]</default>
 | 
			
		||||
      <_summary>Keybinding to focus the active notification</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Keybinding to focus the active notification.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="toggle-recording" type="as">
 | 
			
		||||
      <default><![CDATA[['<Control><Shift><Alt>r']]]></default>
 | 
			
		||||
      <_summary>Keybinding to toggle the screen recorder</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Keybinding to start/stop the builtin screen recorder.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
  <schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
 | 
			
		||||
          gettext-domain="@GETTEXT_PACKAGE@">
 | 
			
		||||
    <key name="keyboard-type" type="s">
 | 
			
		||||
@@ -133,10 +86,28 @@ 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">
 | 
			
		||||
      <default>30</default>
 | 
			
		||||
      <default>15</default>
 | 
			
		||||
      <_summary>Framerate used for recording screencasts.</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        The framerate of the resulting screencast recordered
 | 
			
		||||
@@ -155,7 +126,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'
 | 
			
		||||
        'videorate ! vp8enc quality=10 speed=2 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>
 | 
			
		||||
@@ -170,57 +141,4 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
  <schema id="org.gnome.shell.overrides" path="/org/gnome/shell/overrides/"
 | 
			
		||||
	  gettext-domain="@GETTEXT_PACKAGE@">
 | 
			
		||||
    <key name="attach-modal-dialogs" type="b">
 | 
			
		||||
      <default>true</default>
 | 
			
		||||
      <_summary>Attach modal dialog to the parent window</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        This key overrides the key in org.gnome.mutter when running
 | 
			
		||||
        GNOME Shell.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
 | 
			
		||||
    <key name="button-layout" type="s">
 | 
			
		||||
      <default>":close"</default>
 | 
			
		||||
      <_summary>Arrangement of buttons on the titlebar</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        This key overrides the key in org.gnome.desktop.wm.preferences when
 | 
			
		||||
        running GNOME Shell.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
 | 
			
		||||
    <key name="edge-tiling" type="b">
 | 
			
		||||
      <default>true</default>
 | 
			
		||||
      <_summary>Enable edge tiling when dropping windows on screen edges</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        This key overrides the key in org.gnome.mutter when running GNOME Shell.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
 | 
			
		||||
    <key name="dynamic-workspaces" type="b">
 | 
			
		||||
      <default>true</default>
 | 
			
		||||
      <_summary>Workspaces are managed dynamically</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        This key overrides the key in org.gnome.mutter when running GNOME Shell.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
 | 
			
		||||
    <key name="workspaces-only-on-primary" type="b">
 | 
			
		||||
      <default>true</default>
 | 
			
		||||
      <_summary>Workspaces only on primary monitor</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        This key overrides the key in org.gnome.mutter when running GNOME Shell.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
 | 
			
		||||
    <key name="focus-change-on-pointer-rest" type="b">
 | 
			
		||||
      <default>true</default>
 | 
			
		||||
      <summary>Delay focus changes in mouse mode until the pointer stops moving</summary>
 | 
			
		||||
      <description>
 | 
			
		||||
        This key overrides the key in org.gnome.mutter when running GNOME Shell.
 | 
			
		||||
      </description>
 | 
			
		||||
    </key>
 | 
			
		||||
  </schema>
 | 
			
		||||
</schemalist>
 | 
			
		||||
							
								
								
									
										7
									
								
								data/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/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>
 | 
			
		||||
							
								
								
									
										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  | 
@@ -1,289 +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="24"
 | 
			
		||||
   height="22"
 | 
			
		||||
   id="svg3199"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.1 r9760"
 | 
			
		||||
   sodipodi:docname="checkbox.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs3201">
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       id="linearGradient15404"
 | 
			
		||||
       inkscape:collect="always">
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop15406"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         style="stop-color:#515151;stop-opacity:1" />
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop15408"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         style="stop-color:#292929;stop-opacity:1" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       sodipodi:type="inkscape:persp3d"
 | 
			
		||||
       inkscape:vp_x="0 : 526.18109 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_z="744.09448 : 526.18109 : 1"
 | 
			
		||||
       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
 | 
			
		||||
       id="perspective3207" />
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       id="perspective3187"
 | 
			
		||||
       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="#linearGradient5872-5-1"
 | 
			
		||||
       id="linearGradient5891-0-4"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       x1="205.84143"
 | 
			
		||||
       y1="246.7094"
 | 
			
		||||
       x2="206.74803"
 | 
			
		||||
       y2="231.24142" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       id="linearGradient5872-5-1">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#0b2e52;stop-opacity:1"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop5874-4-4" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#1862af;stop-opacity:1"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop5876-0-5" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <inkscape:path-effect
 | 
			
		||||
       effect="spiro"
 | 
			
		||||
       id="path-effect5837-4-6"
 | 
			
		||||
       is_visible="true" />
 | 
			
		||||
    <inkscape:path-effect
 | 
			
		||||
       effect="spiro"
 | 
			
		||||
       id="path-effect14768"
 | 
			
		||||
       is_visible="true" />
 | 
			
		||||
    <inkscape:path-effect
 | 
			
		||||
       effect="spiro"
 | 
			
		||||
       id="path-effect5884-4-7"
 | 
			
		||||
       is_visible="true" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       y2="-388.72955"
 | 
			
		||||
       x2="-93.031357"
 | 
			
		||||
       y1="-396.34738"
 | 
			
		||||
       x1="-93.031357"
 | 
			
		||||
       gradientTransform="matrix(1.5918367,0,0,0.85714285,-256.56122,59.685418)"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       id="linearGradient14219"
 | 
			
		||||
       xlink:href="#linearGradient15404"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       id="linearGradient10013-4-63-6">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#333333;stop-opacity:1;"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop10015-2-76-1" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#292929;stop-opacity:1"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop10017-46-15-8" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       id="linearGradient10597-5">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#16191a;stop-opacity:1;"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop10599-2" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#2b3133;stop-opacity:1"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop10601-5" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       y2="-322.16354"
 | 
			
		||||
       x2="921.22498"
 | 
			
		||||
       y1="-330.05121"
 | 
			
		||||
       x1="921.32812"
 | 
			
		||||
       gradientTransform="matrix(1.5918367,0,0,0.85714285,-1456.5464,275.45191)"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       id="linearGradient15374"
 | 
			
		||||
       xlink:href="#linearGradient10013-4-63-6"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       gradientTransform="translate(-1199.9852,216.38048)"
 | 
			
		||||
       y2="-227.07961"
 | 
			
		||||
       x2="1203.9177"
 | 
			
		||||
       y1="-217.56708"
 | 
			
		||||
       x1="1203.9177"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       id="linearGradient15376"
 | 
			
		||||
       xlink:href="#linearGradient10597-5"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       y2="-388.72955"
 | 
			
		||||
       x2="-93.031357"
 | 
			
		||||
       y1="-396.34738"
 | 
			
		||||
       x1="-93.031357"
 | 
			
		||||
       gradientTransform="matrix(1.5918367,0,0,0.85714285,-256.56122,59.685418)"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       id="linearGradient14219-6"
 | 
			
		||||
       xlink:href="#linearGradient15404-9"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       id="linearGradient15404-9"
 | 
			
		||||
       inkscape:collect="always">
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop15406-6"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         style="stop-color:#515151;stop-opacity:1" />
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop15408-7"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         style="stop-color:#292929;stop-opacity:1" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
  </defs>
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#000000"
 | 
			
		||||
     bordercolor="#2d2d2d"
 | 
			
		||||
     borderopacity="1"
 | 
			
		||||
     inkscape:pageopacity="1"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="1"
 | 
			
		||||
     inkscape:cx="71.516955"
 | 
			
		||||
     inkscape:cy="5.8710559"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     inkscape:window-width="1412"
 | 
			
		||||
     inkscape:window-height="1067"
 | 
			
		||||
     inkscape:window-x="2635"
 | 
			
		||||
     inkscape:window-y="226"
 | 
			
		||||
     inkscape:window-maximized="0"
 | 
			
		||||
     borderlayer="true"
 | 
			
		||||
     inkscape:showpageshadow="false"
 | 
			
		||||
     inkscape:snap-nodes="false"
 | 
			
		||||
     inkscape:snap-bbox="true"
 | 
			
		||||
     showborder="false">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
       id="grid14843"
 | 
			
		||||
       empspacing="5"
 | 
			
		||||
       visible="true"
 | 
			
		||||
       enabled="true"
 | 
			
		||||
       snapvisiblegridlinesonly="true" />
 | 
			
		||||
  </sodipodi:namedview>
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata3204">
 | 
			
		||||
    <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"
 | 
			
		||||
     transform="translate(-342.5,-521.36218)">
 | 
			
		||||
    <g
 | 
			
		||||
       transform="matrix(0.80230061,0,0,0.80230061,-87.624044,-453.10297)"
 | 
			
		||||
       id="g14586-0"
 | 
			
		||||
       style="stroke-width:2.3714385;stroke-miterlimit:4;stroke-dasharray:none">
 | 
			
		||||
      <g
 | 
			
		||||
         inkscape:export-ydpi="90"
 | 
			
		||||
         inkscape:export-xdpi="90"
 | 
			
		||||
         inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
 | 
			
		||||
         transform="matrix(1.9969286,0,0,1.9969286,-397.05491,877.00482)"
 | 
			
		||||
         id="g15291-9-6"
 | 
			
		||||
         style="stroke-width:1.18754292;stroke-miterlimit:4;stroke-dasharray:none;display:inline;enable-background:new">
 | 
			
		||||
        <g
 | 
			
		||||
           transform="translate(877.50354,-102.83507)"
 | 
			
		||||
           id="g16853-4-9"
 | 
			
		||||
           style="stroke-width:1.18754292;stroke-miterlimit:4;stroke-dasharray:none;enable-background:new">
 | 
			
		||||
          <rect
 | 
			
		||||
             transform="scale(1,-1)"
 | 
			
		||||
             style="color:#000000;fill:url(#linearGradient14219-6);fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:1.24833274;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:new"
 | 
			
		||||
             id="rect6506-6"
 | 
			
		||||
             width="11.281681"
 | 
			
		||||
             height="11.26221"
 | 
			
		||||
             x="-409.59354"
 | 
			
		||||
             y="-284.40115"
 | 
			
		||||
             rx="1.0052766"
 | 
			
		||||
             ry="1.0052764" />
 | 
			
		||||
        </g>
 | 
			
		||||
      </g>
 | 
			
		||||
      <g
 | 
			
		||||
         inkscape:export-ydpi="90"
 | 
			
		||||
         inkscape:export-xdpi="90"
 | 
			
		||||
         inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
 | 
			
		||||
         transform="translate(343.99999,987.99997)"
 | 
			
		||||
         id="g5886-5"
 | 
			
		||||
         style="stroke-width:2.3714385;stroke-miterlimit:4;stroke-dasharray:none;display:inline;enable-background:new" />
 | 
			
		||||
    </g>
 | 
			
		||||
    <g
 | 
			
		||||
       transform="matrix(0.84337,0,0,0.84337,-110.16632,-503.56182)"
 | 
			
		||||
       id="g14586">
 | 
			
		||||
      <g
 | 
			
		||||
         inkscape:export-ydpi="90"
 | 
			
		||||
         inkscape:export-xdpi="90"
 | 
			
		||||
         inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
 | 
			
		||||
         transform="matrix(1.9969286,0,0,1.9969286,-397.05491,877.00482)"
 | 
			
		||||
         id="g15291-9"
 | 
			
		||||
         style="display:inline;enable-background:new">
 | 
			
		||||
        <g
 | 
			
		||||
           transform="translate(877.50354,-102.83507)"
 | 
			
		||||
           id="g16853-4"
 | 
			
		||||
           style="enable-background:new" />
 | 
			
		||||
      </g>
 | 
			
		||||
      <g
 | 
			
		||||
         inkscape:export-ydpi="90"
 | 
			
		||||
         inkscape:export-xdpi="90"
 | 
			
		||||
         inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
 | 
			
		||||
         transform="translate(343.99999,987.99997)"
 | 
			
		||||
         id="g5886"
 | 
			
		||||
         style="display:inline;enable-background:new">
 | 
			
		||||
        <path
 | 
			
		||||
           style="fill:none;stroke:url(#linearGradient5891-0-4);stroke-width:7.11431503;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
 | 
			
		||||
           d="m 198.5,240 5.25,5.25 13.98616,-14.43081"
 | 
			
		||||
           id="path5835"
 | 
			
		||||
           inkscape:path-effect="#path-effect5837-4-6"
 | 
			
		||||
           inkscape:original-d="m 198.5,240 5.25,5.25 13.98616,-14.43081"
 | 
			
		||||
           inkscape:connector-curvature="0"
 | 
			
		||||
           sodipodi:nodetypes="ccc" />
 | 
			
		||||
        <path
 | 
			
		||||
           inkscape:connector-curvature="0"
 | 
			
		||||
           inkscape:original-d="m 198.5,240 5.25,5.25 13.91205,-14.31964"
 | 
			
		||||
           inkscape:path-effect="#path-effect5837-4-6"
 | 
			
		||||
           id="path5880"
 | 
			
		||||
           d="m 198.5,240 5.25,5.25 13.91205,-14.31964"
 | 
			
		||||
           style="fill:none;stroke:#4787c8;stroke-width:3.55715752;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
 | 
			
		||||
           sodipodi:nodetypes="ccc" />
 | 
			
		||||
        <path
 | 
			
		||||
           style="fill:none;stroke:#7ea7d3;stroke-width:1.18571913px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
			
		||||
           d="m 197.45937,240.47455 c -0.17828,-0.29362 -0.20087,-0.67548 -0.0603,-0.98892 0.14055,-0.31344 0.43739,-0.54812 0.77144,-0.62817 0.33405,-0.08 0.69314,-0.01 0.99635,0.15175 0.30321,0.16144 0.55146,0.40727 0.79165,0.65284 l 3.66429,3.74643 12.87946,-12.98973 c 0.20796,-0.20974 0.42306,-0.41969 0.68548,-0.55522 0.26242,-0.13553 0.57293,-0.19052 0.85827,-0.11426 0.14267,0.0381 0.27708,0.10787 0.38874,0.20452 0.11167,0.0966 0.20021,0.22004 0.25479,0.35726 0.0546,0.13722 0.075,0.28793 0.0585,0.43468 -0.0165,0.14674 -0.07,0.28919 -0.15422,0.41052"
 | 
			
		||||
           id="path5882"
 | 
			
		||||
           inkscape:path-effect="#path-effect5884-4-7"
 | 
			
		||||
           inkscape:original-d="m 197.45937,240.47455 c 0.65604,-0.56057 2.02485,-1.34847 2.49911,-0.8125 l 3.66429,3.74643 12.87946,-12.98973 c 0.6875,-0.6875 2.09152,0.7375 2.09152,0.7375"
 | 
			
		||||
           inkscape:connector-curvature="0"
 | 
			
		||||
           sodipodi:nodetypes="csccc" />
 | 
			
		||||
      </g>
 | 
			
		||||
    </g>
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 11 KiB  | 
@@ -1,198 +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="24"
 | 
			
		||||
   height="22"
 | 
			
		||||
   id="svg3199"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.1 r9760"
 | 
			
		||||
   sodipodi:docname="checkbox-off.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs3201">
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       id="linearGradient15404"
 | 
			
		||||
       inkscape:collect="always">
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop15406"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         style="stop-color:#515151;stop-opacity:1" />
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop15408"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         style="stop-color:#292929;stop-opacity:1" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       sodipodi:type="inkscape:persp3d"
 | 
			
		||||
       inkscape:vp_x="0 : 526.18109 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_z="744.09448 : 526.18109 : 1"
 | 
			
		||||
       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
 | 
			
		||||
       id="perspective3207" />
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       id="perspective3187"
 | 
			
		||||
       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" />
 | 
			
		||||
    <inkscape:path-effect
 | 
			
		||||
       effect="spiro"
 | 
			
		||||
       id="path-effect5837-4-6"
 | 
			
		||||
       is_visible="true" />
 | 
			
		||||
    <inkscape:path-effect
 | 
			
		||||
       effect="spiro"
 | 
			
		||||
       id="path-effect14768"
 | 
			
		||||
       is_visible="true" />
 | 
			
		||||
    <inkscape:path-effect
 | 
			
		||||
       effect="spiro"
 | 
			
		||||
       id="path-effect5884-4-7"
 | 
			
		||||
       is_visible="true" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       y2="-388.72955"
 | 
			
		||||
       x2="-93.031357"
 | 
			
		||||
       y1="-396.34738"
 | 
			
		||||
       x1="-93.031357"
 | 
			
		||||
       gradientTransform="matrix(1.5918367,0,0,0.85714285,-256.56122,59.685418)"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       id="linearGradient14219"
 | 
			
		||||
       xlink:href="#linearGradient15404"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       id="linearGradient10013-4-63-6">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#333333;stop-opacity:1;"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop10015-2-76-1" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#292929;stop-opacity:1"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop10017-46-15-8" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       id="linearGradient10597-5">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#16191a;stop-opacity:1;"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop10599-2" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#2b3133;stop-opacity:1"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop10601-5" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       y2="-322.16354"
 | 
			
		||||
       x2="921.22498"
 | 
			
		||||
       y1="-330.05121"
 | 
			
		||||
       x1="921.32812"
 | 
			
		||||
       gradientTransform="matrix(1.5918367,0,0,0.85714285,-1456.5464,275.45191)"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       id="linearGradient15374"
 | 
			
		||||
       xlink:href="#linearGradient10013-4-63-6"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       gradientTransform="translate(-1199.9852,216.38048)"
 | 
			
		||||
       y2="-227.07961"
 | 
			
		||||
       x2="1203.9177"
 | 
			
		||||
       y1="-217.56708"
 | 
			
		||||
       x1="1203.9177"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       id="linearGradient15376"
 | 
			
		||||
       xlink:href="#linearGradient10597-5"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
  </defs>
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#000000"
 | 
			
		||||
     bordercolor="#2d2d2d"
 | 
			
		||||
     borderopacity="1"
 | 
			
		||||
     inkscape:pageopacity="1"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="1"
 | 
			
		||||
     inkscape:cx="6.1225392"
 | 
			
		||||
     inkscape:cy="3.6003241"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     inkscape:window-width="1412"
 | 
			
		||||
     inkscape:window-height="1067"
 | 
			
		||||
     inkscape:window-x="2116"
 | 
			
		||||
     inkscape:window-y="261"
 | 
			
		||||
     inkscape:window-maximized="0"
 | 
			
		||||
     borderlayer="true"
 | 
			
		||||
     inkscape:showpageshadow="false"
 | 
			
		||||
     inkscape:snap-nodes="false"
 | 
			
		||||
     inkscape:snap-bbox="true"
 | 
			
		||||
     showborder="false">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
       id="grid14843"
 | 
			
		||||
       empspacing="5"
 | 
			
		||||
       visible="true"
 | 
			
		||||
       enabled="true"
 | 
			
		||||
       snapvisiblegridlinesonly="true" />
 | 
			
		||||
  </sodipodi:namedview>
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata3204">
 | 
			
		||||
    <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"
 | 
			
		||||
     transform="translate(-342.5,-521.36218)">
 | 
			
		||||
    <g
 | 
			
		||||
       transform="matrix(0.80230061,0,0,0.80230061,-87.624044,-453.10297)"
 | 
			
		||||
       id="g14586"
 | 
			
		||||
       style="stroke-width:2.3714385;stroke-miterlimit:4;stroke-dasharray:none">
 | 
			
		||||
      <g
 | 
			
		||||
         inkscape:export-ydpi="90"
 | 
			
		||||
         inkscape:export-xdpi="90"
 | 
			
		||||
         inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
 | 
			
		||||
         transform="matrix(1.9969286,0,0,1.9969286,-397.05491,877.00482)"
 | 
			
		||||
         id="g15291-9"
 | 
			
		||||
         style="stroke-width:1.18754292;stroke-miterlimit:4;stroke-dasharray:none;display:inline;enable-background:new">
 | 
			
		||||
        <g
 | 
			
		||||
           transform="translate(877.50354,-102.83507)"
 | 
			
		||||
           id="g16853-4"
 | 
			
		||||
           style="stroke-width:1.18754292;stroke-miterlimit:4;stroke-dasharray:none;enable-background:new">
 | 
			
		||||
          <rect
 | 
			
		||||
             transform="scale(1,-1)"
 | 
			
		||||
             style="color:#000000;fill:url(#linearGradient14219);fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:1.24833274;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:new"
 | 
			
		||||
             id="rect6506-6"
 | 
			
		||||
             width="11.281681"
 | 
			
		||||
             height="11.26221"
 | 
			
		||||
             x="-409.59354"
 | 
			
		||||
             y="-284.40115"
 | 
			
		||||
             rx="1.0052766"
 | 
			
		||||
             ry="1.0052764" />
 | 
			
		||||
        </g>
 | 
			
		||||
      </g>
 | 
			
		||||
      <g
 | 
			
		||||
         inkscape:export-ydpi="90"
 | 
			
		||||
         inkscape:export-xdpi="90"
 | 
			
		||||
         inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
 | 
			
		||||
         transform="translate(343.99999,987.99997)"
 | 
			
		||||
         id="g5886"
 | 
			
		||||
         style="stroke-width:2.3714385;stroke-miterlimit:4;stroke-dasharray:none;display:inline;enable-background:new" />
 | 
			
		||||
    </g>
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 6.5 KiB  | 
@@ -1,218 +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="24"
 | 
			
		||||
   height="22"
 | 
			
		||||
   id="svg3199"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.1 r9760"
 | 
			
		||||
   sodipodi:docname="checkbox.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs3201">
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       id="linearGradient15404"
 | 
			
		||||
       inkscape:collect="always">
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop15406"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         style="stop-color:#515151;stop-opacity:1" />
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop15408"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         style="stop-color:#292929;stop-opacity:1" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       sodipodi:type="inkscape:persp3d"
 | 
			
		||||
       inkscape:vp_x="0 : 526.18109 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_z="744.09448 : 526.18109 : 1"
 | 
			
		||||
       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
 | 
			
		||||
       id="perspective3207" />
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       id="perspective3187"
 | 
			
		||||
       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="#linearGradient5872-5-1"
 | 
			
		||||
       id="linearGradient5891-0-4"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       x1="205.84143"
 | 
			
		||||
       y1="246.7094"
 | 
			
		||||
       x2="206.74803"
 | 
			
		||||
       y2="231.24142" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       id="linearGradient5872-5-1">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#0b2e52;stop-opacity:1"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop5874-4-4" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#1862af;stop-opacity:1"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop5876-0-5" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <inkscape:path-effect
 | 
			
		||||
       effect="spiro"
 | 
			
		||||
       id="path-effect5837-4-6"
 | 
			
		||||
       is_visible="true" />
 | 
			
		||||
    <inkscape:path-effect
 | 
			
		||||
       effect="spiro"
 | 
			
		||||
       id="path-effect14768"
 | 
			
		||||
       is_visible="true" />
 | 
			
		||||
    <inkscape:path-effect
 | 
			
		||||
       effect="spiro"
 | 
			
		||||
       id="path-effect5884-4-7"
 | 
			
		||||
       is_visible="true" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       y2="-388.72955"
 | 
			
		||||
       x2="-93.031357"
 | 
			
		||||
       y1="-396.34738"
 | 
			
		||||
       x1="-93.031357"
 | 
			
		||||
       gradientTransform="matrix(1.5918367,0,0,0.85714285,-256.56122,59.685418)"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       id="linearGradient14219"
 | 
			
		||||
       xlink:href="#linearGradient15404"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       id="linearGradient10013-4-63-6">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#333333;stop-opacity:1;"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop10015-2-76-1" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#292929;stop-opacity:1"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop10017-46-15-8" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       id="linearGradient10597-5">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#16191a;stop-opacity:1;"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop10599-2" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#2b3133;stop-opacity:1"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop10601-5" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       y2="-322.16354"
 | 
			
		||||
       x2="921.22498"
 | 
			
		||||
       y1="-330.05121"
 | 
			
		||||
       x1="921.32812"
 | 
			
		||||
       gradientTransform="matrix(1.5918367,0,0,0.85714285,-1456.5464,275.45191)"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       id="linearGradient15374"
 | 
			
		||||
       xlink:href="#linearGradient10013-4-63-6"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       gradientTransform="translate(-1199.9852,216.38048)"
 | 
			
		||||
       y2="-227.07961"
 | 
			
		||||
       x2="1203.9177"
 | 
			
		||||
       y1="-217.56708"
 | 
			
		||||
       x1="1203.9177"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       id="linearGradient15376"
 | 
			
		||||
       xlink:href="#linearGradient10597-5"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
  </defs>
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#000000"
 | 
			
		||||
     bordercolor="#2d2d2d"
 | 
			
		||||
     borderopacity="1"
 | 
			
		||||
     inkscape:pageopacity="1"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="4"
 | 
			
		||||
     inkscape:cx="71.247925"
 | 
			
		||||
     inkscape:cy="33.339093"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     inkscape:window-width="1412"
 | 
			
		||||
     inkscape:window-height="1067"
 | 
			
		||||
     inkscape:window-x="2116"
 | 
			
		||||
     inkscape:window-y="261"
 | 
			
		||||
     inkscape:window-maximized="0"
 | 
			
		||||
     borderlayer="true"
 | 
			
		||||
     inkscape:showpageshadow="false"
 | 
			
		||||
     inkscape:snap-nodes="false"
 | 
			
		||||
     inkscape:snap-bbox="true"
 | 
			
		||||
     showborder="false">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
       id="grid14843"
 | 
			
		||||
       empspacing="5"
 | 
			
		||||
       visible="true"
 | 
			
		||||
       enabled="true"
 | 
			
		||||
       snapvisiblegridlinesonly="true" />
 | 
			
		||||
  </sodipodi:namedview>
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata3204">
 | 
			
		||||
    <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"
 | 
			
		||||
     transform="translate(-342.5,-521.36218)">
 | 
			
		||||
    <g
 | 
			
		||||
       transform="matrix(0.84337,0,0,0.84337,-110.16632,-503.56182)"
 | 
			
		||||
       id="g14586">
 | 
			
		||||
      <g
 | 
			
		||||
         inkscape:export-ydpi="90"
 | 
			
		||||
         inkscape:export-xdpi="90"
 | 
			
		||||
         inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
 | 
			
		||||
         transform="matrix(1.9969286,0,0,1.9969286,-397.05491,877.00482)"
 | 
			
		||||
         id="g15291-9"
 | 
			
		||||
         style="display:inline;enable-background:new">
 | 
			
		||||
        <g
 | 
			
		||||
           transform="translate(877.50354,-102.83507)"
 | 
			
		||||
           id="g16853-4"
 | 
			
		||||
           style="enable-background:new">
 | 
			
		||||
          <rect
 | 
			
		||||
             transform="scale(1,-1)"
 | 
			
		||||
             style="color:#000000;fill:url(#linearGradient14219);fill-opacity:1;fill-rule:nonzero;stroke:#868686;stroke-width:0.59377144999999998;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:new"
 | 
			
		||||
             id="rect6506-6"
 | 
			
		||||
             width="11.281681"
 | 
			
		||||
             height="11.26221"
 | 
			
		||||
             x="-409.59354"
 | 
			
		||||
             y="-284.40115"
 | 
			
		||||
             rx="0.95632279"
 | 
			
		||||
             ry="0.95632273" />
 | 
			
		||||
        </g>
 | 
			
		||||
      </g>
 | 
			
		||||
      <g
 | 
			
		||||
         inkscape:export-ydpi="90"
 | 
			
		||||
         inkscape:export-xdpi="90"
 | 
			
		||||
         inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
 | 
			
		||||
         transform="translate(343.99999,987.99997)"
 | 
			
		||||
         id="g5886"
 | 
			
		||||
         style="display:inline;enable-background:new" />
 | 
			
		||||
    </g>
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 6.8 KiB  | 
@@ -1,243 +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="24"
 | 
			
		||||
   height="22"
 | 
			
		||||
   id="svg3199"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.1 r9760"
 | 
			
		||||
   sodipodi:docname="checkbox-focused.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs3201">
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       id="linearGradient15404"
 | 
			
		||||
       inkscape:collect="always">
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop15406"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         style="stop-color:#515151;stop-opacity:1" />
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop15408"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         style="stop-color:#292929;stop-opacity:1" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       sodipodi:type="inkscape:persp3d"
 | 
			
		||||
       inkscape:vp_x="0 : 526.18109 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_z="744.09448 : 526.18109 : 1"
 | 
			
		||||
       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
 | 
			
		||||
       id="perspective3207" />
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       id="perspective3187"
 | 
			
		||||
       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="#linearGradient5872-5-1"
 | 
			
		||||
       id="linearGradient5891-0-4"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       x1="205.84143"
 | 
			
		||||
       y1="246.7094"
 | 
			
		||||
       x2="206.74803"
 | 
			
		||||
       y2="231.24142" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       id="linearGradient5872-5-1">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#0b2e52;stop-opacity:1"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop5874-4-4" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#1862af;stop-opacity:1"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop5876-0-5" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <inkscape:path-effect
 | 
			
		||||
       effect="spiro"
 | 
			
		||||
       id="path-effect5837-4-6"
 | 
			
		||||
       is_visible="true" />
 | 
			
		||||
    <inkscape:path-effect
 | 
			
		||||
       effect="spiro"
 | 
			
		||||
       id="path-effect14768"
 | 
			
		||||
       is_visible="true" />
 | 
			
		||||
    <inkscape:path-effect
 | 
			
		||||
       effect="spiro"
 | 
			
		||||
       id="path-effect5884-4-7"
 | 
			
		||||
       is_visible="true" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       y2="-388.72955"
 | 
			
		||||
       x2="-93.031357"
 | 
			
		||||
       y1="-396.34738"
 | 
			
		||||
       x1="-93.031357"
 | 
			
		||||
       gradientTransform="matrix(1.5918367,0,0,0.85714285,-256.56122,59.685418)"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       id="linearGradient14219"
 | 
			
		||||
       xlink:href="#linearGradient15404"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       id="linearGradient10013-4-63-6">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#333333;stop-opacity:1;"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop10015-2-76-1" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#292929;stop-opacity:1"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop10017-46-15-8" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       id="linearGradient10597-5">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#16191a;stop-opacity:1;"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop10599-2" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#2b3133;stop-opacity:1"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop10601-5" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       y2="-322.16354"
 | 
			
		||||
       x2="921.22498"
 | 
			
		||||
       y1="-330.05121"
 | 
			
		||||
       x1="921.32812"
 | 
			
		||||
       gradientTransform="matrix(1.5918367,0,0,0.85714285,-1456.5464,275.45191)"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       id="linearGradient15374"
 | 
			
		||||
       xlink:href="#linearGradient10013-4-63-6"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       gradientTransform="translate(-1199.9852,216.38048)"
 | 
			
		||||
       y2="-227.07961"
 | 
			
		||||
       x2="1203.9177"
 | 
			
		||||
       y1="-217.56708"
 | 
			
		||||
       x1="1203.9177"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       id="linearGradient15376"
 | 
			
		||||
       xlink:href="#linearGradient10597-5"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
  </defs>
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#000000"
 | 
			
		||||
     bordercolor="#2d2d2d"
 | 
			
		||||
     borderopacity="1"
 | 
			
		||||
     inkscape:pageopacity="1"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="1"
 | 
			
		||||
     inkscape:cx="64.516955"
 | 
			
		||||
     inkscape:cy="13.871056"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     inkscape:window-width="1412"
 | 
			
		||||
     inkscape:window-height="1067"
 | 
			
		||||
     inkscape:window-x="2635"
 | 
			
		||||
     inkscape:window-y="226"
 | 
			
		||||
     inkscape:window-maximized="0"
 | 
			
		||||
     borderlayer="true"
 | 
			
		||||
     inkscape:showpageshadow="false"
 | 
			
		||||
     inkscape:snap-nodes="false"
 | 
			
		||||
     inkscape:snap-bbox="true"
 | 
			
		||||
     showborder="false">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
       id="grid14843"
 | 
			
		||||
       empspacing="5"
 | 
			
		||||
       visible="true"
 | 
			
		||||
       enabled="true"
 | 
			
		||||
       snapvisiblegridlinesonly="true" />
 | 
			
		||||
  </sodipodi:namedview>
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata3204">
 | 
			
		||||
    <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"
 | 
			
		||||
     transform="translate(-342.5,-521.36218)">
 | 
			
		||||
    <g
 | 
			
		||||
       transform="matrix(0.84337,0,0,0.84337,-110.16632,-503.56182)"
 | 
			
		||||
       id="g14586">
 | 
			
		||||
      <g
 | 
			
		||||
         inkscape:export-ydpi="90"
 | 
			
		||||
         inkscape:export-xdpi="90"
 | 
			
		||||
         inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
 | 
			
		||||
         transform="matrix(1.9969286,0,0,1.9969286,-397.05491,877.00482)"
 | 
			
		||||
         id="g15291-9"
 | 
			
		||||
         style="display:inline;enable-background:new">
 | 
			
		||||
        <g
 | 
			
		||||
           transform="translate(877.50354,-102.83507)"
 | 
			
		||||
           id="g16853-4"
 | 
			
		||||
           style="enable-background:new">
 | 
			
		||||
          <rect
 | 
			
		||||
             transform="scale(1,-1)"
 | 
			
		||||
             style="color:#000000;fill:url(#linearGradient14219);fill-opacity:1;fill-rule:nonzero;stroke:#868686;stroke-width:0.59377144999999998;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:new"
 | 
			
		||||
             id="rect6506-6"
 | 
			
		||||
             width="11.281681"
 | 
			
		||||
             height="11.26221"
 | 
			
		||||
             x="-409.59354"
 | 
			
		||||
             y="-284.40115"
 | 
			
		||||
             rx="0.95632279"
 | 
			
		||||
             ry="0.95632273" />
 | 
			
		||||
        </g>
 | 
			
		||||
      </g>
 | 
			
		||||
      <g
 | 
			
		||||
         inkscape:export-ydpi="90"
 | 
			
		||||
         inkscape:export-xdpi="90"
 | 
			
		||||
         inkscape:export-filename="/home/jimmac/SparkleShare/gnome-mockups/boxes/interactive/img/checkbox-on.png"
 | 
			
		||||
         transform="translate(343.99999,987.99997)"
 | 
			
		||||
         id="g5886"
 | 
			
		||||
         style="display:inline;enable-background:new">
 | 
			
		||||
        <path
 | 
			
		||||
           style="fill:none;stroke:url(#linearGradient5891-0-4);stroke-width:7.11431503;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
 | 
			
		||||
           d="m 198.5,240 5.25,5.25 13.98616,-14.43081"
 | 
			
		||||
           id="path5835"
 | 
			
		||||
           inkscape:path-effect="#path-effect5837-4-6"
 | 
			
		||||
           inkscape:original-d="m 198.5,240 5.25,5.25 13.98616,-14.43081"
 | 
			
		||||
           inkscape:connector-curvature="0"
 | 
			
		||||
           sodipodi:nodetypes="ccc" />
 | 
			
		||||
        <path
 | 
			
		||||
           inkscape:connector-curvature="0"
 | 
			
		||||
           inkscape:original-d="m 198.5,240 5.25,5.25 13.91205,-14.31964"
 | 
			
		||||
           inkscape:path-effect="#path-effect5837-4-6"
 | 
			
		||||
           id="path5880"
 | 
			
		||||
           d="m 198.5,240 5.25,5.25 13.91205,-14.31964"
 | 
			
		||||
           style="fill:none;stroke:#4787c8;stroke-width:3.55715752;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
 | 
			
		||||
           sodipodi:nodetypes="ccc" />
 | 
			
		||||
        <path
 | 
			
		||||
           style="fill:none;stroke:#7ea7d3;stroke-width:1.18571913px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
			
		||||
           d="m 197.45937,240.47455 c -0.17828,-0.29362 -0.20087,-0.67548 -0.0603,-0.98892 0.14055,-0.31344 0.43739,-0.54812 0.77144,-0.62817 0.33405,-0.08 0.69314,-0.01 0.99635,0.15175 0.30321,0.16144 0.55146,0.40727 0.79165,0.65284 l 3.66429,3.74643 12.87946,-12.98973 c 0.20796,-0.20974 0.42306,-0.41969 0.68548,-0.55522 0.26242,-0.13553 0.57293,-0.19052 0.85827,-0.11426 0.14267,0.0381 0.27708,0.10787 0.38874,0.20452 0.11167,0.0966 0.20021,0.22004 0.25479,0.35726 0.0546,0.13722 0.075,0.28793 0.0585,0.43468 -0.0165,0.14674 -0.07,0.28919 -0.15422,0.41052"
 | 
			
		||||
           id="path5882"
 | 
			
		||||
           inkscape:path-effect="#path-effect5884-4-7"
 | 
			
		||||
           inkscape:original-d="m 197.45937,240.47455 c 0.65604,-0.56057 2.02485,-1.34847 2.49911,-0.8125 l 3.66429,3.74643 12.87946,-12.98973 c 0.6875,-0.6875 2.09152,0.7375 2.09152,0.7375"
 | 
			
		||||
           inkscape:connector-curvature="0"
 | 
			
		||||
           sodipodi:nodetypes="csccc" />
 | 
			
		||||
      </g>
 | 
			
		||||
    </g>
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 8.8 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  | 
							
								
								
									
										33
									
								
								data/theme/panel-border.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,33 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   width="3"
 | 
			
		||||
   height="10"
 | 
			
		||||
   id="svg2"
 | 
			
		||||
   version="1.1">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs4" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata7">
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1">
 | 
			
		||||
    <rect
 | 
			
		||||
       style="fill:#000000;fill-opacity:1;stroke-width:0.43599999000000000;stroke-miterlimit:4;stroke-dasharray:none"
 | 
			
		||||
       id="rect3779"
 | 
			
		||||
       width="3"
 | 
			
		||||
       height="10"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="0" />
 | 
			
		||||
    <rect
 | 
			
		||||
       style="fill:#536272;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
 | 
			
		||||
       id="rect3796"
 | 
			
		||||
       width="3"
 | 
			
		||||
       height="1"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="9" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 787 B  | 
@@ -9,7 +9,7 @@
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="17"
 | 
			
		||||
   width="21"
 | 
			
		||||
   height="10"
 | 
			
		||||
   id="svg2"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
@@ -66,9 +66,9 @@
 | 
			
		||||
    <rect
 | 
			
		||||
       style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
 | 
			
		||||
       id="rect3796"
 | 
			
		||||
       width="7"
 | 
			
		||||
       width="3"
 | 
			
		||||
       height="2"
 | 
			
		||||
       x="5"
 | 
			
		||||
       x="9"
 | 
			
		||||
       y="8" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
 
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 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  | 
@@ -9,7 +9,7 @@
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="65"
 | 
			
		||||
   width="64"
 | 
			
		||||
   height="22"
 | 
			
		||||
   id="svg3273"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
 
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB  | 
@@ -9,7 +9,7 @@
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="65"
 | 
			
		||||
   width="64"
 | 
			
		||||
   height="22"
 | 
			
		||||
   id="svg3012"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
 
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 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  | 
@@ -1 +0,0 @@
 | 
			
		||||
SUBDIRS = reference
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
SUBDIRS = shell st
 | 
			
		||||
@@ -1,117 +0,0 @@
 | 
			
		||||
## Process this file with automake to produce Makefile.in
 | 
			
		||||
 | 
			
		||||
# We require automake 1.6 at least.
 | 
			
		||||
AUTOMAKE_OPTIONS = 1.6
 | 
			
		||||
 | 
			
		||||
# This is a blank Makefile.am for using gtk-doc.
 | 
			
		||||
# Copy this to your project's API docs directory and modify the variables to
 | 
			
		||||
# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples
 | 
			
		||||
# of using the various options.
 | 
			
		||||
 | 
			
		||||
# The name of the module, e.g. 'glib'.
 | 
			
		||||
DOC_MODULE=shell
 | 
			
		||||
 | 
			
		||||
# Uncomment for versioned docs and specify the version of the module, e.g. '2'.
 | 
			
		||||
#DOC_MODULE_VERSION=2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# The top-level SGML file. You can change this if you want to.
 | 
			
		||||
DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml
 | 
			
		||||
 | 
			
		||||
# Directories containing the source code
 | 
			
		||||
# gtk-doc will search all .c and .h files beneath these paths
 | 
			
		||||
# for inline comments documenting functions and macros.
 | 
			
		||||
DOC_SOURCE_DIR=$(top_srcdir)/src
 | 
			
		||||
 | 
			
		||||
# Extra options to pass to gtkdoc-scangobj. Not normally needed.
 | 
			
		||||
SCANGOBJ_OPTIONS=
 | 
			
		||||
 | 
			
		||||
# Extra options to supply to gtkdoc-scan.
 | 
			
		||||
# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED"
 | 
			
		||||
SCAN_OPTIONS=--rebuild-types
 | 
			
		||||
 | 
			
		||||
# Extra options to supply to gtkdoc-mkdb.
 | 
			
		||||
# e.g. MKDB_OPTIONS=--xml-mode --output-format=xml
 | 
			
		||||
MKDB_OPTIONS=--xml-mode --output-format=xml
 | 
			
		||||
 | 
			
		||||
# Extra options to supply to gtkdoc-mktmpl
 | 
			
		||||
# e.g. MKTMPL_OPTIONS=--only-section-tmpl
 | 
			
		||||
MKTMPL_OPTIONS=
 | 
			
		||||
 | 
			
		||||
# Extra options to supply to gtkdoc-mkhtml
 | 
			
		||||
MKHTML_OPTIONS=
 | 
			
		||||
 | 
			
		||||
# Extra options to supply to gtkdoc-fixref. Not normally needed.
 | 
			
		||||
# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html
 | 
			
		||||
FIXXREF_OPTIONS=
 | 
			
		||||
 | 
			
		||||
# Used for dependencies. The docs will be rebuilt if any of these change.
 | 
			
		||||
# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h
 | 
			
		||||
# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c
 | 
			
		||||
HFILE_GLOB=$(top_srcdir)/src/*.h
 | 
			
		||||
CFILE_GLOB=$(top_srcdir)/src/*.c
 | 
			
		||||
 | 
			
		||||
# Extra header to include when scanning, which are not under DOC_SOURCE_DIR
 | 
			
		||||
# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h
 | 
			
		||||
EXTRA_HFILES=
 | 
			
		||||
 | 
			
		||||
# Header files or dirs to ignore when scanning. Use base file/dir names
 | 
			
		||||
# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code
 | 
			
		||||
IGNORE_HFILES=					\
 | 
			
		||||
	calendar-server				\
 | 
			
		||||
	gvc					\
 | 
			
		||||
	hotplug-sniffer				\
 | 
			
		||||
	st					\
 | 
			
		||||
	tray					\
 | 
			
		||||
	gactionmuxer.h				\
 | 
			
		||||
	gactionobservable.h			\
 | 
			
		||||
	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=
 | 
			
		||||
 | 
			
		||||
# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
 | 
			
		||||
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
 | 
			
		||||
content_files=
 | 
			
		||||
 | 
			
		||||
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
 | 
			
		||||
# These files must be listed here *and* in content_files
 | 
			
		||||
# e.g. expand_content_files=running.sgml
 | 
			
		||||
expand_content_files=
 | 
			
		||||
 | 
			
		||||
# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
 | 
			
		||||
# Only needed if you are using gtkdoc-scangobj to dynamically query widget
 | 
			
		||||
# signals and properties.
 | 
			
		||||
# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
 | 
			
		||||
# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
 | 
			
		||||
GTKDOC_CFLAGS=$(GNOME_SHELL_CFLAGS)
 | 
			
		||||
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(BLUETOOTH_LIBS) $(top_builddir)/src/libgnome-shell.la
 | 
			
		||||
 | 
			
		||||
# This includes the standard gtk-doc make rules, copied by gtkdocize.
 | 
			
		||||
include $(top_srcdir)/gtk-doc.make
 | 
			
		||||
 | 
			
		||||
# Other files to distribute
 | 
			
		||||
# e.g. EXTRA_DIST += version.xml.in
 | 
			
		||||
EXTRA_DIST +=
 | 
			
		||||
 | 
			
		||||
# Files not to distribute
 | 
			
		||||
# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
 | 
			
		||||
# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
 | 
			
		||||
DISTCLEANFILES = $(DOC_MODULES).types
 | 
			
		||||
 | 
			
		||||
# Comment this out if you want 'make check' to test you doc status
 | 
			
		||||
# and run some sanity checks
 | 
			
		||||
if ENABLE_GTK_DOC
 | 
			
		||||
TESTS_ENVIRONMENT = cd $(srcdir) && \
 | 
			
		||||
  DOC_MODULE=$(DOC_MODULE) DOC_MAIN_SGML_FILE=$(DOC_MAIN_SGML_FILE) \
 | 
			
		||||
  SRCDIR=$(abs_srcdir) BUILDDIR=$(abs_builddir)
 | 
			
		||||
#TESTS = $(GTKDOC_CHECK)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
-include $(top_srcdir)/git.mk
 | 
			
		||||
@@ -1,70 +0,0 @@
 | 
			
		||||
<?xml version="1.0"?>
 | 
			
		||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
 | 
			
		||||
               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
 | 
			
		||||
[
 | 
			
		||||
  <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
 | 
			
		||||
]>
 | 
			
		||||
<book id="index">
 | 
			
		||||
  <bookinfo>
 | 
			
		||||
    <title>Shell Reference Manual</title>
 | 
			
		||||
    <releaseinfo>
 | 
			
		||||
      for Shell @VERSION@.
 | 
			
		||||
      <!--The latest version of this documentation can be found on-line at
 | 
			
		||||
      <ulink role="online-location" url="http://[SERVER]/shell/index.html">http://[SERVER]/shell/</ulink>.-->
 | 
			
		||||
    </releaseinfo>
 | 
			
		||||
  </bookinfo>
 | 
			
		||||
 | 
			
		||||
  <chapter>
 | 
			
		||||
    <title>Actors</title>
 | 
			
		||||
    <xi:include href="xml/shell-generic-container.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-slicer.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-stack.xml"/>
 | 
			
		||||
  </chapter>
 | 
			
		||||
  <chapter>
 | 
			
		||||
    <title>Application tracking</title>
 | 
			
		||||
    <xi:include href="xml/shell-app.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-app-usage.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-window-tracker.xml"/>
 | 
			
		||||
  </chapter>
 | 
			
		||||
  <chapter>
 | 
			
		||||
    <title>Search</title>
 | 
			
		||||
    <xi:include href="xml/shell-app-system.xml"/>
 | 
			
		||||
  </chapter>
 | 
			
		||||
  <chapter>
 | 
			
		||||
    <title>Tray Icons</title>
 | 
			
		||||
    <xi:include href="xml/shell-embedded-window.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-gtk-embed.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-tray-icon.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-tray-manager.xml"/>
 | 
			
		||||
  </chapter>
 | 
			
		||||
  <chapter>
 | 
			
		||||
    <title>Recorder</title>
 | 
			
		||||
    <xi:include href="xml/shell-recorder.xml"/>
 | 
			
		||||
  </chapter>
 | 
			
		||||
  <chapter>
 | 
			
		||||
    <title>Integration helpers and utilities</title>
 | 
			
		||||
    <xi:include href="xml/shell-global.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-wm.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-xfixes-cursor.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-util.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-mount-operation.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-mobile-providers.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-network-agent.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-polkit-authentication-agent.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-tp-client.xml"/>
 | 
			
		||||
  </chapter>
 | 
			
		||||
  <chapter id="object-tree">
 | 
			
		||||
    <title>Object Hierarchy</title>
 | 
			
		||||
     <xi:include href="xml/tree_index.sgml"/>
 | 
			
		||||
  </chapter>
 | 
			
		||||
  <index id="api-index-full">
 | 
			
		||||
    <title>API Index</title>
 | 
			
		||||
    <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
 | 
			
		||||
  </index>
 | 
			
		||||
  <index id="deprecated-api-index" role="deprecated">
 | 
			
		||||
    <title>Index of deprecated API</title>
 | 
			
		||||
    <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
 | 
			
		||||
  </index>
 | 
			
		||||
 | 
			
		||||
  <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
 | 
			
		||||
</book>
 | 
			
		||||
@@ -1,104 +0,0 @@
 | 
			
		||||
## Process this file with automake to produce Makefile.in
 | 
			
		||||
 | 
			
		||||
# We require automake 1.6 at least.
 | 
			
		||||
AUTOMAKE_OPTIONS = 1.6
 | 
			
		||||
 | 
			
		||||
# This is a blank Makefile.am for using gtk-doc.
 | 
			
		||||
# Copy this to your project's API docs directory and modify the variables to
 | 
			
		||||
# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples
 | 
			
		||||
# of using the various options.
 | 
			
		||||
 | 
			
		||||
# The name of the module, e.g. 'glib'.
 | 
			
		||||
DOC_MODULE=st
 | 
			
		||||
 | 
			
		||||
# Uncomment for versioned docs and specify the version of the module, e.g. '2'.
 | 
			
		||||
#DOC_MODULE_VERSION=2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# The top-level SGML file. You can change this if you want to.
 | 
			
		||||
DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml
 | 
			
		||||
 | 
			
		||||
# Directories containing the source code
 | 
			
		||||
# gtk-doc will search all .c and .h files beneath these paths
 | 
			
		||||
# for inline comments documenting functions and macros.
 | 
			
		||||
DOC_SOURCE_DIR=$(top_srcdir)/src/st
 | 
			
		||||
 | 
			
		||||
# Extra options to pass to gtkdoc-scangobj. Not normally needed.
 | 
			
		||||
SCANGOBJ_OPTIONS=
 | 
			
		||||
 | 
			
		||||
# Extra options to supply to gtkdoc-scan.
 | 
			
		||||
# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED"
 | 
			
		||||
SCAN_OPTIONS=--rebuild-types --rebuild-sections
 | 
			
		||||
 | 
			
		||||
# Extra options to supply to gtkdoc-mkdb.
 | 
			
		||||
# e.g. MKDB_OPTIONS=--xml-mode --output-format=xml
 | 
			
		||||
MKDB_OPTIONS=--xml-mode --output-format=xml
 | 
			
		||||
 | 
			
		||||
# Extra options to supply to gtkdoc-mktmpl
 | 
			
		||||
# e.g. MKTMPL_OPTIONS=--only-section-tmpl
 | 
			
		||||
MKTMPL_OPTIONS=
 | 
			
		||||
 | 
			
		||||
# Extra options to supply to gtkdoc-mkhtml
 | 
			
		||||
MKHTML_OPTIONS=
 | 
			
		||||
 | 
			
		||||
# Extra options to supply to gtkdoc-fixref. Not normally needed.
 | 
			
		||||
# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html
 | 
			
		||||
FIXXREF_OPTIONS=
 | 
			
		||||
 | 
			
		||||
# Used for dependencies. The docs will be rebuilt if any of these change.
 | 
			
		||||
# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h
 | 
			
		||||
# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c
 | 
			
		||||
HFILE_GLOB=$(top_srcdir)/src/st/*.h
 | 
			
		||||
CFILE_GLOB=$(top_srcdir)/src/st/*.c
 | 
			
		||||
 | 
			
		||||
# Extra header to include when scanning, which are not under DOC_SOURCE_DIR
 | 
			
		||||
# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h
 | 
			
		||||
EXTRA_HFILES=
 | 
			
		||||
 | 
			
		||||
# Header files or dirs to ignore when scanning. Use base file/dir names
 | 
			
		||||
# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code
 | 
			
		||||
IGNORE_HFILES=st-private.h st-theme-node-private.h
 | 
			
		||||
 | 
			
		||||
# Images to copy into HTML directory.
 | 
			
		||||
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
 | 
			
		||||
HTML_IMAGES=
 | 
			
		||||
 | 
			
		||||
# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
 | 
			
		||||
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
 | 
			
		||||
content_files=
 | 
			
		||||
 | 
			
		||||
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
 | 
			
		||||
# These files must be listed here *and* in content_files
 | 
			
		||||
# e.g. expand_content_files=running.sgml
 | 
			
		||||
expand_content_files=
 | 
			
		||||
 | 
			
		||||
# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
 | 
			
		||||
# Only needed if you are using gtkdoc-scangobj to dynamically query widget
 | 
			
		||||
# signals and properties.
 | 
			
		||||
# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
 | 
			
		||||
# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
 | 
			
		||||
GTKDOC_CFLAGS=
 | 
			
		||||
GTKDOC_LIBS=$(top_builddir)/src/libst-1.0.la
 | 
			
		||||
 | 
			
		||||
# This includes the standard gtk-doc make rules, copied by gtkdocize.
 | 
			
		||||
include $(top_srcdir)/gtk-doc.make
 | 
			
		||||
 | 
			
		||||
# Other files to distribute
 | 
			
		||||
# e.g. EXTRA_DIST += version.xml.in
 | 
			
		||||
EXTRA_DIST +=
 | 
			
		||||
 | 
			
		||||
# Files not to distribute
 | 
			
		||||
# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
 | 
			
		||||
# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
 | 
			
		||||
DISTCLEANFILES = $(DOC_MODULE).types $(DOC_MODULE)-sections.txt
 | 
			
		||||
 | 
			
		||||
# Comment this out if you want 'make check' to test you doc status
 | 
			
		||||
# and run some sanity checks
 | 
			
		||||
if ENABLE_GTK_DOC
 | 
			
		||||
TESTS_ENVIRONMENT = cd $(srcdir) && \
 | 
			
		||||
  DOC_MODULE=$(DOC_MODULE) DOC_MAIN_SGML_FILE=$(DOC_MAIN_SGML_FILE) \
 | 
			
		||||
  SRCDIR=$(abs_srcdir) BUILDDIR=$(abs_builddir)
 | 
			
		||||
#TESTS = $(GTKDOC_CHECK)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
-include $(top_srcdir)/git.mk
 | 
			
		||||
@@ -1,64 +0,0 @@
 | 
			
		||||
<?xml version="1.0"?>
 | 
			
		||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
 | 
			
		||||
               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
 | 
			
		||||
[
 | 
			
		||||
  <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
 | 
			
		||||
]>
 | 
			
		||||
<book id="index">
 | 
			
		||||
  <bookinfo>
 | 
			
		||||
    <title>St Reference Manual</title>
 | 
			
		||||
    <releaseinfo>
 | 
			
		||||
      for St @VERSION@.
 | 
			
		||||
      <!--The latest version of this documentation can be found on-line at
 | 
			
		||||
      <ulink role="online-location" url="http://[SERVER]/st/index.html">http://[SERVER]/st/</ulink>.-->
 | 
			
		||||
    </releaseinfo>
 | 
			
		||||
  </bookinfo>
 | 
			
		||||
 | 
			
		||||
  <part>
 | 
			
		||||
    <title>API reference</title>
 | 
			
		||||
    <chapter id="base">
 | 
			
		||||
      <title>Abstract classes and Interfaces</title>
 | 
			
		||||
      <xi:include href="xml/st-widget.xml"/>
 | 
			
		||||
      <xi:include href="xml/st-widget-accessible.xml"/>
 | 
			
		||||
      <xi:include href="xml/st-scrollable.xml"/>
 | 
			
		||||
    </chapter>
 | 
			
		||||
    <chapter id="widgets">
 | 
			
		||||
      <title>Widgets</title>
 | 
			
		||||
      <xi:include href="xml/st-button.xml"/>
 | 
			
		||||
      <xi:include href="xml/st-drawing-area.xml"/>
 | 
			
		||||
      <xi:include href="xml/st-entry.xml"/>
 | 
			
		||||
      <xi:include href="xml/st-icon.xml"/>
 | 
			
		||||
      <xi:include href="xml/st-label.xml"/>
 | 
			
		||||
    </chapter>
 | 
			
		||||
    <chapter id="containers">
 | 
			
		||||
      <title>Containers</title>
 | 
			
		||||
      <xi:include href="xml/st-bin.xml"/>
 | 
			
		||||
      <xi:include href="xml/st-box-layout.xml"/>
 | 
			
		||||
      <xi:include href="xml/st-scroll-view.xml"/>
 | 
			
		||||
      <xi:include href="xml/st-table.xml"/>
 | 
			
		||||
    </chapter>
 | 
			
		||||
 | 
			
		||||
    <chapter id="styling">
 | 
			
		||||
      <title>Styling</title>
 | 
			
		||||
      <xi:include href="xml/st-theme.xml"/>
 | 
			
		||||
      <xi:include href="xml/st-theme-context.xml"/>
 | 
			
		||||
      <xi:include href="xml/st-theme-node.xml"/>
 | 
			
		||||
      <xi:include href="xml/st-theme-node-transition.xml"/>
 | 
			
		||||
      <xi:include href="xml/st-texture-cache.xml"/>
 | 
			
		||||
    </chapter>
 | 
			
		||||
  </part>
 | 
			
		||||
  <chapter id="object-tree">
 | 
			
		||||
    <title>Object Hierarchy</title>
 | 
			
		||||
     <xi:include href="xml/tree_index.sgml"/>
 | 
			
		||||
  </chapter>
 | 
			
		||||
  <index id="api-index-full">
 | 
			
		||||
    <title>API Index</title>
 | 
			
		||||
    <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
 | 
			
		||||
  </index>
 | 
			
		||||
  <index id="deprecated-api-index" role="deprecated">
 | 
			
		||||
    <title>Index of deprecated API</title>
 | 
			
		||||
    <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
 | 
			
		||||
  </index>
 | 
			
		||||
 | 
			
		||||
  <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
 | 
			
		||||
</book>
 | 
			
		||||
@@ -66,11 +66,4 @@ its dependencies to build from tarballs.</description>
 | 
			
		||||
      <gnome:userid>marinaz</gnome:userid>
 | 
			
		||||
    </foaf:Person>
 | 
			
		||||
  </maintainer>
 | 
			
		||||
  <maintainer>
 | 
			
		||||
    <foaf:Person>
 | 
			
		||||
      <foaf:name>Florian Müllner</foaf:name>
 | 
			
		||||
      <foaf:mbox rdf:resource="mailto:fmuellner@gnome.org" />
 | 
			
		||||
      <gnome:userid>fmuellner</gnome:userid>
 | 
			
		||||
    </foaf:Person>
 | 
			
		||||
  </maintainer>
 | 
			
		||||
</Project>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,108 +1,80 @@
 | 
			
		||||
NULL =
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = misc/config.js.in
 | 
			
		||||
CLEANFILES = misc/config.js
 | 
			
		||||
 | 
			
		||||
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|[@]HAVE_BLUETOOTH@|$(HAVE_BLUETOOTH)|g" \
 | 
			
		||||
	    -e "s|[@]GETTEXT_PACKAGE@|$(GETTEXT_PACKAGE)|g" \
 | 
			
		||||
	    -e "s|[@]datadir@|$(datadir)|g" \
 | 
			
		||||
	    -e "s|[@]libexecdir@|$(libexecdir)|g" \
 | 
			
		||||
	    -e "s|[@]sysconfdir@|$(sysconfdir)|g" \
 | 
			
		||||
               $< > $@
 | 
			
		||||
 | 
			
		||||
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		\
 | 
			
		||||
	extensionPrefs/main.js	\
 | 
			
		||||
	misc/config.js		\
 | 
			
		||||
	misc/extensionUtils.js	\
 | 
			
		||||
	misc/docInfo.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/docDisplay.js	\
 | 
			
		||||
	ui/endSessionDialog.js	\
 | 
			
		||||
	ui/extensionSystem.js	\
 | 
			
		||||
	ui/extensionDownloader.js \
 | 
			
		||||
	ui/environment.js	\
 | 
			
		||||
	ui/flashspot.js		\
 | 
			
		||||
	ui/ibusCandidatePopup.js\
 | 
			
		||||
	ui/grabHelper.js	\
 | 
			
		||||
	ui/extensionSystem.js	\
 | 
			
		||||
	ui/iconGrid.js		\
 | 
			
		||||
	ui/keyboard.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		\
 | 
			
		||||
        ui/screenShield.js	\
 | 
			
		||||
	ui/scripting.js		\
 | 
			
		||||
	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		\
 | 
			
		||||
	ui/viewSelector.js	\
 | 
			
		||||
	ui/wanda.js		\
 | 
			
		||||
	ui/windowAttentionHandler.js	\
 | 
			
		||||
	ui/windowManager.js	\
 | 
			
		||||
	ui/workspace.js		\
 | 
			
		||||
	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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,270 +0,0 @@
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Gettext = imports.gettext;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
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 ExtensionUtils = imports.misc.extensionUtils;
 | 
			
		||||
 | 
			
		||||
const GnomeShellIface = <interface name="org.gnome.Shell.Extensions">
 | 
			
		||||
<signal name="ExtensionStatusChanged">
 | 
			
		||||
    <arg type="s" name="uuid"/>
 | 
			
		||||
    <arg type="i" name="state"/>
 | 
			
		||||
    <arg type="s" name="error"/>
 | 
			
		||||
</signal>
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
 | 
			
		||||
 | 
			
		||||
function stripPrefix(string, prefix) {
 | 
			
		||||
    if (string.slice(0, prefix.length) == prefix)
 | 
			
		||||
        return string.slice(prefix.length);
 | 
			
		||||
    return string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Application = new Lang.Class({
 | 
			
		||||
    Name: 'Application',
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        GLib.set_prgname('gnome-shell-extension-prefs');
 | 
			
		||||
        this.application = new Gtk.Application({
 | 
			
		||||
            application_id: 'org.gnome.shell.ExtensionPrefs',
 | 
			
		||||
            flags: Gio.ApplicationFlags.HANDLES_COMMAND_LINE
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.application.connect('activate', Lang.bind(this, this._onActivate));
 | 
			
		||||
        this.application.connect('command-line', Lang.bind(this, this._onCommandLine));
 | 
			
		||||
        this.application.connect('startup', Lang.bind(this, this._onStartup));
 | 
			
		||||
 | 
			
		||||
        this._extensionPrefsModules = {};
 | 
			
		||||
 | 
			
		||||
        this._extensionIters = {};
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildModel: function() {
 | 
			
		||||
        this._model = new Gtk.ListStore();
 | 
			
		||||
        this._model.set_column_types([GObject.TYPE_STRING, GObject.TYPE_STRING]);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _extensionAvailable: function(uuid) {
 | 
			
		||||
        let extension = ExtensionUtils.extensions[uuid];
 | 
			
		||||
 | 
			
		||||
        if (!extension)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (ExtensionUtils.isOutOfDate(extension))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (!extension.dir.get_child('prefs.js').query_exists(null))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setExtensionInsensitive: function(layout, cell, model, iter, data) {
 | 
			
		||||
        let uuid = model.get_value(iter, 0);
 | 
			
		||||
        cell.set_sensitive(this._extensionAvailable(uuid));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getExtensionPrefsModule: function(extension) {
 | 
			
		||||
        let uuid = extension.metadata.uuid;
 | 
			
		||||
 | 
			
		||||
        if (this._extensionPrefsModules.hasOwnProperty(uuid))
 | 
			
		||||
            return this._extensionPrefsModules[uuid];
 | 
			
		||||
 | 
			
		||||
        ExtensionUtils.installImporter(extension);
 | 
			
		||||
 | 
			
		||||
        let prefsModule = extension.imports.prefs;
 | 
			
		||||
        prefsModule.init(extension.metadata);
 | 
			
		||||
 | 
			
		||||
        this._extensionPrefsModules[uuid] = prefsModule;
 | 
			
		||||
        return prefsModule;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _selectExtension: function(uuid) {
 | 
			
		||||
        if (!this._extensionAvailable(uuid))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let extension = ExtensionUtils.extensions[uuid];
 | 
			
		||||
        let widget;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            let prefsModule = this._getExtensionPrefsModule(extension);
 | 
			
		||||
            widget = prefsModule.buildPrefsWidget();
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            widget = this._buildErrorUI(extension, e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Destroy the current prefs widget, if it exists
 | 
			
		||||
        if (this._extensionPrefsBin.get_child())
 | 
			
		||||
            this._extensionPrefsBin.get_child().destroy();
 | 
			
		||||
 | 
			
		||||
        this._extensionPrefsBin.add(widget);
 | 
			
		||||
        this._extensionSelector.set_active_iter(this._extensionIters[uuid]);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _extensionSelected: function() {
 | 
			
		||||
        let [success, iter] = this._extensionSelector.get_active_iter();
 | 
			
		||||
        if (!success)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let uuid = this._model.get_value(iter, 0);
 | 
			
		||||
        this._selectExtension(uuid);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildErrorUI: function(extension, exc) {
 | 
			
		||||
        let box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
 | 
			
		||||
        let label = new Gtk.Label({
 | 
			
		||||
            label: _("There was an error loading the preferences dialog for %s:").format(extension.metadata.name)
 | 
			
		||||
        });
 | 
			
		||||
        box.add(label);
 | 
			
		||||
 | 
			
		||||
        let errortext = '';
 | 
			
		||||
        errortext += exc;
 | 
			
		||||
        errortext += '\n\n';
 | 
			
		||||
        errortext += 'Stack trace:\n';
 | 
			
		||||
 | 
			
		||||
        // Indent stack trace.
 | 
			
		||||
        errortext += exc.stack.split('\n').map(function(line) {
 | 
			
		||||
            return '  ' + line;
 | 
			
		||||
        }).join('\n');
 | 
			
		||||
 | 
			
		||||
        let scroll = new Gtk.ScrolledWindow({ vexpand: true });
 | 
			
		||||
        let buffer = new Gtk.TextBuffer({ text: errortext });
 | 
			
		||||
        let textview = new Gtk.TextView({ buffer: buffer });
 | 
			
		||||
        textview.override_font(Pango.font_description_from_string('monospace'));
 | 
			
		||||
        scroll.add(textview);
 | 
			
		||||
        box.add(scroll);
 | 
			
		||||
 | 
			
		||||
        box.show_all();
 | 
			
		||||
        return box;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildUI: function(app) {
 | 
			
		||||
        this._window = new Gtk.ApplicationWindow({ application: app,
 | 
			
		||||
                                                   window_position: Gtk.WindowPosition.CENTER,
 | 
			
		||||
                                                   title: _("GNOME Shell Extension Preferences") });
 | 
			
		||||
 | 
			
		||||
        this._window.set_size_request(600, 400);
 | 
			
		||||
 | 
			
		||||
        let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
 | 
			
		||||
        this._window.add(vbox);
 | 
			
		||||
 | 
			
		||||
        let toolbar = new Gtk.Toolbar();
 | 
			
		||||
        toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR);
 | 
			
		||||
        vbox.add(toolbar);
 | 
			
		||||
        let toolitem;
 | 
			
		||||
 | 
			
		||||
        let label = new Gtk.Label({ label: '<b>' + _("Extension") + '</b>',
 | 
			
		||||
                                    use_markup: true });
 | 
			
		||||
        toolitem = new Gtk.ToolItem({ child: label });
 | 
			
		||||
        toolbar.add(toolitem);
 | 
			
		||||
 | 
			
		||||
        this._extensionSelector = new Gtk.ComboBox({ model: this._model,
 | 
			
		||||
                                                     margin_left: 8,
 | 
			
		||||
                                                     hexpand: true });
 | 
			
		||||
        this._extensionSelector.get_style_context().add_class(Gtk.STYLE_CLASS_RAISED);
 | 
			
		||||
 | 
			
		||||
        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.connect('changed', Lang.bind(this, this._extensionSelected));
 | 
			
		||||
 | 
			
		||||
        toolitem = new Gtk.ToolItem({ child: this._extensionSelector });
 | 
			
		||||
        toolitem.set_expand(true);
 | 
			
		||||
        toolbar.add(toolitem);
 | 
			
		||||
 | 
			
		||||
        this._extensionPrefsBin = new Gtk.Frame();
 | 
			
		||||
        vbox.add(this._extensionPrefsBin);
 | 
			
		||||
 | 
			
		||||
        let label = new Gtk.Label({
 | 
			
		||||
            label: _("Select an extension to configure using the combobox above."),
 | 
			
		||||
            vexpand: true
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this._extensionPrefsBin.add(label);
 | 
			
		||||
 | 
			
		||||
        this._shellProxy = new GnomeShellProxy(Gio.DBus.session, 'org.gnome.Shell', '/org/gnome/Shell');
 | 
			
		||||
        this._shellProxy.connectSignal('ExtensionStatusChanged', Lang.bind(this, function(proxy, senderName, [uuid, state, error]) {
 | 
			
		||||
            if (ExtensionUtils.extensions[uuid] !== undefined)
 | 
			
		||||
                this._scanExtensions();
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._window.show_all();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _scanExtensions: function() {
 | 
			
		||||
        let finder = new ExtensionUtils.ExtensionFinder();
 | 
			
		||||
        finder.connect('extension-found', Lang.bind(this, this._extensionFound));
 | 
			
		||||
        finder.scanExtensions();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _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;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    _onActivate: function() {
 | 
			
		||||
        this._window.present();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onStartup: function(app) {
 | 
			
		||||
        this._buildModel();
 | 
			
		||||
        this._buildUI(app);
 | 
			
		||||
        this._scanExtensions();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCommandLine: function(app, commandLine) {
 | 
			
		||||
        app.activate();
 | 
			
		||||
        let args = commandLine.get_arguments();
 | 
			
		||||
        if (args.length) {
 | 
			
		||||
            let uuid = args[0];
 | 
			
		||||
 | 
			
		||||
            // Strip off "extension:///" prefix which fakes a URI, if it exists
 | 
			
		||||
            uuid = stripPrefix(uuid, "extension:///");
 | 
			
		||||
 | 
			
		||||
            if (!this._extensionAvailable(uuid))
 | 
			
		||||
                return 1;
 | 
			
		||||
 | 
			
		||||
            this._selectExtension(uuid);
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function initEnvironment() {
 | 
			
		||||
    // Monkey-patch in a "global" object that fakes some Shell utilities
 | 
			
		||||
    // that ExtensionUtils depends on.
 | 
			
		||||
    window.global = {
 | 
			
		||||
        log: function() {
 | 
			
		||||
            print([].join.call(arguments, ', '));
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        logError: function(s) {
 | 
			
		||||
            log('ERROR: ' + s);
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell'])
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    String.prototype.format = Format.format;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function main(argv) {
 | 
			
		||||
    initEnvironment();
 | 
			
		||||
 | 
			
		||||
    Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
 | 
			
		||||
    Gettext.textdomain(Config.GETTEXT_PACKAGE);
 | 
			
		||||
 | 
			
		||||
    let app = new Application();
 | 
			
		||||
    app.application.run(argv);
 | 
			
		||||
}
 | 
			
		||||
@@ -21,9 +21,11 @@
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const Task = new Lang.Class({
 | 
			
		||||
    Name: 'Task',
 | 
			
		||||
function Task() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Task.prototype = {
 | 
			
		||||
    _init: function(scope, handler) {
 | 
			
		||||
        if (scope)
 | 
			
		||||
            this.scope = scope;
 | 
			
		||||
@@ -39,17 +41,22 @@ const Task = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(Task.prototype);
 | 
			
		||||
 | 
			
		||||
const Hold = new Lang.Class({
 | 
			
		||||
    Name: 'Hold',
 | 
			
		||||
    Extends: Task,
 | 
			
		||||
function Hold() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Hold.prototype = {
 | 
			
		||||
    __proto__: Task.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent(this, function () {
 | 
			
		||||
            return this;
 | 
			
		||||
        });
 | 
			
		||||
        Task.prototype._init.call(this,
 | 
			
		||||
                                  this,
 | 
			
		||||
                                  function () {
 | 
			
		||||
                                      return this;
 | 
			
		||||
                                  });
 | 
			
		||||
 | 
			
		||||
        this._acquisitions = 1;
 | 
			
		||||
    },
 | 
			
		||||
@@ -81,15 +88,18 @@ const Hold = new Lang.Class({
 | 
			
		||||
    isAcquired: function() {
 | 
			
		||||
        return this._acquisitions > 0;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
}
 | 
			
		||||
Signals.addSignalMethods(Hold.prototype);
 | 
			
		||||
 | 
			
		||||
const Batch = new Lang.Class({
 | 
			
		||||
    Name: 'Batch',
 | 
			
		||||
    Extends: Task,
 | 
			
		||||
function Batch() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Batch.prototype = {
 | 
			
		||||
    __proto__: Task.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(scope, tasks) {
 | 
			
		||||
        this.parent();
 | 
			
		||||
        Task.prototype._init.call(this);
 | 
			
		||||
 | 
			
		||||
        this.tasks = [];
 | 
			
		||||
 | 
			
		||||
@@ -156,12 +166,20 @@ const Batch = new Lang.Class({
 | 
			
		||||
    cancel: function() {
 | 
			
		||||
        this.tasks = this.tasks.splice(0, this._currentTaskIndex + 1);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(Batch.prototype);
 | 
			
		||||
 | 
			
		||||
const ConcurrentBatch = new Lang.Class({
 | 
			
		||||
    Name: 'ConcurrentBatch',
 | 
			
		||||
    Extends: Batch,
 | 
			
		||||
function ConcurrentBatch() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConcurrentBatch.prototype = {
 | 
			
		||||
    __proto__: Batch.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(scope, tasks) {
 | 
			
		||||
        Batch.prototype._init.call(this, scope, tasks);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    process: function() {
 | 
			
		||||
       let hold = this.runTask();
 | 
			
		||||
@@ -175,12 +193,19 @@ const ConcurrentBatch = new Lang.Class({
 | 
			
		||||
       // concurrently.
 | 
			
		||||
       this.nextTask();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(ConcurrentBatch.prototype);
 | 
			
		||||
 | 
			
		||||
const ConsecutiveBatch = new Lang.Class({
 | 
			
		||||
    Name: 'ConsecutiveBatch',
 | 
			
		||||
    Extends: Batch,
 | 
			
		||||
function ConsecutiveBatch() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConsecutiveBatch.prototype = {
 | 
			
		||||
    __proto__: Batch.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(scope, tasks) {
 | 
			
		||||
        Batch.prototype._init.call(this, scope, tasks);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    process: function() {
 | 
			
		||||
       let hold = this.runTask();
 | 
			
		||||
@@ -199,5 +224,5 @@ const ConsecutiveBatch = new Lang.Class({
 | 
			
		||||
           this.nextTask();
 | 
			
		||||
       }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(ConsecutiveBatch.prototype);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								js/gdm/consoleKit.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,32 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
 | 
			
		||||
const ConsoleKitManagerIface = {
 | 
			
		||||
    name: 'org.freedesktop.ConsoleKit.Manager',
 | 
			
		||||
    methods: [{ name: 'CanRestart',
 | 
			
		||||
                inSignature: '',
 | 
			
		||||
                outSignature: 'b' },
 | 
			
		||||
              { name: 'CanStop',
 | 
			
		||||
                inSignature: '',
 | 
			
		||||
                outSignature: 'b' },
 | 
			
		||||
              { name: 'Restart',
 | 
			
		||||
                inSignature: '',
 | 
			
		||||
                outSignature: '' },
 | 
			
		||||
              { name: 'Stop',
 | 
			
		||||
                inSignature: '',
 | 
			
		||||
                outSignature: '' }]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function ConsoleKitManager() {
 | 
			
		||||
    this._init();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ConsoleKitManager.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        DBus.system.proxifyObject(this,
 | 
			
		||||
                                  'org.freedesktop.ConsoleKit',
 | 
			
		||||
                                  '/org/freedesktop/ConsoleKit/Manager');
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
DBus.proxifyPrototype(ConsoleKitManager.prototype, ConsoleKitManagerIface);
 | 
			
		||||
@@ -1,26 +1,26 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const FprintManagerIface = <interface name='net.reactivated.Fprint.Manager'>
 | 
			
		||||
<method name='GetDefaultDevice'>
 | 
			
		||||
    <arg type='o' direction='out' />
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
const FprintManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(FprintManagerIface);
 | 
			
		||||
const FprintManagerIface = {
 | 
			
		||||
    name: 'net.reactivated.Fprint.Manager',
 | 
			
		||||
    methods: [{ name: 'GetDefaultDevice',
 | 
			
		||||
                inSignature: '',
 | 
			
		||||
                outSignature: 'o' }]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function FprintManager() {
 | 
			
		||||
    var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
 | 
			
		||||
                                   g_interface_name: FprintManagerInfo.name,
 | 
			
		||||
                                   g_interface_info: FprintManagerInfo,
 | 
			
		||||
                                   g_name: 'net.reactivated.Fprint',
 | 
			
		||||
                                   g_object_path: '/net/reactivated/Fprint/Manager',
 | 
			
		||||
                                   g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
 | 
			
		||||
    this._init();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
    self.init(null);
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
FprintManager.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        DBus.system.proxifyObject(this,
 | 
			
		||||
                                  'net.reactivated.Fprint',
 | 
			
		||||
                                  '/net/reactivated/Fprint/Manager');
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
DBus.proxifyPrototype(FprintManager.prototype, FprintManagerIface);
 | 
			
		||||
 
 | 
			
		||||
@@ -18,31 +18,31 @@
 | 
			
		||||
 * 02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const UPowerGlib = imports.gi.UPowerGlib;
 | 
			
		||||
 | 
			
		||||
const LoginManager = imports.misc.loginManager;
 | 
			
		||||
 | 
			
		||||
const GdmUtil = imports.gdm.util;
 | 
			
		||||
const ConsoleKit = imports.gdm.consoleKit;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
 | 
			
		||||
const PowerMenuButton = new Lang.Class({
 | 
			
		||||
    Name: 'PowerMenuButton',
 | 
			
		||||
    Extends: PanelMenu.SystemStatusButton,
 | 
			
		||||
function PowerMenuButton() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PowerMenuButton.prototype = {
 | 
			
		||||
    __proto__: PanelMenu.SystemStatusButton.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        /* Translators: accessible name of the power menu in the login screen */
 | 
			
		||||
        this.parent('system-shutdown-symbolic', _("Power"));
 | 
			
		||||
 | 
			
		||||
        this._loginManager = LoginManager.getLoginManager();
 | 
			
		||||
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
 | 
			
		||||
        this._settings.connect('changed::disable-restart-buttons',
 | 
			
		||||
                               Lang.bind(this, this._updateVisibility));
 | 
			
		||||
        PanelMenu.SystemStatusButton.prototype._init.call(this, 'system-shutdown', null);
 | 
			
		||||
        this._consoleKitManager = new ConsoleKit.ConsoleKitManager();
 | 
			
		||||
        this._upClient = new UPowerGlib.Client();
 | 
			
		||||
 | 
			
		||||
        this._createSubMenu();
 | 
			
		||||
 | 
			
		||||
        this._upClient.connect('notify::can-suspend',
 | 
			
		||||
                               Lang.bind(this, this._updateHaveSuspend));
 | 
			
		||||
        this._updateHaveSuspend();
 | 
			
		||||
 | 
			
		||||
        // ConsoleKit doesn't send notifications when shutdown/reboot
 | 
			
		||||
        // are disabled, so we update the menu item each time the menu opens
 | 
			
		||||
        this.menu.connect('open-state-changed', Lang.bind(this,
 | 
			
		||||
@@ -50,41 +50,64 @@ const PowerMenuButton = new Lang.Class({
 | 
			
		||||
                if (open) {
 | 
			
		||||
                    this._updateHaveShutdown();
 | 
			
		||||
                    this._updateHaveRestart();
 | 
			
		||||
                    this._updateHaveSuspend();
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
        this._updateHaveShutdown();
 | 
			
		||||
        this._updateHaveRestart();
 | 
			
		||||
        this._updateHaveSuspend();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateVisibility: function() {
 | 
			
		||||
        let shouldBeVisible = (this._haveSuspend || this._haveShutdown || this._haveRestart);
 | 
			
		||||
        this.actor.visible = shouldBeVisible && !this._settings.get_boolean('disable-restart-buttons');
 | 
			
		||||
        if (!this._haveSuspend && !this._haveShutdown && !this._haveRestart)
 | 
			
		||||
            this.actor.hide();
 | 
			
		||||
        else
 | 
			
		||||
            this.actor.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateHaveShutdown: function() {
 | 
			
		||||
        this._loginManager.canPowerOff(Lang.bind(this, function(result) {
 | 
			
		||||
            this._haveShutdown = result;
 | 
			
		||||
            this._powerOffItem.actor.visible = this._haveShutdown;
 | 
			
		||||
            this._updateVisibility();
 | 
			
		||||
        }));
 | 
			
		||||
        this._consoleKitManager.CanStopRemote(Lang.bind(this,
 | 
			
		||||
            function(result, error) {
 | 
			
		||||
                if (!error)
 | 
			
		||||
                    this._haveShutdown = result;
 | 
			
		||||
                else
 | 
			
		||||
                    this._haveShutdown = false;
 | 
			
		||||
 | 
			
		||||
                if (this._haveShutdown) {
 | 
			
		||||
                    this._powerOffItem.actor.show();
 | 
			
		||||
                } else {
 | 
			
		||||
                    this._powerOffItem.actor.hide();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                this._updateVisibility();
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateHaveRestart: function() {
 | 
			
		||||
        this._loginManager.canReboot(Lang.bind(this, function(result) {
 | 
			
		||||
            this._haveRestart = result;
 | 
			
		||||
            this._restartItem.actor.visible = this._haveRestart;
 | 
			
		||||
            this._updateVisibility();
 | 
			
		||||
        }));
 | 
			
		||||
        this._consoleKitManager.CanRestartRemote(Lang.bind(this,
 | 
			
		||||
            function(result, error) {
 | 
			
		||||
                if (!error)
 | 
			
		||||
                    this._haveRestart = result;
 | 
			
		||||
                else
 | 
			
		||||
                    this._haveRestart = false;
 | 
			
		||||
 | 
			
		||||
                if (this._haveRestart) {
 | 
			
		||||
                    this._restartItem.actor.show();
 | 
			
		||||
                } else {
 | 
			
		||||
                    this._restartItem.actor.hide();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                this._updateVisibility();
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateHaveSuspend: function() {
 | 
			
		||||
        this._loginManager.canSuspend(Lang.bind(this, function(result) {
 | 
			
		||||
            this._haveSuspend = result;
 | 
			
		||||
            this._suspendItem.actor.visible = this._haveSuspend;
 | 
			
		||||
            this._updateVisibility();
 | 
			
		||||
        }));
 | 
			
		||||
        this._haveSuspend = this._upClient.get_can_suspend();
 | 
			
		||||
 | 
			
		||||
        if (this._haveSuspend)
 | 
			
		||||
            this._suspendItem.actor.show();
 | 
			
		||||
        else
 | 
			
		||||
            this._suspendItem.actor.hide();
 | 
			
		||||
 | 
			
		||||
        this._updateVisibility();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createSubMenu: function() {
 | 
			
		||||
@@ -107,23 +130,17 @@ const PowerMenuButton = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActivateSuspend: function() {
 | 
			
		||||
        if (!this._haveSuspend)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._loginManager.suspend();
 | 
			
		||||
        if (this._haveSuspend)
 | 
			
		||||
            this._upClient.suspend_sync(null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActivateRestart: function() {
 | 
			
		||||
        if (!this._haveRestart)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._loginManager.reboot();
 | 
			
		||||
        if (this._haveRestart)
 | 
			
		||||
            this._consoleKitManager.RestartRemote();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActivatePowerOff: function() {
 | 
			
		||||
        if (!this._haveShutdown)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._loginManager.powerOff();
 | 
			
		||||
        if (this._haveShutdown)
 | 
			
		||||
            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)
 | 
			
		||||
							
								
								
									
										372
									
								
								js/gdm/util.js
									
									
									
									
									
								
							
							
						
						@@ -1,372 +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';
 | 
			
		||||
const DISABLE_USER_LIST_KEY = 'disable-user-list';
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
        // Clear any previous message
 | 
			
		||||
        this.emit('show-message', null, null);
 | 
			
		||||
 | 
			
		||||
        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.
 | 
			
		||||
 | 
			
		||||
        this._failCounter++;
 | 
			
		||||
        let canRetry = retry && this._userName &&
 | 
			
		||||
            this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
 | 
			
		||||
 | 
			
		||||
        if (canRetry) {
 | 
			
		||||
            this.clear();
 | 
			
		||||
            this.begin(this._userName, new Batch.Hold());
 | 
			
		||||
        } else {
 | 
			
		||||
            // Allow some time to see the message, then reset everything
 | 
			
		||||
            Mainloop.timeout_add(3000, Lang.bind(this, function() {
 | 
			
		||||
                this.cancel();
 | 
			
		||||
 | 
			
		||||
                this._onReset();
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('verification-failed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onConversationStopped: function(client, serviceName) {
 | 
			
		||||
        // if the password service fails, then cancel everything.
 | 
			
		||||
        // But if, e.g., fingerprint fails, still give
 | 
			
		||||
        // password authentication a chance to succeed
 | 
			
		||||
        if (serviceName == PASSWORD_SERVICE_NAME) {
 | 
			
		||||
            this._verificationFailed(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('hide-login-hint');
 | 
			
		||||
 | 
			
		||||
        if (this._realmLoginHintSignalId) {
 | 
			
		||||
            this._realmManager.disconnect(this._realmLoginHintSignalId);
 | 
			
		||||
            this._realmLoginHintSignalId = 0;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ShellUserVerifier.prototype);
 | 
			
		||||
@@ -4,12 +4,9 @@
 | 
			
		||||
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@;
 | 
			
		||||
/* gettext package */
 | 
			
		||||
const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
 | 
			
		||||
/* locale dir */
 | 
			
		||||
const LOCALEDIR = '@datadir@/locale';
 | 
			
		||||
/* other standard directories */
 | 
			
		||||
const LIBEXECDIR = '@libexecdir@';
 | 
			
		||||
const SYSCONFDIR = '@sysconfdir@';
 | 
			
		||||
/* The system TLS CA list */
 | 
			
		||||
const SHELL_SYSTEM_CA_FILE = '@SHELL_SYSTEM_CA_FILE@';
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										140
									
								
								js/misc/docInfo.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,140 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Search = imports.ui.search;
 | 
			
		||||
 | 
			
		||||
const THUMBNAIL_ICON_MARGIN = 2;
 | 
			
		||||
 | 
			
		||||
function DocInfo(recentInfo) {
 | 
			
		||||
    this._init(recentInfo);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DocInfo.prototype = {
 | 
			
		||||
    _init : function(recentInfo) {
 | 
			
		||||
        this.recentInfo = recentInfo;
 | 
			
		||||
        // We actually used get_modified() instead of get_visited()
 | 
			
		||||
        // here, as GtkRecentInfo doesn't updated get_visited()
 | 
			
		||||
        // correctly. See http://bugzilla.gnome.org/show_bug.cgi?id=567094
 | 
			
		||||
        this.timestamp = recentInfo.get_modified();
 | 
			
		||||
        this.name = recentInfo.get_display_name();
 | 
			
		||||
        this._lowerName = this.name.toLowerCase();
 | 
			
		||||
        this.uri = recentInfo.get_uri();
 | 
			
		||||
        this.mimeType = recentInfo.get_mime_type();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createIcon : function(size) {
 | 
			
		||||
        return St.TextureCache.get_default().load_recent_thumbnail(size, this.recentInfo);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    launch : function(workspaceIndex) {
 | 
			
		||||
        Shell.DocSystem.get_default().open(this.recentInfo, workspaceIndex);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
var docManagerInstance = null;
 | 
			
		||||
 | 
			
		||||
function getDocManager() {
 | 
			
		||||
    if (docManagerInstance == null)
 | 
			
		||||
        docManagerInstance = new DocManager();
 | 
			
		||||
    return docManagerInstance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * DocManager wraps the DocSystem, primarily to expose DocInfo objects.
 | 
			
		||||
 */
 | 
			
		||||
function DocManager() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DocManager.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._docSystem = Shell.DocSystem.get_default();
 | 
			
		||||
        this._infosByTimestamp = [];
 | 
			
		||||
        this._infosByUri = {};
 | 
			
		||||
        this._docSystem.connect('changed', Lang.bind(this, this._reload));
 | 
			
		||||
        this._reload();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reload: function() {
 | 
			
		||||
        let docs = this._docSystem.get_all();
 | 
			
		||||
        this._infosByTimestamp = [];
 | 
			
		||||
        this._infosByUri = {};
 | 
			
		||||
        for (let i = 0; i < docs.length; i++) {
 | 
			
		||||
            let recentInfo = docs[i];
 | 
			
		||||
 | 
			
		||||
            let docInfo = new DocInfo(recentInfo);
 | 
			
		||||
            this._infosByTimestamp.push(docInfo);
 | 
			
		||||
            this._infosByUri[docInfo.uri] = docInfo;
 | 
			
		||||
        }
 | 
			
		||||
        this.emit('changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getTimestampOrderedInfos: function() {
 | 
			
		||||
        return this._infosByTimestamp;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getInfosByUri: function() {
 | 
			
		||||
        return this._infosByUri;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    lookupByUri: function(uri) {
 | 
			
		||||
        return this._infosByUri[uri];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    queueExistenceCheck: function(count) {
 | 
			
		||||
        return this._docSystem.queue_existence_check(count);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _searchDocs: function(items, terms) {
 | 
			
		||||
        let multiplePrefixMatches = [];
 | 
			
		||||
        let prefixMatches = [];
 | 
			
		||||
        let multipleSubtringMatches = [];
 | 
			
		||||
        let substringMatches = [];
 | 
			
		||||
        for (let i = 0; i < items.length; i++) {
 | 
			
		||||
            let item = items[i];
 | 
			
		||||
            let mtype = item.matchTerms(terms);
 | 
			
		||||
            if (mtype == Search.MatchType.MULTIPLE_PREFIX)
 | 
			
		||||
                multiplePrefixMatches.push(item.uri);
 | 
			
		||||
            else if (mtype == Search.MatchType.PREFIX)
 | 
			
		||||
                prefixMatches.push(item.uri);
 | 
			
		||||
            else if (mtype == Search.MatchType.MULTIPLE_SUBSTRING)
 | 
			
		||||
                multipleSubtringMatches.push(item.uri);
 | 
			
		||||
            else if (mtype == Search.MatchType.SUBSTRING)
 | 
			
		||||
                substringMatches.push(item.uri);
 | 
			
		||||
         }
 | 
			
		||||
        return multiplePrefixMatches.concat(prefixMatches.concat(multipleSubtringMatches.concat(substringMatches)));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    initialSearch: function(terms) {
 | 
			
		||||
        return this._searchDocs(this._infosByTimestamp, terms);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    subsearch: function(previousResults, terms) {
 | 
			
		||||
        return this._searchDocs(previousResults.map(Lang.bind(this,
 | 
			
		||||
            function(url) {
 | 
			
		||||
                return this._infosByUri[url];
 | 
			
		||||
            })), terms);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(DocManager.prototype);
 | 
			
		||||
@@ -1,202 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
// 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;
 | 
			
		||||
 | 
			
		||||
const Config = imports.misc.config;
 | 
			
		||||
 | 
			
		||||
const ExtensionType = {
 | 
			
		||||
    SYSTEM: 1,
 | 
			
		||||
    PER_USER: 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Maps uuid -> metadata object
 | 
			
		||||
const extensions = {};
 | 
			
		||||
 | 
			
		||||
function getCurrentExtension() {
 | 
			
		||||
    let stack = (new Error()).stack;
 | 
			
		||||
 | 
			
		||||
    // Assuming we're importing this directly from an extension (and we shouldn't
 | 
			
		||||
    // ever not be), its UUID should be directly in the path here.
 | 
			
		||||
    let extensionStackLine = stack.split('\n')[1];
 | 
			
		||||
    if (!extensionStackLine)
 | 
			
		||||
        throw new Error('Could not find current extension');
 | 
			
		||||
 | 
			
		||||
    // The stack line is like:
 | 
			
		||||
    //   init([object Object])@/home/user/data/gnome-shell/extensions/u@u.id/prefs.js:8
 | 
			
		||||
    //
 | 
			
		||||
    // In the case that we're importing from
 | 
			
		||||
    // module scope, the first field is blank:
 | 
			
		||||
    //   @/home/user/data/gnome-shell/extensions/u@u.id/prefs.js:8
 | 
			
		||||
    let match = new RegExp('@(.+):\\d+').exec(extensionStackLine);
 | 
			
		||||
    if (!match)
 | 
			
		||||
        throw new Error('Could not find current extension');
 | 
			
		||||
 | 
			
		||||
    let path = match[1];
 | 
			
		||||
    let file = Gio.File.new_for_path(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();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    throw new Error('Could not find current extension');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * versionCheck:
 | 
			
		||||
 * @required: an array of versions we're compatible with
 | 
			
		||||
 * @current: the version we have
 | 
			
		||||
 *
 | 
			
		||||
 * Check if a component is compatible for an extension.
 | 
			
		||||
 * @required is an array, and at least one version must match.
 | 
			
		||||
 * @current must be in the format <major>.<minor>.<point>.<micro>
 | 
			
		||||
 * <micro> is always ignored
 | 
			
		||||
 * <point> is ignored if <minor> is even (so you can target the
 | 
			
		||||
 * whole stable release)
 | 
			
		||||
 * <minor> and <major> must match
 | 
			
		||||
 * Each target version must be at least <major> and <minor>
 | 
			
		||||
 */
 | 
			
		||||
function versionCheck(required, current) {
 | 
			
		||||
    let currentArray = current.split('.');
 | 
			
		||||
    let major = currentArray[0];
 | 
			
		||||
    let minor = currentArray[1];
 | 
			
		||||
    let point = currentArray[2];
 | 
			
		||||
    for (let i = 0; i < required.length; i++) {
 | 
			
		||||
        let requiredArray = required[i].split('.');
 | 
			
		||||
        if (requiredArray[0] == major &&
 | 
			
		||||
            requiredArray[1] == minor &&
 | 
			
		||||
            (requiredArray[2] == point ||
 | 
			
		||||
             (requiredArray[2] == undefined && parseInt(minor) % 2 == 0)))
 | 
			
		||||
            return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function isOutOfDate(extension) {
 | 
			
		||||
    if (!versionCheck(extension.metadata['shell-version'], Config.PACKAGE_VERSION))
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function createExtensionObject(uuid, dir, type) {
 | 
			
		||||
    let info;
 | 
			
		||||
 | 
			
		||||
    let metadataFile = dir.get_child('metadata.json');
 | 
			
		||||
    if (!metadataFile.query_exists(null)) {
 | 
			
		||||
        throw new Error('Missing metadata.json');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let metadataContents, success, tag;
 | 
			
		||||
    try {
 | 
			
		||||
        [success, metadataContents, tag] = metadataFile.load_contents(null);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        throw new Error('Failed to load metadata.json: ' + e);
 | 
			
		||||
    }
 | 
			
		||||
    let meta;
 | 
			
		||||
    try {
 | 
			
		||||
        meta = JSON.parse(metadataContents);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        throw new Error('Failed to parse metadata.json: ' + e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
 | 
			
		||||
    for (let i = 0; i < requiredProperties.length; i++) {
 | 
			
		||||
        let prop = requiredProperties[i];
 | 
			
		||||
        if (!meta[prop]) {
 | 
			
		||||
            throw new Error('missing "' + prop + '" property in metadata.json');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (uuid != meta.uuid) {
 | 
			
		||||
        throw new Error('uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let extension = {};
 | 
			
		||||
 | 
			
		||||
    extension.metadata = meta;
 | 
			
		||||
    extension.uuid = meta.uuid;
 | 
			
		||||
    extension.type = type;
 | 
			
		||||
    extension.dir = dir;
 | 
			
		||||
    extension.path = dir.get_path();
 | 
			
		||||
    extension.error = '';
 | 
			
		||||
    extension.hasPrefs = dir.get_child('prefs.js').query_exists(null);
 | 
			
		||||
 | 
			
		||||
    extensions[uuid] = extension;
 | 
			
		||||
 | 
			
		||||
    return extension;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _extension = null;
 | 
			
		||||
 | 
			
		||||
function installImporter(extension) {
 | 
			
		||||
    _extension = extension;
 | 
			
		||||
    ShellJS.add_extension_importer('imports.misc.extensionUtils._extension', 'imports', extension.path);
 | 
			
		||||
    _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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ExtensionFinder.prototype);
 | 
			
		||||
@@ -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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										60
									
								
								js/misc/format.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,60 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 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(/%([0-9]+)?(?:\.([0-9]+))?(.)/g, function (str, widthGroup, precisionGroup, genericGroup) {
 | 
			
		||||
 | 
			
		||||
                    if (precisionGroup != '' && genericGroup != 'f')
 | 
			
		||||
                        throw new Error("Precision can only be specified for 'f'");
 | 
			
		||||
 | 
			
		||||
                    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':
 | 
			
		||||
                            s = parseInt(args[i++]).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);
 | 
			
		||||
                });
 | 
			
		||||
}
 | 
			
		||||
@@ -1,18 +1,20 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const PresenceIface = <interface name="org.gnome.SessionManager.Presence">
 | 
			
		||||
<method name="SetStatus">
 | 
			
		||||
    <arg type="u" direction="in"/>
 | 
			
		||||
</method>
 | 
			
		||||
<property name="status" type="u" access="readwrite"/>
 | 
			
		||||
<signal name="StatusChanged">
 | 
			
		||||
    <arg type="u" direction="out"/>
 | 
			
		||||
</signal>
 | 
			
		||||
</interface>;
 | 
			
		||||
const PresenceIface = {
 | 
			
		||||
    name: 'org.gnome.SessionManager.Presence',
 | 
			
		||||
    methods: [{ name: 'SetStatus',
 | 
			
		||||
                inSignature: 'u',
 | 
			
		||||
                outSignature: '' }],
 | 
			
		||||
    properties: [{ name: 'status',
 | 
			
		||||
                   signature: 'u',
 | 
			
		||||
                   access: 'readwrite' }],
 | 
			
		||||
    signals: [{ name: 'StatusChanged',
 | 
			
		||||
                inSignature: 'u' }]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const PresenceStatus = {
 | 
			
		||||
    AVAILABLE: 0,
 | 
			
		||||
@@ -21,52 +23,104 @@ const PresenceStatus = {
 | 
			
		||||
    IDLE: 3
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
var PresenceProxy = Gio.DBusProxy.makeProxyWrapper(PresenceIface);
 | 
			
		||||
function Presence(initCallback, cancellable) {
 | 
			
		||||
    return new PresenceProxy(Gio.DBus.session, 'org.gnome.SessionManager',
 | 
			
		||||
                             '/org/gnome/SessionManager/Presence', initCallback, cancellable);
 | 
			
		||||
function Presence() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Presence.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        DBus.session.proxifyObject(this, 'org.gnome.SessionManager', '/org/gnome/SessionManager/Presence', this);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getStatus: function(callback) {
 | 
			
		||||
        this.GetRemote('status', Lang.bind(this,
 | 
			
		||||
            function(status, ex) {
 | 
			
		||||
                if (!ex)
 | 
			
		||||
                    callback(this, status);
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setStatus: function(status) {
 | 
			
		||||
        this.SetStatusRemote(status);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
DBus.proxifyPrototype(Presence.prototype, PresenceIface);
 | 
			
		||||
 | 
			
		||||
// Note inhibitors are immutable objects, so they don't
 | 
			
		||||
// change at runtime (changes always come in the form
 | 
			
		||||
// of new inhibitors)
 | 
			
		||||
const InhibitorIface = <interface name="org.gnome.SessionManager.Inhibitor">
 | 
			
		||||
<method name="GetAppId">
 | 
			
		||||
    <arg type="s" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GetReason">
 | 
			
		||||
    <arg type="s" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
const InhibitorIface = {
 | 
			
		||||
    name: 'org.gnome.SessionManager.Inhibitor',
 | 
			
		||||
    properties: [{ name: 'app_id',
 | 
			
		||||
                   signature: 's',
 | 
			
		||||
                   access: 'readonly' },
 | 
			
		||||
                 { name: 'client_id',
 | 
			
		||||
                   signature: 's',
 | 
			
		||||
                   access: 'readonly' },
 | 
			
		||||
                 { name: 'reason',
 | 
			
		||||
                   signature: 's',
 | 
			
		||||
                   access: 'readonly' },
 | 
			
		||||
                 { name: 'flags',
 | 
			
		||||
                   signature: 'u',
 | 
			
		||||
                   access: 'readonly' },
 | 
			
		||||
                 { name: 'toplevel_xid',
 | 
			
		||||
                   signature: 'u',
 | 
			
		||||
                   access: 'readonly' },
 | 
			
		||||
                 { name: 'cookie',
 | 
			
		||||
                   signature: 'u',
 | 
			
		||||
                   access: 'readonly' }],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
var InhibitorProxy = Gio.DBusProxy.makeProxyWrapper(InhibitorIface);
 | 
			
		||||
function Inhibitor(objectPath, initCallback, cancellable) {
 | 
			
		||||
    return new InhibitorProxy(Gio.DBus.session, 'org.gnome.SessionManager', objectPath, initCallback, cancellable);
 | 
			
		||||
function Inhibitor(objectPath) {
 | 
			
		||||
    this._init(objectPath);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Inhibitor.prototype = {
 | 
			
		||||
    _init: function(objectPath) {
 | 
			
		||||
        DBus.session.proxifyObject(this,
 | 
			
		||||
                                   'org.gnome.SessionManager',
 | 
			
		||||
                                   objectPath);
 | 
			
		||||
        this.isLoaded = false;
 | 
			
		||||
        this._loadingPropertiesCount = InhibitorIface.properties.length;
 | 
			
		||||
        for (let i = 0; i < InhibitorIface.properties.length; i++) {
 | 
			
		||||
            let propertyName = InhibitorIface.properties[i].name;
 | 
			
		||||
            this.GetRemote(propertyName, Lang.bind(this,
 | 
			
		||||
                function(value, exception) {
 | 
			
		||||
                    if (exception)
 | 
			
		||||
                        return;
 | 
			
		||||
 | 
			
		||||
                    this[propertyName] = value;
 | 
			
		||||
                    this._loadingPropertiesCount--;
 | 
			
		||||
 | 
			
		||||
                    if (this._loadingPropertiesCount == 0) {
 | 
			
		||||
                        this.isLoaded = true;
 | 
			
		||||
                        this.emit('is-loaded');
 | 
			
		||||
                    }
 | 
			
		||||
                }));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
DBus.proxifyPrototype(Inhibitor.prototype, InhibitorIface);
 | 
			
		||||
Signals.addSignalMethods(Inhibitor.prototype);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Not the full interface, only the methods we use
 | 
			
		||||
const SessionManagerIface = <interface name="org.gnome.SessionManager">
 | 
			
		||||
<method name="Logout">
 | 
			
		||||
    <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>;
 | 
			
		||||
const SessionManagerIface = {
 | 
			
		||||
    name: 'org.gnome.SessionManager',
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'Logout', inSignature: 'u', outSignature: '' },
 | 
			
		||||
        { name: 'Shutdown', inSignature: '', outSignature: '' },
 | 
			
		||||
        { name: 'CanShutdown', inSignature: '', outSignature: 'b' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface);
 | 
			
		||||
function SessionManager(initCallback, cancellable) {
 | 
			
		||||
    return new SessionManagerProxy(Gio.DBus.session, 'org.gnome.SessionManager', '/org/gnome/SessionManager', initCallback, cancellable);
 | 
			
		||||
function SessionManager() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SessionManager.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        DBus.session.proxifyObject(this, 'org.gnome.SessionManager', '/org/gnome/SessionManager');
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
DBus.proxifyPrototype(SessionManager.prototype, SessionManagerIface);
 | 
			
		||||
@@ -7,9 +7,11 @@ const Params = imports.misc.params;
 | 
			
		||||
 | 
			
		||||
const DEFAULT_LIMIT = 512;
 | 
			
		||||
 | 
			
		||||
const HistoryManager = new Lang.Class({
 | 
			
		||||
    Name: 'HistoryManager',
 | 
			
		||||
function HistoryManager(params) {
 | 
			
		||||
    this._init(params);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HistoryManager.prototype = {
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        params = Params.parse(params, { gsettingsKey: null,
 | 
			
		||||
                                        limit: DEFAULT_LIMIT,
 | 
			
		||||
@@ -41,26 +43,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 +85,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 +100,8 @@ const HistoryManager = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (this._entry)
 | 
			
		||||
            this._entry.set_text(current);
 | 
			
		||||
 | 
			
		||||
        return current;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _save: function() {
 | 
			
		||||
@@ -107,5 +111,5 @@ const HistoryManager = new Lang.Class({
 | 
			
		||||
        if (this._key)
 | 
			
		||||
            global.settings.set_strv(this._key, this._history);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(HistoryManager.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,246 +0,0 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
// Returns a list of potential completions for text. Completions either
 | 
			
		||||
// follow a dot (e.g. foo.ba -> bar) or they are picked from globalCompletionList (e.g. fo -> foo)
 | 
			
		||||
// commandHeader is prefixed on any expression before it is eval'ed.  It will most likely
 | 
			
		||||
// consist of global constants that might not carry over from the calling environment.
 | 
			
		||||
//
 | 
			
		||||
// This function is likely the one you want to call from external modules
 | 
			
		||||
function getCompletions(text, commandHeader, globalCompletionList) {
 | 
			
		||||
    let methods = [];
 | 
			
		||||
    let expr, base;
 | 
			
		||||
    let attrHead = '';
 | 
			
		||||
    if (globalCompletionList == null) {
 | 
			
		||||
        globalCompletionList = [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let offset = getExpressionOffset(text, text.length - 1);
 | 
			
		||||
    if (offset >= 0) {
 | 
			
		||||
        text = text.slice(offset);
 | 
			
		||||
 | 
			
		||||
        // Look for expressions like "Main.panel.foo" and match Main.panel and foo
 | 
			
		||||
        let matches = text.match(/(.*)\.(.*)/);
 | 
			
		||||
        if (matches) {
 | 
			
		||||
            [expr, base, attrHead] = matches;
 | 
			
		||||
 | 
			
		||||
            methods = getPropertyNamesFromExpression(base, commandHeader).filter(function(attr) {
 | 
			
		||||
                return attr.slice(0, attrHead.length) == attrHead;
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Look for the empty expression or partially entered words
 | 
			
		||||
        // not proceeded by a dot and match them against global constants
 | 
			
		||||
        matches = text.match(/^(\w*)$/);
 | 
			
		||||
        if (text == '' || matches) {
 | 
			
		||||
            [expr, attrHead] = matches;
 | 
			
		||||
            methods = globalCompletionList.filter(function(attr) {
 | 
			
		||||
                return attr.slice(0, attrHead.length) == attrHead;
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return [methods, attrHead];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// A few functions for parsing strings of javascript code.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Identify characters that delimit an expression.  That is,
 | 
			
		||||
// if we encounter anything that isn't a letter, '.', ')', or ']',
 | 
			
		||||
// we should stop parsing.
 | 
			
		||||
function isStopChar(c) {
 | 
			
		||||
    return !c.match(/[\w\.\)\]]/);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Given the ending position of a quoted string, find where it starts
 | 
			
		||||
function findMatchingQuote(expr, offset) {
 | 
			
		||||
    let quoteChar = expr.charAt(offset);
 | 
			
		||||
    for (let i = offset - 1; i >= 0; --i) {
 | 
			
		||||
        if (expr.charAt(i) == quoteChar && expr.charAt(i-1) != '\\'){
 | 
			
		||||
            return i;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Given the ending position of a regex, find where it starts
 | 
			
		||||
function findMatchingSlash(expr, offset) {
 | 
			
		||||
    for (let i = offset - 1; i >= 0; --i) {
 | 
			
		||||
        if (expr.charAt(i) == '/' && expr.charAt(i-1) != '\\'){
 | 
			
		||||
            return i;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// If expr.charAt(offset) is ')' or ']',
 | 
			
		||||
// return the position of the corresponding '(' or '[' bracket.
 | 
			
		||||
// This function does not check for syntactic correctness.  e.g.,
 | 
			
		||||
// findMatchingBrace("[(])", 3) returns 1.
 | 
			
		||||
function findMatchingBrace(expr, offset) {
 | 
			
		||||
    let closeBrace = expr.charAt(offset);
 | 
			
		||||
    let openBrace = ({')': '(', ']': '['})[closeBrace];
 | 
			
		||||
 | 
			
		||||
    function findTheBrace(expr, offset) {
 | 
			
		||||
        if (offset < 0) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (expr.charAt(offset) == openBrace) {
 | 
			
		||||
            return offset;
 | 
			
		||||
        }
 | 
			
		||||
        if (expr.charAt(offset).match(/['"]/)) {
 | 
			
		||||
            return findTheBrace(expr, findMatchingQuote(expr, offset) - 1);
 | 
			
		||||
        }
 | 
			
		||||
        if (expr.charAt(offset) == '/') {
 | 
			
		||||
            return findTheBrace(expr, findMatchingSlash(expr, offset) - 1);
 | 
			
		||||
        }
 | 
			
		||||
        if (expr.charAt(offset) == closeBrace) {
 | 
			
		||||
            return findTheBrace(expr, findTheBrace(expr, offset - 1) - 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return findTheBrace(expr, offset - 1);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return findTheBrace(expr, offset - 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Walk expr backwards from offset looking for the beginning of an
 | 
			
		||||
// expression suitable for passing to eval.
 | 
			
		||||
// There is no guarantee of correct javascript syntax between the return
 | 
			
		||||
// value and offset.  This function is meant to take a string like
 | 
			
		||||
// "foo(Obj.We.Are.Completing" and allow you to extract "Obj.We.Are.Completing"
 | 
			
		||||
function getExpressionOffset(expr, offset) {
 | 
			
		||||
    while (offset >= 0) {
 | 
			
		||||
        let currChar = expr.charAt(offset);
 | 
			
		||||
 | 
			
		||||
        if (isStopChar(currChar)){
 | 
			
		||||
            return offset + 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (currChar.match(/[\)\]]/)) {
 | 
			
		||||
            offset = findMatchingBrace(expr, offset);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        --offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return offset + 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Things with non-word characters or that start with a number
 | 
			
		||||
// are not accessible via .foo notation and so aren't returned
 | 
			
		||||
function isValidPropertyName(w) {
 | 
			
		||||
    return !(w.match(/\W/) || w.match(/^\d/));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// To get all properties (enumerable and not), we need to walk
 | 
			
		||||
// the prototype chain ourselves
 | 
			
		||||
function getAllProps(obj) {
 | 
			
		||||
    if (obj === null || obj === undefined) {
 | 
			
		||||
        return [];
 | 
			
		||||
    }
 | 
			
		||||
    return Object.getOwnPropertyNames(obj).concat( getAllProps(Object.getPrototypeOf(obj)) );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Given a string _expr_, returns all methods
 | 
			
		||||
// that can be accessed via '.' notation.
 | 
			
		||||
// e.g., expr="({ foo: null, bar: null, 4: null })" will
 | 
			
		||||
// return ["foo", "bar", ...] but the list will not include "4",
 | 
			
		||||
// since methods accessed with '.' notation must star with a letter or _.
 | 
			
		||||
function getPropertyNamesFromExpression(expr, commandHeader) {
 | 
			
		||||
    if (commandHeader == null) {
 | 
			
		||||
        commandHeader = '';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let obj = {};
 | 
			
		||||
    if (!isUnsafeExpression(expr)) {
 | 
			
		||||
        try {
 | 
			
		||||
                obj = eval(commandHeader + expr);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        return [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let propsUnique = {};
 | 
			
		||||
    if (typeof obj === 'object'){
 | 
			
		||||
        let allProps = getAllProps(obj);
 | 
			
		||||
        // Get only things we are allowed to complete following a '.'
 | 
			
		||||
        allProps = allProps.filter( isValidPropertyName );
 | 
			
		||||
 | 
			
		||||
        // Make sure propsUnique contains one key for every
 | 
			
		||||
        // property so we end up with a unique list of properties
 | 
			
		||||
        allProps.map(function(p){ propsUnique[p] = null; });
 | 
			
		||||
    }
 | 
			
		||||
    return Object.keys(propsUnique).sort();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Given a list of words, returns the longest prefix they all have in common
 | 
			
		||||
function getCommonPrefix(words) {
 | 
			
		||||
    let word = words[0];
 | 
			
		||||
    for (let i = 0; i < word.length; i++) {
 | 
			
		||||
        for (let w = 1; w < words.length; w++) {
 | 
			
		||||
            if (words[w].charAt(i) != word.charAt(i))
 | 
			
		||||
                return word.slice(0, i);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return word;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns true if there is reason to think that eval(str)
 | 
			
		||||
// will modify the global scope
 | 
			
		||||
function isUnsafeExpression(str) {
 | 
			
		||||
    // Remove any blocks that are quoted or are in a regex
 | 
			
		||||
    function removeLiterals(str) {
 | 
			
		||||
        if (str.length == 0) {
 | 
			
		||||
            return '';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let currChar = str.charAt(str.length - 1);
 | 
			
		||||
        if (currChar == '"' || currChar == '\'') {
 | 
			
		||||
            return removeLiterals(str.slice(0, findMatchingQuote(str, str.length - 1)));
 | 
			
		||||
        } else if (currChar == '/') {
 | 
			
		||||
            return removeLiterals(str.slice(0, findMatchingSlash(str, str.length - 1)));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return removeLiterals(str.slice(0, str.length - 1)) + currChar;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check for any sort of assignment
 | 
			
		||||
    // The strategy used is dumb: remove any quotes
 | 
			
		||||
    // or regexs and comparison operators and see if there is an '=' character.
 | 
			
		||||
    // If there is, it might be an unsafe assignment.
 | 
			
		||||
 | 
			
		||||
    let prunedStr = removeLiterals(str);
 | 
			
		||||
    prunedStr = prunedStr.replace(/[=!]==/g, '');    //replace === and !== with nothing
 | 
			
		||||
    prunedStr = prunedStr.replace(/[=<>!]=/g, '');    //replace ==, <=, >=, != with nothing
 | 
			
		||||
 | 
			
		||||
    if (prunedStr.match(/=/)) {
 | 
			
		||||
        return true;
 | 
			
		||||
    } else if (prunedStr.match(/;/)) {
 | 
			
		||||
        // If we contain a semicolon not inside of a quote/regex, assume we're unsafe as well
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns a list of global keywords derived from str
 | 
			
		||||
function getDeclaredConstants(str) {
 | 
			
		||||
    let ret = [];
 | 
			
		||||
    str.split(';').forEach(function(s) {
 | 
			
		||||
        let base, keyword;
 | 
			
		||||
        let match = s.match(/const\s+(\w+)\s*=/);
 | 
			
		||||
        if (match) {
 | 
			
		||||
            [base, keyword] = match;
 | 
			
		||||
            ret.push(keyword);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,229 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const UPowerGlib = imports.gi.UPowerGlib;
 | 
			
		||||
 | 
			
		||||
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
 | 
			
		||||
<method name='PowerOff'>
 | 
			
		||||
    <arg type='b' direction='in'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='Reboot'>
 | 
			
		||||
    <arg type='b' direction='in'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='Suspend'>
 | 
			
		||||
    <arg type='b' direction='in'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='CanPowerOff'>
 | 
			
		||||
    <arg type='s' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='CanReboot'>
 | 
			
		||||
    <arg type='s' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='CanSuspend'>
 | 
			
		||||
    <arg type='s' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
 | 
			
		||||
<signal name='Lock' />
 | 
			
		||||
<signal name='Unlock' />
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
const SystemdLoginManager = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
 | 
			
		||||
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
 | 
			
		||||
 | 
			
		||||
const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'>
 | 
			
		||||
<method name='CanRestart'>
 | 
			
		||||
    <arg type='b' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='CanStop'>
 | 
			
		||||
    <arg type='b' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='Restart' />
 | 
			
		||||
<method name='Stop' />
 | 
			
		||||
<method name='GetCurrentSession'>
 | 
			
		||||
    <arg type='o' direction='out' />
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'>
 | 
			
		||||
<method name='IsActive'>
 | 
			
		||||
    <arg type='b' direction='out' />
 | 
			
		||||
</method>
 | 
			
		||||
<signal name='ActiveChanged'>
 | 
			
		||||
    <arg type='b' direction='out' />
 | 
			
		||||
</signal>
 | 
			
		||||
<signal name='Lock' />
 | 
			
		||||
<signal name='Unlock' />
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
 | 
			
		||||
const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
 | 
			
		||||
 | 
			
		||||
function haveSystemd() {
 | 
			
		||||
    return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let _loginManager = null;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * LoginManager:
 | 
			
		||||
 * An abstraction over systemd/logind and ConsoleKit.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
function getLoginManager() {
 | 
			
		||||
    if (_loginManager == null) {
 | 
			
		||||
        if (haveSystemd())
 | 
			
		||||
            _loginManager = new LoginManagerSystemd();
 | 
			
		||||
        else
 | 
			
		||||
            _loginManager = new LoginManagerConsoleKit();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return _loginManager;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const LoginManagerSystemd = new Lang.Class({
 | 
			
		||||
    Name: 'LoginManagerSystemd',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._proxy = new SystemdLoginManager(Gio.DBus.system,
 | 
			
		||||
                                              'org.freedesktop.login1',
 | 
			
		||||
                                              '/org/freedesktop/login1');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Having this function is a bit of a hack since the Systemd and ConsoleKit
 | 
			
		||||
    // session objects have different interfaces - but in both cases there are
 | 
			
		||||
    // Lock/Unlock signals, and that's all we count upon at the moment.
 | 
			
		||||
    getCurrentSessionProxy: function() {
 | 
			
		||||
        if (!this._currentSession) {
 | 
			
		||||
            this._currentSession = new SystemdLoginSession(Gio.DBus.system,
 | 
			
		||||
                                                           'org.freedesktop.login1',
 | 
			
		||||
                                                           '/org/freedesktop/login1/session/' +
 | 
			
		||||
                                                           GLib.getenv('XDG_SESSION_ID'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this._currentSession;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get sessionActive() {
 | 
			
		||||
        return Shell.session_is_active_for_systemd();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canPowerOff: function(asyncCallback) {
 | 
			
		||||
        this._proxy.CanPowerOffRemote(function(result, error) {
 | 
			
		||||
            if (error)
 | 
			
		||||
                asyncCallback(false);
 | 
			
		||||
            else
 | 
			
		||||
                asyncCallback(result[0] != 'no');
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canReboot: function(asyncCallback) {
 | 
			
		||||
        this._proxy.CanRebootRemote(function(result, error) {
 | 
			
		||||
            if (error)
 | 
			
		||||
                asyncCallback(false);
 | 
			
		||||
            else
 | 
			
		||||
                asyncCallback(result[0] != 'no');
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canSuspend: function(asyncCallback) {
 | 
			
		||||
        this._proxy.CanSuspendRemote(function(result, error) {
 | 
			
		||||
            if (error)
 | 
			
		||||
                asyncCallback(false);
 | 
			
		||||
            else
 | 
			
		||||
                asyncCallback(result[0] != 'no');
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    powerOff: function() {
 | 
			
		||||
        this._proxy.PowerOffRemote(true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    reboot: function() {
 | 
			
		||||
        this._proxy.RebootRemote(true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    suspend: function() {
 | 
			
		||||
        this._proxy.SuspendRemote(true);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const LoginManagerConsoleKit = new Lang.Class({
 | 
			
		||||
    Name: 'LoginManagerConsoleKit',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._proxy = new ConsoleKitManager(Gio.DBus.system,
 | 
			
		||||
                                            'org.freedesktop.ConsoleKit',
 | 
			
		||||
                                            '/org/freedesktop/ConsoleKit/Manager');
 | 
			
		||||
        this._upClient = new UPowerGlib.Client();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Having this function is a bit of a hack since the Systemd and ConsoleKit
 | 
			
		||||
    // session objects have different interfaces - but in both cases there are
 | 
			
		||||
    // Lock/Unlock signals, and that's all we count upon at the moment.
 | 
			
		||||
    getCurrentSessionProxy: function() {
 | 
			
		||||
        if (!this._currentSession) {
 | 
			
		||||
            let [currentSessionId] = this._proxy.GetCurrentSessionSync();
 | 
			
		||||
            this._currentSession = new ConsoleKitSession(Gio.DBus.system,
 | 
			
		||||
                                                         'org.freedesktop.ConsoleKit',
 | 
			
		||||
                                                         currentSessionId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this._currentSession;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get sessionActive() {
 | 
			
		||||
        if (this._sessionActive !== undefined)
 | 
			
		||||
            return this._sessionActive;
 | 
			
		||||
 | 
			
		||||
        let session = this.getCurrentSessionProxy();
 | 
			
		||||
        session.connectSignal('ActiveChanged', Lang.bind(this, function(object, senderName, [isActive]) {
 | 
			
		||||
            this._sessionActive = isActive;
 | 
			
		||||
        }));
 | 
			
		||||
        [this._sessionActive] = session.IsActiveSync();
 | 
			
		||||
 | 
			
		||||
        return this._sessionActive;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canPowerOff: function(asyncCallback) {
 | 
			
		||||
        this._proxy.CanStopRemote(function(result, error) {
 | 
			
		||||
            if (error)
 | 
			
		||||
                asyncCallback(false);
 | 
			
		||||
            else
 | 
			
		||||
                asyncCallback(result[0]);
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canReboot: function(asyncCallback) {
 | 
			
		||||
        this._proxy.CanRestartRemote(function(result, error) {
 | 
			
		||||
            if (error)
 | 
			
		||||
                asyncCallback(false);
 | 
			
		||||
            else
 | 
			
		||||
                asyncCallback(result[0]);
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canSuspend: function(asyncCallback) {
 | 
			
		||||
        Mainloop.idle_add(Lang.bind(this, function() {
 | 
			
		||||
            asyncCallback(this._upClient.get_can_suspend());
 | 
			
		||||
            return false;
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    powerOff: function() {
 | 
			
		||||
        this._proxy.StopRemote();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    reboot: function() {
 | 
			
		||||
        this._proxy.RestartRemote();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    suspend: function() {
 | 
			
		||||
        this._upClient.suspend_sync(null);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
@@ -8,129 +8,63 @@ const Signals = imports.signals;
 | 
			
		||||
// The following are not the complete interfaces, just the methods we need
 | 
			
		||||
// (or may need in the future)
 | 
			
		||||
 | 
			
		||||
const ModemGsmNetworkInterface = <interface name="org.freedesktop.ModemManager.Modem.Gsm.Network">
 | 
			
		||||
<method name="GetRegistrationInfo">
 | 
			
		||||
    <arg type="(uss)" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GetSignalQuality">
 | 
			
		||||
    <arg type="u" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<property name="AccessTechnology" type="u" access="read" />
 | 
			
		||||
<signal name="SignalQuality">
 | 
			
		||||
    <arg type="u" direction="out" />
 | 
			
		||||
</signal>
 | 
			
		||||
<signal name="RegistrationInfo">
 | 
			
		||||
    <arg type="u" direction="out" />
 | 
			
		||||
    <arg type="s" direction="out" />
 | 
			
		||||
    <arg type="s" direction="out" />
 | 
			
		||||
</signal>
 | 
			
		||||
</interface>;
 | 
			
		||||
const ModemGsmNetworkInterface = {
 | 
			
		||||
    name: 'org.freedesktop.ModemManager.Modem.Gsm.Network',
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'GetRegistrationInfo', inSignature: '', outSignature: 'uss' },
 | 
			
		||||
        { name: 'GetSignalQuality', inSignature: '', outSignature: 'u' }
 | 
			
		||||
    ],
 | 
			
		||||
    properties: [
 | 
			
		||||
        { name: 'AccessTechnology', signature: 'u', access: 'read' }
 | 
			
		||||
    ],
 | 
			
		||||
    signals: [
 | 
			
		||||
        { name: 'SignalQuality', inSignature: 'u' },
 | 
			
		||||
        { name: 'RegistrationInfo', inSignature: 'uss' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
const ModemGsmNetworkProxy = DBus.makeProxyClass(ModemGsmNetworkInterface);
 | 
			
		||||
 | 
			
		||||
const ModemGsmNetworkProxy = Gio.DBusProxy.makeProxyWrapper(ModemGsmNetworkInterface);
 | 
			
		||||
 | 
			
		||||
const ModemCdmaInterface = <interface name="org.freedesktop.ModemManager.Modem.Cdma">
 | 
			
		||||
<method name="GetSignalQuality">
 | 
			
		||||
    <arg type="u" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GetServingSystem">
 | 
			
		||||
    <arg type="(usu)" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<signal name="SignalQuality">
 | 
			
		||||
    <arg type="u" direction="out" />
 | 
			
		||||
</signal>
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
 | 
			
		||||
const ModemCdmaInterface = {
 | 
			
		||||
    name: 'org.freedesktop.ModemManager.Modem.Cdma',
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'GetSignalQuality', inSignature: '', outSignature: 'u' },
 | 
			
		||||
        { name: 'GetServingSystem', inSignature: '', outSignature: 'usu' }
 | 
			
		||||
    ],
 | 
			
		||||
    signals: [
 | 
			
		||||
        { name: 'SignalQuality', inSignature: 'u' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
const ModemCdmaProxy = DBus.makeProxyClass(ModemCdmaInterface);
 | 
			
		||||
 | 
			
		||||
let _providersTable;
 | 
			
		||||
function _getProvidersTable() {
 | 
			
		||||
    if (_providersTable)
 | 
			
		||||
        return _providersTable;
 | 
			
		||||
    return _providersTable = Shell.mobile_providers_parse(null,null);
 | 
			
		||||
    let [providers, countryCodes] = Shell.mobile_providers_parse();
 | 
			
		||||
    return _providersTable = providers;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function findProviderForMCCMNC(table, needle) {
 | 
			
		||||
    let needlemcc = needle.substring(0, 3);
 | 
			
		||||
    let needlemnc = needle.substring(3, needle.length);
 | 
			
		||||
 | 
			
		||||
    let name2, name3;
 | 
			
		||||
    for (let iter in table) {
 | 
			
		||||
        let country = table[iter];
 | 
			
		||||
        let providers = country.get_providers();
 | 
			
		||||
 | 
			
		||||
        // Search through each country's providers
 | 
			
		||||
        for (let i = 0; i < providers.length; i++) {
 | 
			
		||||
            let provider = providers[i];
 | 
			
		||||
 | 
			
		||||
            // Search through MCC/MNC list
 | 
			
		||||
            let list = provider.get_gsm_mcc_mnc();
 | 
			
		||||
            for (let j = 0; j < list.length; j++) {
 | 
			
		||||
                let mccmnc = list[j];
 | 
			
		||||
 | 
			
		||||
                // Match both 2-digit and 3-digit MNC; prefer a
 | 
			
		||||
                // 3-digit match if found, otherwise a 2-digit one.
 | 
			
		||||
                if (mccmnc.mcc != needlemcc)
 | 
			
		||||
                    continue;  // MCC was wrong
 | 
			
		||||
 | 
			
		||||
                if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc)
 | 
			
		||||
                    name3 = provider.name;
 | 
			
		||||
 | 
			
		||||
                if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2))
 | 
			
		||||
                    name2 = provider.name;
 | 
			
		||||
 | 
			
		||||
                if (name2 && name3)
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return name3 || name2 || null;
 | 
			
		||||
function ModemGsm() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function findProviderForSid(table, sid) {
 | 
			
		||||
    if (sid == 0)
 | 
			
		||||
        return null;
 | 
			
		||||
 | 
			
		||||
    // Search through each country
 | 
			
		||||
    for (let iter in table) {
 | 
			
		||||
        let country = table[iter];
 | 
			
		||||
        let providers = country.get_providers();
 | 
			
		||||
 | 
			
		||||
        // Search through each country's providers
 | 
			
		||||
        for (let i = 0; i < providers.length; i++) {
 | 
			
		||||
            let provider = providers[i];
 | 
			
		||||
            let cdma_sid = provider.get_cdma_sid();
 | 
			
		||||
 | 
			
		||||
            // Search through CDMA SID list
 | 
			
		||||
            for (let j = 0; j < cdma_sid.length; j++) {
 | 
			
		||||
                if (cdma_sid[j] == sid)
 | 
			
		||||
                    return provider.name;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ModemGsm = new Lang.Class({
 | 
			
		||||
    Name: 'ModemGsm',
 | 
			
		||||
 | 
			
		||||
ModemGsm.prototype = {
 | 
			
		||||
    _init: function(path) {
 | 
			
		||||
        this._proxy = new ModemGsmNetworkProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path);
 | 
			
		||||
        this._proxy = new ModemGsmNetworkProxy(DBus.system, 'org.freedesktop.ModemManager', path);
 | 
			
		||||
 | 
			
		||||
        this.signal_quality = 0;
 | 
			
		||||
        this.operator_name = null;
 | 
			
		||||
 | 
			
		||||
        // Code is duplicated because the function have different signatures
 | 
			
		||||
        this._proxy.connectSignal('SignalQuality', Lang.bind(this, function(proxy, sender, [quality]) {
 | 
			
		||||
        this._proxy.connect('SignalQuality', Lang.bind(this, function(proxy, quality) {
 | 
			
		||||
            this.signal_quality = quality;
 | 
			
		||||
            this.emit('notify::signal-quality');
 | 
			
		||||
        }));
 | 
			
		||||
        this._proxy.connectSignal('RegistrationInfo', Lang.bind(this, function(proxy, sender, [status, code, name]) {
 | 
			
		||||
        this._proxy.connect('RegistrationInfo', Lang.bind(this, function(proxy, status, code, name) {
 | 
			
		||||
            this.operator_name = this._findOperatorName(name, code);
 | 
			
		||||
            this.emit('notify::operator-name');
 | 
			
		||||
        }));
 | 
			
		||||
        this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function([result], err) {
 | 
			
		||||
        this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function(result, err) {
 | 
			
		||||
            if (err) {
 | 
			
		||||
                log(err);
 | 
			
		||||
                return;
 | 
			
		||||
@@ -153,43 +87,80 @@ const ModemGsm = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _findOperatorName: function(name, opCode) {
 | 
			
		||||
        if (name) {
 | 
			
		||||
            if (name && name.length != 0 && (name.length > 6 || name.length < 5)) {
 | 
			
		||||
                // this looks like a valid name, i.e. not an MCCMNC (that some
 | 
			
		||||
                // devices return when not yet connected
 | 
			
		||||
                return name;
 | 
			
		||||
            }
 | 
			
		||||
            if (isNaN(parseInt(name))) {
 | 
			
		||||
                // name is definitely not a MCCMNC, so it may be a name
 | 
			
		||||
                // after all; return that
 | 
			
		||||
                return name;
 | 
			
		||||
            }
 | 
			
		||||
        if (name.length != 0 && (name.length > 6 || name.length < 5)) {
 | 
			
		||||
            // this looks like a valid name, i.e. not an MCCMNC (that some
 | 
			
		||||
            // devices return when not yet connected
 | 
			
		||||
            return name;
 | 
			
		||||
        }
 | 
			
		||||
        if (isNaN(parseInt(name))) {
 | 
			
		||||
            // name is definitely not a MCCMNC, so it may be a name
 | 
			
		||||
            // after all; return that
 | 
			
		||||
            return name;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let needle;
 | 
			
		||||
        if ((name == null || name.length == 0) && opCode)
 | 
			
		||||
        if (name.length == 0 && opCode)
 | 
			
		||||
            needle = opCode;
 | 
			
		||||
        else if (name.length == 6 || name.length == 5)
 | 
			
		||||
            needle = name;
 | 
			
		||||
        else // nothing to search
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        return this._findProviderForMCCMNC(needle);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _findProviderForMCCMNC: function(needle) {
 | 
			
		||||
        let table = _getProvidersTable();
 | 
			
		||||
        return findProviderForMCCMNC(table, needle);
 | 
			
		||||
        let needlemcc = needle.substring(0, 3);
 | 
			
		||||
        let needlemnc = needle.substring(3, needle.length);
 | 
			
		||||
 | 
			
		||||
        let name2, name3;
 | 
			
		||||
        for (let iter in table) {
 | 
			
		||||
            let providers = table[iter];
 | 
			
		||||
 | 
			
		||||
            // Search through each country's providers
 | 
			
		||||
            for (let i = 0; i < providers.length; i++) {
 | 
			
		||||
                let provider = providers[i];
 | 
			
		||||
 | 
			
		||||
                // Search through MCC/MNC list
 | 
			
		||||
                let list = provider.get_gsm_mcc_mnc();
 | 
			
		||||
                for (let j = 0; j < list.length; j++) {
 | 
			
		||||
                    let mccmnc = list[j];
 | 
			
		||||
 | 
			
		||||
                    // Match both 2-digit and 3-digit MNC; prefer a
 | 
			
		||||
                    // 3-digit match if found, otherwise a 2-digit one.
 | 
			
		||||
                    if (mccmnc.mcc != needlemcc)
 | 
			
		||||
                        continue;  // MCC was wrong
 | 
			
		||||
 | 
			
		||||
                    if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc)
 | 
			
		||||
                        name3 = provider.name;
 | 
			
		||||
 | 
			
		||||
                    if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2))
 | 
			
		||||
                        name2 = provider.name;
 | 
			
		||||
 | 
			
		||||
                    if (name2 && name3)
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return name3 || name2 || null;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
}
 | 
			
		||||
Signals.addSignalMethods(ModemGsm.prototype);
 | 
			
		||||
 | 
			
		||||
const ModemCdma = new Lang.Class({
 | 
			
		||||
    Name: 'ModemCdma',
 | 
			
		||||
function ModemCdma() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ModemCdma.prototype = {
 | 
			
		||||
    _init: function(path) {
 | 
			
		||||
        this._proxy = new ModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path);
 | 
			
		||||
        this._proxy = new ModemCdmaProxy(DBus.system, 'org.freedesktop.ModemManager', path);
 | 
			
		||||
 | 
			
		||||
        this.signal_quality = 0;
 | 
			
		||||
        this.operator_name = null;
 | 
			
		||||
        this._proxy.connectSignal('SignalQuality', Lang.bind(this, function(proxy, sender, params) {
 | 
			
		||||
            this.signal_quality = params[0];
 | 
			
		||||
        this._proxy.connect('SignalQuality', Lang.bind(this, function(proxy, quality) {
 | 
			
		||||
            this.signal_quality = quality;
 | 
			
		||||
            this.emit('notify::signal-quality');
 | 
			
		||||
 | 
			
		||||
            // receiving this signal means the device got activated
 | 
			
		||||
@@ -210,20 +181,45 @@ const ModemCdma = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _refreshServingSystem: function() {
 | 
			
		||||
        this._proxy.GetServingSystemRemote(Lang.bind(this, function([result], err) {
 | 
			
		||||
        this._proxy.GetServingSystemRemote(Lang.bind(this, function(result, err) {
 | 
			
		||||
            if (err) {
 | 
			
		||||
                // it will return an error if the device is not connected
 | 
			
		||||
                this.operator_name = null;
 | 
			
		||||
            } else {
 | 
			
		||||
                let [bandClass, band, id] = result;
 | 
			
		||||
                if (name.length > 0) {
 | 
			
		||||
                    let table = _getProvidersTable();
 | 
			
		||||
                    this.operator_name = findProviderForSid(table, id);
 | 
			
		||||
                } else
 | 
			
		||||
                if (name.length > 0)
 | 
			
		||||
                    this.operator_name = this._findProviderForSid(id);
 | 
			
		||||
                else
 | 
			
		||||
                    this.operator_name = null;
 | 
			
		||||
            }
 | 
			
		||||
            this.emit('notify::operator-name');
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _findProviderForSid: function(sid) {
 | 
			
		||||
        if (sid == 0)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        let table = _getProvidersTable();
 | 
			
		||||
 | 
			
		||||
        // Search through each country
 | 
			
		||||
        for (let iter in table) {
 | 
			
		||||
            let providers = table[iter];
 | 
			
		||||
 | 
			
		||||
            // Search through each country's providers
 | 
			
		||||
            for (let i = 0; i < providers.length; i++) {
 | 
			
		||||
                let provider = providers[i];
 | 
			
		||||
                let cdma_sid = provider.get_cdma_sid();
 | 
			
		||||
 | 
			
		||||
                // Search through CDMA SID list
 | 
			
		||||
                for (let j = 0; j < cdma_sid.length; j++) {
 | 
			
		||||
                    if (cdma_sid[j] == sid)
 | 
			
		||||
                        return provider.name;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(ModemCdma.prototype);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										53
									
								
								js/misc/screenSaver.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,53 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
 | 
			
		||||
const ScreenSaverIface = {
 | 
			
		||||
    name: 'org.gnome.ScreenSaver',
 | 
			
		||||
    methods: [{ name: 'GetActive',
 | 
			
		||||
                inSignature: '',
 | 
			
		||||
                outSignature: 'b' },
 | 
			
		||||
              { name: 'Lock',
 | 
			
		||||
                inSignature: '' },
 | 
			
		||||
              { name: 'SetActive',
 | 
			
		||||
                inSignature: 'b' }],
 | 
			
		||||
    signals: [{ name: 'ActiveChanged',
 | 
			
		||||
                inSignature: 'b' }]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function ScreenSaverProxy() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ScreenSaverProxy.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        DBus.session.proxifyObject(this,
 | 
			
		||||
                                   'org.gnome.ScreenSaver',
 | 
			
		||||
                                   '/org/gnome/ScreenSaver');
 | 
			
		||||
 | 
			
		||||
        DBus.session.watch_name('org.gnome.ScreenSaver',
 | 
			
		||||
                                false, // do not launch a name-owner if none exists
 | 
			
		||||
                                Lang.bind(this, this._onSSAppeared),
 | 
			
		||||
                                Lang.bind(this, this._onSSVanished));
 | 
			
		||||
 | 
			
		||||
        this.screenSaverActive = false;
 | 
			
		||||
        this.connect('ActiveChanged',
 | 
			
		||||
                     Lang.bind(this, this._onActiveChanged));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSSAppeared: function(owner) {
 | 
			
		||||
        this.GetActiveRemote(Lang.bind(this, function(isActive) {
 | 
			
		||||
            this.screenSaverActive = isActive;
 | 
			
		||||
        }))
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSSVanished: function(oldOwner) {
 | 
			
		||||
        this.screenSaverActive = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActiveChanged: function(object, isActive) {
 | 
			
		||||
        this.screenSaverActive = isActive;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
DBus.proxifyPrototype(ScreenSaverProxy.prototype, ScreenSaverIface);
 | 
			
		||||
@@ -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;
 | 
			
		||||
 | 
			
		||||
@@ -80,33 +83,24 @@ function spawnCommandLine(command_line) {
 | 
			
		||||
// this will throw an error.
 | 
			
		||||
function trySpawn(argv)
 | 
			
		||||
{
 | 
			
		||||
    var success, pid;
 | 
			
		||||
    try {
 | 
			
		||||
        [success, pid] = GLib.spawn_async(null, argv, null,
 | 
			
		||||
                                          GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
 | 
			
		||||
                                          null);
 | 
			
		||||
        GLib.spawn_async(null, argv, null,
 | 
			
		||||
                         GLib.SpawnFlags.SEARCH_PATH,
 | 
			
		||||
                         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
 | 
			
		||||
    // can break polkit.  See https://bugzilla.redhat.com//show_bug.cgi?id=819275
 | 
			
		||||
    GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function () {}, null);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// trySpawnCommandLine:
 | 
			
		||||
@@ -150,7 +144,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.
 | 
			
		||||
@@ -238,53 +232,3 @@ function fixupPCIDescription(desc) {
 | 
			
		||||
 | 
			
		||||
    return out.join(' ');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// lowerBound:
 | 
			
		||||
// @array: an array or array-like object, already sorted
 | 
			
		||||
//         according to @cmp
 | 
			
		||||
// @val: the value to add
 | 
			
		||||
// @cmp: a comparator (or undefined to compare as numbers)
 | 
			
		||||
//
 | 
			
		||||
// Returns the position of the first element that is not
 | 
			
		||||
// lower than @val, according to @cmp.
 | 
			
		||||
// That is, returns the first position at which it
 | 
			
		||||
// is possible to insert @val without violating the
 | 
			
		||||
// order.
 | 
			
		||||
// This is quite like an ordinary binary search, except
 | 
			
		||||
// that it doesn't stop at first element comparing equal.
 | 
			
		||||
 | 
			
		||||
function lowerBound(array, val, cmp) {
 | 
			
		||||
    let min, max, mid, v;
 | 
			
		||||
    cmp = cmp || function(a, b) { return a - b; };
 | 
			
		||||
 | 
			
		||||
    if (array.length == 0)
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    min = 0; max = array.length;
 | 
			
		||||
    while (min < (max - 1)) {
 | 
			
		||||
        mid = Math.floor((min + max) / 2);
 | 
			
		||||
        v = cmp(array[mid], val);
 | 
			
		||||
 | 
			
		||||
        if (v < 0)
 | 
			
		||||
            min = mid + 1;
 | 
			
		||||
        else
 | 
			
		||||
            max = mid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (min == max || cmp(array[min], val) < 0) ? max : min;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// insertSorted:
 | 
			
		||||
// @array: an array sorted according to @cmp
 | 
			
		||||
// @val: a value to insert
 | 
			
		||||
// @cmp: the sorting function
 | 
			
		||||
//
 | 
			
		||||
// Inserts @val into @array, preserving the
 | 
			
		||||
// sorting invariants.
 | 
			
		||||
// Returns the position at which it was inserted
 | 
			
		||||
function insertSorted(array, val, cmp) {
 | 
			
		||||
    let pos = lowerBound(array, val, cmp);
 | 
			
		||||
    array.splice(pos, 0, val);
 | 
			
		||||
 | 
			
		||||
    return pos;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										208
									
								
								js/ui/altTab.js
									
									
									
									
									
								
							
							
						
						@@ -2,14 +2,12 @@
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gdk = imports.gi.Gdk;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
@@ -45,9 +43,11 @@ function primaryModifier(mask) {
 | 
			
		||||
    return primary;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AltTabPopup = new Lang.Class({
 | 
			
		||||
    Name: 'AltTabPopup',
 | 
			
		||||
function AltTabPopup() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AltTabPopup.prototype = {
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        this.actor = new Shell.GenericContainer({ name: 'altTabPopup',
 | 
			
		||||
                                                  reactive: true,
 | 
			
		||||
@@ -127,7 +127,7 @@ const AltTabPopup = new Lang.Class({
 | 
			
		||||
            if (childBox.x2 > primary.x + primary.width - rightPadding)
 | 
			
		||||
                childBox.x2 = primary.x + primary.width - rightPadding;
 | 
			
		||||
            childBox.y1 = this._appSwitcher.actor.allocation.y2 + spacing;
 | 
			
		||||
            this._thumbnails.addClones(primary.y + primary.height - bottomPadding - childBox.y1);
 | 
			
		||||
            this._thumbnails.addClones(primary.height - bottomPadding - childBox.y1);
 | 
			
		||||
            let [childMinHeight, childNaturalHeight] = this._thumbnails.actor.get_preferred_height(-1);
 | 
			
		||||
            childBox.y2 = childBox.y1 + childNaturalHeight;
 | 
			
		||||
            this._thumbnails.actor.allocate(childBox, flags);
 | 
			
		||||
@@ -141,7 +141,7 @@ const AltTabPopup = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let screen = global.screen;
 | 
			
		||||
        let display = screen.get_display();
 | 
			
		||||
        let windows = display.get_tab_list(Meta.TabList.NORMAL_ALL, screen,
 | 
			
		||||
        let windows = display.get_tab_list(Meta.TabList.NORMAL, screen,
 | 
			
		||||
                                           screen.get_active_workspace());
 | 
			
		||||
 | 
			
		||||
        // windows is only the windows on the current workspace. For
 | 
			
		||||
@@ -170,12 +170,8 @@ const AltTabPopup = new Lang.Class({
 | 
			
		||||
        if (localApps.length == 0 && otherApps.length == 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (!Main.pushModal(this.actor)) {
 | 
			
		||||
            // Probably someone else has a pointer grab, try again with keyboard only
 | 
			
		||||
            if (!Main.pushModal(this.actor, { options: Meta.ModalOptions.POINTER_ALREADY_GRABBED })) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (!Main.pushModal(this.actor))
 | 
			
		||||
            return false;
 | 
			
		||||
        this._haveModal = true;
 | 
			
		||||
        this._modifierMask = primaryModifier(mask);
 | 
			
		||||
 | 
			
		||||
@@ -199,7 +195,7 @@ const AltTabPopup = new Lang.Class({
 | 
			
		||||
        this.actor.get_allocation_box();
 | 
			
		||||
 | 
			
		||||
        // Make the initial selection
 | 
			
		||||
        if (binding == 'switch-group') {
 | 
			
		||||
        if (binding == 'switch_group') {
 | 
			
		||||
            if (backward) {
 | 
			
		||||
                this._select(0, this._appIcons[0].cachedWindows.length - 1);
 | 
			
		||||
            } else {
 | 
			
		||||
@@ -208,9 +204,9 @@ const AltTabPopup = new Lang.Class({
 | 
			
		||||
                else
 | 
			
		||||
                    this._select(0, 0);
 | 
			
		||||
            }
 | 
			
		||||
        } else if (binding == 'switch-group-backward') {
 | 
			
		||||
        } else if (binding == 'switch_group_backward') {
 | 
			
		||||
            this._select(0, this._appIcons[0].cachedWindows.length - 1);
 | 
			
		||||
        } else if (binding == 'switch-windows-backward') {
 | 
			
		||||
        } else if (binding == 'switch_windows_backward') {
 | 
			
		||||
            this._select(this._appIcons.length - 1);
 | 
			
		||||
        } else if (this._appIcons.length == 1) {
 | 
			
		||||
            this._select(0);
 | 
			
		||||
@@ -266,7 +262,7 @@ const AltTabPopup = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _keyPressEvent : function(actor, event) {
 | 
			
		||||
        let keysym = event.get_key_symbol();
 | 
			
		||||
        let event_state = event.get_state();
 | 
			
		||||
        let event_state = Shell.get_event_state(event);
 | 
			
		||||
        let backwards = event_state & Clutter.ModifierType.SHIFT_MASK;
 | 
			
		||||
        let action = global.display.get_keybinding_action(event.get_key_code(), event_state);
 | 
			
		||||
 | 
			
		||||
@@ -519,7 +515,6 @@ const AltTabPopup = new Lang.Class({
 | 
			
		||||
                                                        })
 | 
			
		||||
                         });
 | 
			
		||||
        this._thumbnails = null;
 | 
			
		||||
        this._appSwitcher._items[this._currentApp].remove_accessible_state (Atk.StateType.EXPANDED);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createThumbnails : function() {
 | 
			
		||||
@@ -540,14 +535,14 @@ const AltTabPopup = new Lang.Class({
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this, function () { this.thumbnailsVisible = true; })
 | 
			
		||||
                         });
 | 
			
		||||
 | 
			
		||||
        this._appSwitcher._items[this._currentApp].add_accessible_state (Atk.StateType.EXPANDED);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const SwitcherList = new Lang.Class({
 | 
			
		||||
    Name: 'SwitcherList',
 | 
			
		||||
function SwitcherList(squareItems) {
 | 
			
		||||
    this._init(squareItems);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SwitcherList.prototype = {
 | 
			
		||||
    _init : function(squareItems) {
 | 
			
		||||
        this.actor = new Shell.GenericContainer({ style_class: 'switcher-list' });
 | 
			
		||||
        this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
 | 
			
		||||
@@ -566,14 +561,14 @@ const SwitcherList = new Lang.Class({
 | 
			
		||||
        this._list.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
 | 
			
		||||
        this._list.connect('allocate', Lang.bind(this, this._allocate));
 | 
			
		||||
 | 
			
		||||
        this._scrollView = new St.ScrollView({ style_class: 'hfade',
 | 
			
		||||
                                               enable_mouse_scrolling: false });
 | 
			
		||||
        this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.NEVER);
 | 
			
		||||
        this._clipBin = new St.Bin({style_class: 'cbin'});
 | 
			
		||||
        this._clipBin.child = this._list;
 | 
			
		||||
        this.actor.add_actor(this._clipBin);
 | 
			
		||||
 | 
			
		||||
        let scrollBox = new St.BoxLayout();
 | 
			
		||||
        scrollBox.add_actor(this._list);
 | 
			
		||||
        this._scrollView.add_actor(scrollBox);
 | 
			
		||||
        this.actor.add_actor(this._scrollView);
 | 
			
		||||
        this._leftGradient = new St.BoxLayout({style_class: 'thumbnail-scroll-gradient-left', vertical: true});
 | 
			
		||||
        this._rightGradient = new St.BoxLayout({style_class: 'thumbnail-scroll-gradient-right', vertical: true});
 | 
			
		||||
        this.actor.add_actor(this._leftGradient);
 | 
			
		||||
        this.actor.add_actor(this._rightGradient);
 | 
			
		||||
 | 
			
		||||
        // Those arrows indicate whether scrolling in one direction is possible
 | 
			
		||||
        this._leftArrow = new St.DrawingArea({ style_class: 'switcher-arrow',
 | 
			
		||||
@@ -604,9 +599,21 @@ const SwitcherList = new Lang.Class({
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
        let scrollable = this._minSize > box.x2 - box.x1;
 | 
			
		||||
 | 
			
		||||
        box.y1 -= this.actor.get_theme_node().get_padding(St.Side.TOP);
 | 
			
		||||
        box.y2 += this.actor.get_theme_node().get_padding(St.Side.BOTTOM);
 | 
			
		||||
        this._scrollView.allocate(box, flags);
 | 
			
		||||
        this._clipBin.allocate(box, flags);
 | 
			
		||||
 | 
			
		||||
        childBox.x1 = 0;
 | 
			
		||||
        childBox.y1 = 0;
 | 
			
		||||
        childBox.x2 = this._leftGradient.width;
 | 
			
		||||
        childBox.y2 = this.actor.height;
 | 
			
		||||
        this._leftGradient.allocate(childBox, flags);
 | 
			
		||||
        this._leftGradient.opacity = (this._scrollableLeft && scrollable) ? 255 : 0;
 | 
			
		||||
 | 
			
		||||
        childBox.x1 = (this.actor.allocation.x2 - this.actor.allocation.x1) - this._rightGradient.width;
 | 
			
		||||
        childBox.y1 = 0;
 | 
			
		||||
        childBox.x2 = childBox.x1 + this._rightGradient.width;
 | 
			
		||||
        childBox.y2 = this.actor.height;
 | 
			
		||||
        this._rightGradient.allocate(childBox, flags);
 | 
			
		||||
        this._rightGradient.opacity = (this._scrollableRight && scrollable) ? 255 : 0;
 | 
			
		||||
 | 
			
		||||
        let arrowWidth = Math.floor(leftPadding / 3);
 | 
			
		||||
        let arrowHeight = arrowWidth * 2;
 | 
			
		||||
@@ -615,7 +622,7 @@ const SwitcherList = new Lang.Class({
 | 
			
		||||
        childBox.x2 = childBox.x1 + arrowWidth;
 | 
			
		||||
        childBox.y2 = childBox.y1 + arrowHeight;
 | 
			
		||||
        this._leftArrow.allocate(childBox, flags);
 | 
			
		||||
        this._leftArrow.opacity = (this._scrollableLeft && scrollable) ? 255 : 0;
 | 
			
		||||
        this._leftArrow.opacity = this._leftGradient.opacity;
 | 
			
		||||
 | 
			
		||||
        arrowWidth = Math.floor(rightPadding / 3);
 | 
			
		||||
        arrowHeight = arrowWidth * 2;
 | 
			
		||||
@@ -624,7 +631,7 @@ const SwitcherList = new Lang.Class({
 | 
			
		||||
        childBox.x2 = childBox.x1 + arrowWidth;
 | 
			
		||||
        childBox.y2 = childBox.y1 + arrowHeight;
 | 
			
		||||
        this._rightArrow.allocate(childBox, flags);
 | 
			
		||||
        this._rightArrow.opacity = (this._scrollableRight && scrollable) ? 255 : 0;
 | 
			
		||||
        this._rightArrow.opacity = this._rightGradient.opacity;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addItem : function(item, label) {
 | 
			
		||||
@@ -641,8 +648,6 @@ const SwitcherList = new Lang.Class({
 | 
			
		||||
        bbox.label_actor = label;
 | 
			
		||||
 | 
			
		||||
        this._items.push(bbox);
 | 
			
		||||
 | 
			
		||||
        return bbox;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onItemClicked: function (index) {
 | 
			
		||||
@@ -674,66 +679,47 @@ const SwitcherList = new Lang.Class({
 | 
			
		||||
                this._items[this._highlighted].add_style_pseudo_class('selected');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let adjustment = this._scrollView.hscroll.adjustment;
 | 
			
		||||
        let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
 | 
			
		||||
        let [absItemX, absItemY] = this._items[index].get_transformed_position();
 | 
			
		||||
        let [result, posX, posY] = this.actor.transform_stage_point(absItemX, 0);
 | 
			
		||||
        let [containerWidth, containerHeight] = this.actor.get_transformed_size();
 | 
			
		||||
        if (posX + this._items[index].get_width() > containerWidth)
 | 
			
		||||
            this._scrollToRight();
 | 
			
		||||
        else if (this._items[index].allocation.x1 - value < 0)
 | 
			
		||||
        else if (posX < 0)
 | 
			
		||||
            this._scrollToLeft();
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _scrollToLeft : function() {
 | 
			
		||||
        let adjustment = this._scrollView.hscroll.adjustment;
 | 
			
		||||
        let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
 | 
			
		||||
 | 
			
		||||
        let item = this._items[this._highlighted];
 | 
			
		||||
 | 
			
		||||
        if (item.allocation.x1 < value)
 | 
			
		||||
            value = Math.min(0, item.allocation.x1);
 | 
			
		||||
        else if (item.allocation.x2 > value + pageSize)
 | 
			
		||||
            value = Math.max(upper, item.allocation.x2 - pageSize);
 | 
			
		||||
 | 
			
		||||
        let x = this._items[this._highlighted].allocation.x1;
 | 
			
		||||
        this._scrollableRight = true;
 | 
			
		||||
        Tweener.addTween(adjustment,
 | 
			
		||||
                         { value: value,
 | 
			
		||||
                           time: POPUP_SCROLL_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this, function () {
 | 
			
		||||
                                if (this._highlighted == 0) {
 | 
			
		||||
                                    this._scrollableLeft = false;
 | 
			
		||||
                                    this.actor.queue_relayout();
 | 
			
		||||
                                }
 | 
			
		||||
                           })
 | 
			
		||||
                          });
 | 
			
		||||
        Tweener.addTween(this._list, { anchor_x: x,
 | 
			
		||||
                                        time: POPUP_SCROLL_TIME,
 | 
			
		||||
                                        transition: 'easeOutQuad',
 | 
			
		||||
                                        onComplete: Lang.bind(this, function () {
 | 
			
		||||
                                                                        if (this._highlighted == 0) {
 | 
			
		||||
                                                                            this._scrollableLeft = false;
 | 
			
		||||
                                                                            this.actor.queue_relayout();
 | 
			
		||||
                                                                        }
 | 
			
		||||
                                                             })
 | 
			
		||||
                        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _scrollToRight : function() {
 | 
			
		||||
        let adjustment = this._scrollView.hscroll.adjustment;
 | 
			
		||||
        let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
 | 
			
		||||
 | 
			
		||||
        let item = this._items[this._highlighted];
 | 
			
		||||
 | 
			
		||||
        if (item.allocation.x1 < value)
 | 
			
		||||
            value = Math.max(0, item.allocation.x1);
 | 
			
		||||
        else if (item.allocation.x2 > value + pageSize)
 | 
			
		||||
            value = Math.min(upper, item.allocation.x2 - pageSize);
 | 
			
		||||
 | 
			
		||||
        this._scrollableLeft = true;
 | 
			
		||||
        Tweener.addTween(adjustment,
 | 
			
		||||
                         { value: value,
 | 
			
		||||
                           time: POPUP_SCROLL_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this, function () {
 | 
			
		||||
                                if (this._highlighted == this._items.length - 1) {
 | 
			
		||||
                                    this._scrollableRight = false;
 | 
			
		||||
                                    this.actor.queue_relayout();
 | 
			
		||||
                                }
 | 
			
		||||
                            })
 | 
			
		||||
                          });
 | 
			
		||||
        let monitor = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        let padding = this.actor.get_theme_node().get_horizontal_padding();
 | 
			
		||||
        let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
 | 
			
		||||
        let x = this._items[this._highlighted].allocation.x2 - monitor.width + padding + parentPadding;
 | 
			
		||||
        Tweener.addTween(this._list, { anchor_x: x,
 | 
			
		||||
                                        time: POPUP_SCROLL_TIME,
 | 
			
		||||
                                        transition: 'easeOutQuad',
 | 
			
		||||
                                        onComplete: Lang.bind(this, function () {
 | 
			
		||||
                                                                        if (this._highlighted == this._items.length - 1) {
 | 
			
		||||
                                                                            this._scrollableRight = false;
 | 
			
		||||
                                                                            this.actor.queue_relayout();
 | 
			
		||||
                                                                        }
 | 
			
		||||
                                                             })
 | 
			
		||||
                        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _itemActivated: function(n) {
 | 
			
		||||
@@ -819,6 +805,14 @@ const SwitcherList = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        let parentRightPadding = this.actor.get_parent().get_theme_node().get_padding(St.Side.RIGHT);
 | 
			
		||||
        if (this.actor.allocation.x2 == primary.x + primary.width - parentRightPadding) {
 | 
			
		||||
            if (this._squareItems)
 | 
			
		||||
                childWidth = childHeight;
 | 
			
		||||
            else {
 | 
			
		||||
                let [childMin, childNat] = children[0].get_preferred_width(childHeight);
 | 
			
		||||
                childWidth = childMin;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < children.length; i++) {
 | 
			
		||||
            if (this._items.indexOf(children[i]) != -1) {
 | 
			
		||||
@@ -844,14 +838,24 @@ const SwitcherList = new Lang.Class({
 | 
			
		||||
                // we don't allocate it.
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
 | 
			
		||||
        let rightPadding = this.actor.get_theme_node().get_padding(St.Side.RIGHT);
 | 
			
		||||
        let topPadding = this.actor.get_theme_node().get_padding(St.Side.TOP);
 | 
			
		||||
        let bottomPadding = this.actor.get_theme_node().get_padding(St.Side.BOTTOM);
 | 
			
		||||
 | 
			
		||||
        // Clip the area for scrolling
 | 
			
		||||
        this._clipBin.set_clip(0, -topPadding, (this.actor.allocation.x2 - this.actor.allocation.x1) - leftPadding - rightPadding, this.actor.height + bottomPadding);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(SwitcherList.prototype);
 | 
			
		||||
 | 
			
		||||
const AppIcon = new Lang.Class({
 | 
			
		||||
    Name: 'AppIcon',
 | 
			
		||||
function AppIcon(app) {
 | 
			
		||||
    this._init(app);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AppIcon.prototype = {
 | 
			
		||||
    _init: function(app) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'alt-tab-app',
 | 
			
		||||
@@ -869,14 +873,17 @@ const AppIcon = new Lang.Class({
 | 
			
		||||
        this._iconBin.set_size(size, size);
 | 
			
		||||
        this._iconBin.child = this.icon;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AppSwitcher = new Lang.Class({
 | 
			
		||||
    Name: 'AppSwitcher',
 | 
			
		||||
    Extends: SwitcherList,
 | 
			
		||||
function AppSwitcher() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AppSwitcher.prototype = {
 | 
			
		||||
    __proto__ : SwitcherList.prototype,
 | 
			
		||||
 | 
			
		||||
    _init : function(localApps, otherApps, altTabPopup) {
 | 
			
		||||
        this.parent(true);
 | 
			
		||||
        SwitcherList.prototype._init.call(this, true);
 | 
			
		||||
 | 
			
		||||
        // Construct the AppIcons, add to the popup
 | 
			
		||||
        let activeWorkspace = global.screen.get_active_workspace();
 | 
			
		||||
@@ -955,7 +962,7 @@ const AppSwitcher = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _allocate: function (actor, box, flags) {
 | 
			
		||||
        // Allocate the main list items
 | 
			
		||||
        this.parent(actor, box, flags);
 | 
			
		||||
        SwitcherList.prototype._allocate.call(this, actor, box, flags);
 | 
			
		||||
 | 
			
		||||
        let arrowHeight = Math.floor(this.actor.get_theme_node().get_padding(St.Side.BOTTOM) / 3);
 | 
			
		||||
        let arrowWidth = arrowHeight * 2;
 | 
			
		||||
@@ -1010,7 +1017,7 @@ const AppSwitcher = new Lang.Class({
 | 
			
		||||
                this._arrows[this._curApp].remove_style_pseudo_class('highlighted');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.parent(n, justOutline);
 | 
			
		||||
        SwitcherList.prototype.highlight.call(this, n, justOutline);
 | 
			
		||||
        this._curApp = n;
 | 
			
		||||
 | 
			
		||||
        if (this._curApp != -1) {
 | 
			
		||||
@@ -1023,7 +1030,7 @@ const AppSwitcher = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _addIcon : function(appIcon) {
 | 
			
		||||
        this.icons.push(appIcon);
 | 
			
		||||
        let item = this.addItem(appIcon.actor, appIcon.label);
 | 
			
		||||
        this.addItem(appIcon.actor, appIcon.label);
 | 
			
		||||
 | 
			
		||||
        let n = this._arrows.length;
 | 
			
		||||
        let arrow = new St.DrawingArea({ style_class: 'switcher-arrow' });
 | 
			
		||||
@@ -1033,17 +1040,18 @@ const AppSwitcher = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (appIcon.cachedWindows.length == 1)
 | 
			
		||||
            arrow.hide();
 | 
			
		||||
        else
 | 
			
		||||
            item.add_accessible_state (Atk.StateType.EXPANDABLE);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ThumbnailList = new Lang.Class({
 | 
			
		||||
    Name: 'ThumbnailList',
 | 
			
		||||
    Extends: SwitcherList,
 | 
			
		||||
function ThumbnailList(windows) {
 | 
			
		||||
    this._init(windows);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ThumbnailList.prototype = {
 | 
			
		||||
    __proto__ : SwitcherList.prototype,
 | 
			
		||||
 | 
			
		||||
    _init : function(windows) {
 | 
			
		||||
        this.parent(false);
 | 
			
		||||
        SwitcherList.prototype._init.call(this);
 | 
			
		||||
 | 
			
		||||
        let activeWorkspace = global.screen.get_active_workspace();
 | 
			
		||||
 | 
			
		||||
@@ -1121,7 +1129,7 @@ const ThumbnailList = new Lang.Class({
 | 
			
		||||
        // Make sure we only do this once
 | 
			
		||||
        this._thumbnailBins = new Array();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function _drawArrow(area, side) {
 | 
			
		||||
    let themeNode = area.get_theme_node();
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,6 @@ const Signals = imports.signals;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
 | 
			
		||||
const AppFavorites = imports.ui.appFavorites;
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
@@ -22,22 +21,22 @@ 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;
 | 
			
		||||
const SCROLL_TIME = 0.1;
 | 
			
		||||
 | 
			
		||||
const AlphabeticalView = new Lang.Class({
 | 
			
		||||
    Name: 'AlphabeticalView',
 | 
			
		||||
function AlphabeticalView() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AlphabeticalView.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._grid = new IconGrid.IconGrid({ xAlign: St.Align.START });
 | 
			
		||||
        this._appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
 | 
			
		||||
        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 +61,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,12 +121,22 @@ 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);
 | 
			
		||||
         }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ViewByCategories = new Lang.Class({
 | 
			
		||||
    Name: 'ViewByCategories',
 | 
			
		||||
function ViewByCategories() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ViewByCategories.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'all-app' });
 | 
			
		||||
@@ -147,10 +150,9 @@ 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,
 | 
			
		||||
                                               accessible_role: Atk.Role.LIST });
 | 
			
		||||
        this._categoryBox = new St.BoxLayout({ vertical: true, reactive: true });
 | 
			
		||||
        this._categoryScroll = new St.ScrollView({ x_fill: false,
 | 
			
		||||
                                                   y_fill: false,
 | 
			
		||||
                                                   style_class: 'vfade' });
 | 
			
		||||
@@ -203,30 +205,27 @@ 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,
 | 
			
		||||
                                     can_focus: true ,
 | 
			
		||||
                                     accessible_role: Atk.Role.LIST_ITEM });
 | 
			
		||||
                                     can_focus: true });
 | 
			
		||||
        button.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
            this._selectCategory(index);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        var apps;
 | 
			
		||||
        if (dir == null) {
 | 
			
		||||
            apps = allApps;
 | 
			
		||||
            this._allCategoryButton = button;
 | 
			
		||||
        } else {
 | 
			
		||||
            apps = [];
 | 
			
		||||
@@ -240,16 +239,20 @@ const ViewByCategories = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _removeAll: function() {
 | 
			
		||||
        this._view.removeAll();
 | 
			
		||||
        this._categories = [];
 | 
			
		||||
        this._categoryBox.destroy_all_children();
 | 
			
		||||
        this._categoryBox.destroy_children();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
@@ -277,14 +281,16 @@ const ViewByCategories = new Lang.Class({
 | 
			
		||||
                this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* This class represents a display containing a collection of application items.
 | 
			
		||||
 * The applications are sorted based on their name.
 | 
			
		||||
 */
 | 
			
		||||
const AllAppDisplay = new Lang.Class({
 | 
			
		||||
    Name: 'AllAppDisplay',
 | 
			
		||||
function AllAppDisplay() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AllAppDisplay.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
        this._appSystem.connect('installed-changed', Lang.bind(this, function() {
 | 
			
		||||
@@ -300,37 +306,35 @@ const AllAppDisplay = new Lang.Class({
 | 
			
		||||
    _redisplay: function() {
 | 
			
		||||
        this._appView.refresh();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AppSearchProvider = new Lang.Class({
 | 
			
		||||
    Name: 'AppSearchProvider',
 | 
			
		||||
    Extends: Search.SearchProvider,
 | 
			
		||||
function AppSearchProvider() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AppSearchProvider.prototype = {
 | 
			
		||||
    __proto__: Search.SearchProvider.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent(_("APPLICATIONS"));
 | 
			
		||||
        Search.SearchProvider.prototype._init.call(this, _("APPLICATIONS"));
 | 
			
		||||
        this._appSys = Shell.AppSystem.get_default();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getResultMetas: function(apps, callback) {
 | 
			
		||||
        let metas = [];
 | 
			
		||||
        for (let i = 0; i < apps.length; i++) {
 | 
			
		||||
            let app = apps[i];
 | 
			
		||||
            metas.push({ 'id': app,
 | 
			
		||||
                         'name': app.get_name(),
 | 
			
		||||
                         'createIcon': function(size) {
 | 
			
		||||
                             return app.create_icon_texture(size);
 | 
			
		||||
                         }
 | 
			
		||||
                       });
 | 
			
		||||
        }
 | 
			
		||||
        callback(metas);
 | 
			
		||||
    getResultMeta: function(app) {
 | 
			
		||||
        return { 'id': app,
 | 
			
		||||
                 'name': app.get_name(),
 | 
			
		||||
                 'createIcon': function(size) {
 | 
			
		||||
                                   return app.create_icon_texture(size);
 | 
			
		||||
                               }
 | 
			
		||||
               };
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getInitialResultSet: function(terms) {
 | 
			
		||||
        this.searchSystem.pushResults(this, this._appSys.initial_search(terms));
 | 
			
		||||
        return this._appSys.initial_search(terms);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getSubsearchResultSet: function(previousResults, terms) {
 | 
			
		||||
        this.searchSystem.pushResults(this, this._appSys.subsearch(previousResults, terms));
 | 
			
		||||
        return this._appSys.subsearch(previousResults, terms);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateResult: function(app, params) {
 | 
			
		||||
@@ -338,7 +342,7 @@ const AppSearchProvider = new Lang.Class({
 | 
			
		||||
                                        timestamp: 0 });
 | 
			
		||||
 | 
			
		||||
        let event = Clutter.get_current_event();
 | 
			
		||||
        let modifiers = event ? event.get_state() : 0;
 | 
			
		||||
        let modifiers = event ? Shell.get_event_state(event) : 0;
 | 
			
		||||
        let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
 | 
			
		||||
 | 
			
		||||
        if (openNewWindow)
 | 
			
		||||
@@ -360,39 +364,36 @@ const AppSearchProvider = new Lang.Class({
 | 
			
		||||
        let icon = new AppWellIcon(app);
 | 
			
		||||
        return icon.actor;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const SettingsSearchProvider = new Lang.Class({
 | 
			
		||||
    Name: 'SettingsSearchProvider',
 | 
			
		||||
    Extends: Search.SearchProvider,
 | 
			
		||||
function SettingsSearchProvider() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SettingsSearchProvider.prototype = {
 | 
			
		||||
    __proto__: Search.SearchProvider.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent(_("SETTINGS"));
 | 
			
		||||
 | 
			
		||||
        Search.SearchProvider.prototype._init.call(this, _("SETTINGS"));
 | 
			
		||||
        this._appSys = Shell.AppSystem.get_default();
 | 
			
		||||
        this._gnomecc = this._appSys.lookup_app('gnome-control-center.desktop');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getResultMetas: function(prefs, callback) {
 | 
			
		||||
        let metas = [];
 | 
			
		||||
        for (let i = 0; i < prefs.length; i++) {
 | 
			
		||||
            let pref = prefs[i];
 | 
			
		||||
            metas.push({ 'id': pref,
 | 
			
		||||
                         'name': pref.get_name(),
 | 
			
		||||
                         'createIcon': function(size) {
 | 
			
		||||
                             return pref.create_icon_texture(size);
 | 
			
		||||
                         }
 | 
			
		||||
                       });
 | 
			
		||||
        }
 | 
			
		||||
        callback(metas);
 | 
			
		||||
    getResultMeta: function(pref) {
 | 
			
		||||
        return { 'id': pref,
 | 
			
		||||
                 'name': pref.get_name(),
 | 
			
		||||
                 'createIcon': function(size) {
 | 
			
		||||
                                   return pref.create_icon_texture(size);
 | 
			
		||||
                               }
 | 
			
		||||
               };
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getInitialResultSet: function(terms) {
 | 
			
		||||
        this.searchSystem.pushResults(this, this._appSys.search_settings(terms));
 | 
			
		||||
        return this._appSys.search_settings(terms);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getSubsearchResultSet: function(previousResults, terms) {
 | 
			
		||||
        this.searchSystem.pushResults(this, this._appSys.search_settings(terms));
 | 
			
		||||
        return this._appSys.search_settings(terms);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateResult: function(pref, params) {
 | 
			
		||||
@@ -411,28 +412,35 @@ const SettingsSearchProvider = new Lang.Class({
 | 
			
		||||
        let icon = new AppWellIcon(app);
 | 
			
		||||
        return icon.actor;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AppIcon = new Lang.Class({
 | 
			
		||||
    Name: 'AppIcon',
 | 
			
		||||
    Extends: IconGrid.BaseIcon,
 | 
			
		||||
function AppIcon(app, params) {
 | 
			
		||||
    this._init(app, params);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AppIcon.prototype = {
 | 
			
		||||
    __proto__:  IconGrid.BaseIcon.prototype,
 | 
			
		||||
 | 
			
		||||
    _init : function(app, params) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
 | 
			
		||||
        let label = this.app.get_name();
 | 
			
		||||
 | 
			
		||||
        this.parent(label, params);
 | 
			
		||||
        IconGrid.BaseIcon.prototype._init.call(this,
 | 
			
		||||
                                               label,
 | 
			
		||||
                                               params);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createIcon: function(iconSize) {
 | 
			
		||||
        return this.app.create_icon_texture(iconSize);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AppWellIcon = new Lang.Class({
 | 
			
		||||
    Name: 'AppWellIcon',
 | 
			
		||||
function AppWellIcon(app, iconParams, onActivateOverride) {
 | 
			
		||||
    this._init(app, iconParams, onActivateOverride);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AppWellIcon.prototype = {
 | 
			
		||||
    _init : function(app, iconParams, onActivateOverride) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'app-well-app',
 | 
			
		||||
@@ -560,9 +568,8 @@ const AppWellIcon = new Lang.Class({
 | 
			
		||||
            this._menuManager.addMenu(this._menu);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('menu-state-changed', true);
 | 
			
		||||
 | 
			
		||||
        this.actor.set_hover(true);
 | 
			
		||||
        this.actor.show_tooltip();
 | 
			
		||||
        this._menu.popup();
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
@@ -578,12 +585,11 @@ const AppWellIcon = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onMenuPoppedDown: function() {
 | 
			
		||||
        this.actor.sync_hover();
 | 
			
		||||
        this.emit('menu-state-changed', false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActivate: function (event) {
 | 
			
		||||
        this.emit('launching');
 | 
			
		||||
        let modifiers = event.get_state();
 | 
			
		||||
        let modifiers = Shell.get_event_state(event);
 | 
			
		||||
 | 
			
		||||
        if (this._onActivateOverride) {
 | 
			
		||||
            this._onActivateOverride(event);
 | 
			
		||||
@@ -614,19 +620,22 @@ const AppWellIcon = new Lang.Class({
 | 
			
		||||
    getDragActorSource: function() {
 | 
			
		||||
        return this.icon.icon;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(AppWellIcon.prototype);
 | 
			
		||||
 | 
			
		||||
const AppIconMenu = new Lang.Class({
 | 
			
		||||
    Name: 'AppIconMenu',
 | 
			
		||||
    Extends: PopupMenu.PopupMenu,
 | 
			
		||||
function AppIconMenu(source) {
 | 
			
		||||
    this._init(source);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AppIconMenu.prototype = {
 | 
			
		||||
    __proto__: PopupMenu.PopupMenu.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(source) {
 | 
			
		||||
        let side = St.Side.LEFT;
 | 
			
		||||
        if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
 | 
			
		||||
        if (St.Widget.get_default_direction() == St.TextDirection.RTL)
 | 
			
		||||
            side = St.Side.RIGHT;
 | 
			
		||||
 | 
			
		||||
        this.parent(source.actor, 0.5, side);
 | 
			
		||||
        PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side);
 | 
			
		||||
 | 
			
		||||
        // We want to keep the item hovered while the menu is up
 | 
			
		||||
        this.blockSourceEvents = true;
 | 
			
		||||
@@ -714,5 +723,5 @@ const AppIconMenu = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
        this.close();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(AppIconMenu.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,11 @@ const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
 | 
			
		||||
const AppFavorites = new Lang.Class({
 | 
			
		||||
    Name: 'AppFavorites',
 | 
			
		||||
function AppFavorites() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AppFavorites.prototype = {
 | 
			
		||||
    FAVORITE_APPS_KEY: 'favorite-apps',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
@@ -84,12 +86,9 @@ const AppFavorites = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let app = Shell.AppSystem.get_default().lookup_app(appId);
 | 
			
		||||
 | 
			
		||||
        Main.overview.setMessage(_("%s has been added to your favorites.").format(app.get_name()),
 | 
			
		||||
                                 { forFeedback: true,
 | 
			
		||||
                                   undoCallback: Lang.bind(this, function () {
 | 
			
		||||
                                                               this._removeFavorite(appId);
 | 
			
		||||
                                                           })
 | 
			
		||||
                                 });
 | 
			
		||||
        Main.overview.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
 | 
			
		||||
            this._removeFavorite(appId);
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addFavorite: function(appId) {
 | 
			
		||||
@@ -119,13 +118,11 @@ const AppFavorites = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        Main.overview.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
 | 
			
		||||
                                 { forFeedback: true,
 | 
			
		||||
                                   undoCallback: Lang.bind(this, function () {
 | 
			
		||||
                                                               this._addFavorite(appId, pos);
 | 
			
		||||
                                                           })
 | 
			
		||||
                                 });
 | 
			
		||||
                                 Lang.bind(this, function () {
 | 
			
		||||
            this._addFavorite(appId, pos);
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(AppFavorites.prototype);
 | 
			
		||||
 | 
			
		||||
var appFavoritesInstance = null;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										282
									
								
								js/ui/automountManager.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,282 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
 | 
			
		||||
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 = {
 | 
			
		||||
    name: 'org.freedesktop.ConsoleKit.Session',
 | 
			
		||||
    methods: [{ name: 'IsActive',
 | 
			
		||||
                inSignature: '',
 | 
			
		||||
                outSignature: 'b' }],
 | 
			
		||||
    signals: [{ name: 'ActiveChanged',
 | 
			
		||||
                inSignature: 'b' }]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ConsoleKitSessionProxy = DBus.makeProxyClass(ConsoleKitSessionIface);
 | 
			
		||||
 | 
			
		||||
const ConsoleKitManagerIface = {
 | 
			
		||||
    name: 'org.freedesktop.ConsoleKit.Manager',
 | 
			
		||||
    methods: [{ name: 'GetCurrentSession',
 | 
			
		||||
                inSignature: '',
 | 
			
		||||
                outSignature: 'o' }]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function ConsoleKitManager() {
 | 
			
		||||
    this._init();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ConsoleKitManager.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.sessionActive = true;
 | 
			
		||||
 | 
			
		||||
        DBus.system.proxifyObject(this,
 | 
			
		||||
                                  'org.freedesktop.ConsoleKit',
 | 
			
		||||
                                  '/org/freedesktop/ConsoleKit/Manager');
 | 
			
		||||
 | 
			
		||||
        DBus.system.watch_name('org.freedesktop.ConsoleKit',
 | 
			
		||||
                               false, // do not launch a name-owner if none exists
 | 
			
		||||
                               Lang.bind(this, this._onManagerAppeared),
 | 
			
		||||
                               Lang.bind(this, this._onManagerVanished));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onManagerAppeared: function(owner) {
 | 
			
		||||
        this.GetCurrentSessionRemote(Lang.bind(this, this._onCurrentSession));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onManagerVanished: function(oldOwner) {
 | 
			
		||||
        this.sessionActive = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCurrentSession: function(session) {
 | 
			
		||||
        this._ckSession = new ConsoleKitSessionProxy(DBus.system, 'org.freedesktop.ConsoleKit', session);
 | 
			
		||||
 | 
			
		||||
        this._ckSession.connect
 | 
			
		||||
            ('ActiveChanged', Lang.bind(this, function(object, isActive) {
 | 
			
		||||
                this.sessionActive = isActive;            
 | 
			
		||||
            }));
 | 
			
		||||
        this._ckSession.IsActiveRemote(Lang.bind(this, function(isActive) {
 | 
			
		||||
            this.sessionActive = isActive;            
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
DBus.proxifyPrototype(ConsoleKitManager.prototype, ConsoleKitManagerIface);
 | 
			
		||||
 | 
			
		||||
function AutomountManager() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AutomountManager.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
 | 
			
		||||
        this._volumeQueue = [];
 | 
			
		||||
 | 
			
		||||
        this.ckListener = new ConsoleKitManager();
 | 
			
		||||
 | 
			
		||||
        this._ssProxy = new ScreenSaver.ScreenSaverProxy();
 | 
			
		||||
        this._ssProxy.connect('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, 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;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDriveConnected: function() {
 | 
			
		||||
        // if we're not in the current ConsoleKit session,
 | 
			
		||||
        // or screensaver is active, don't play sounds
 | 
			
		||||
        if (!this.ckListener.sessionActive)
 | 
			
		||||
            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.ckListener.sessionActive)
 | 
			
		||||
            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.ckListener.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 });
 | 
			
		||||
 | 
			
		||||
        if (params.checkSession) {
 | 
			
		||||
            // if we're not in the current ConsoleKit session,
 | 
			
		||||
            // don't attempt automount
 | 
			
		||||
            if (!this.ckListener.sessionActive)
 | 
			
		||||
                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;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
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 +24,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,17 +42,6 @@ function isMountRootHidden(root) {
 | 
			
		||||
    return (path.indexOf('/.') != -1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function isMountNonLocal(mount) {
 | 
			
		||||
    // If the mount doesn't have an associated volume, that means it's
 | 
			
		||||
    // an uninteresting filesystem. Most devices that we care about will
 | 
			
		||||
    // have a mount, like media players and USB sticks.
 | 
			
		||||
    let volume = mount.get_volume();
 | 
			
		||||
    if (volume == null)
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
    return (volume.get_identifier("class") == "network");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function startAppForMount(app, mount) {
 | 
			
		||||
    let files = [];
 | 
			
		||||
    let root = mount.get_root();
 | 
			
		||||
@@ -75,40 +62,40 @@ function startAppForMount(app, mount) {
 | 
			
		||||
 | 
			
		||||
/******************************************/
 | 
			
		||||
 | 
			
		||||
const HotplugSnifferIface = <interface name="org.gnome.Shell.HotplugSniffer">
 | 
			
		||||
<method name="SniffURI">
 | 
			
		||||
    <arg type="s" direction="in" />
 | 
			
		||||
    <arg type="as" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
const HotplugSnifferIface = {
 | 
			
		||||
    name: 'org.gnome.Shell.HotplugSniffer',
 | 
			
		||||
    methods: [{ name: 'SniffURI',
 | 
			
		||||
                inSignature: 's',
 | 
			
		||||
                outSignature: 'as' }]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const HotplugSnifferProxy = Gio.DBusProxy.makeProxyWrapper(HotplugSnifferIface);
 | 
			
		||||
function HotplugSniffer() {
 | 
			
		||||
    return new HotplugSnifferProxy(Gio.DBus.session,
 | 
			
		||||
const HotplugSniffer = function() {
 | 
			
		||||
    this._init();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
HotplugSniffer.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        DBus.session.proxifyObject(this,
 | 
			
		||||
                                   'org.gnome.Shell.HotplugSniffer',
 | 
			
		||||
                                   '/org/gnome/Shell/HotplugSniffer');
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
DBus.proxifyPrototype(HotplugSniffer.prototype, HotplugSnifferIface);
 | 
			
		||||
 | 
			
		||||
function ContentTypeDiscoverer(callback) {
 | 
			
		||||
    this._init(callback);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ContentTypeDiscoverer = new Lang.Class({
 | 
			
		||||
    Name: 'ContentTypeDiscoverer',
 | 
			
		||||
 | 
			
		||||
ContentTypeDiscoverer.prototype = {
 | 
			
		||||
    _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) {
 | 
			
		||||
@@ -127,8 +114,9 @@ const ContentTypeDiscoverer = new Lang.Class({
 | 
			
		||||
            let root = mount.get_root();
 | 
			
		||||
 | 
			
		||||
            let hotplugSniffer = new HotplugSniffer();
 | 
			
		||||
            hotplugSniffer.SniffURIRemote(root.get_uri(),
 | 
			
		||||
                 Lang.bind(this, function([contentTypes]) {
 | 
			
		||||
            hotplugSniffer.SniffURIRemote
 | 
			
		||||
                (root.get_uri(), DBus.CALL_FLAG_START,
 | 
			
		||||
                 Lang.bind(this, function(contentTypes) {
 | 
			
		||||
                     this._emitCallback(mount, contentTypes);
 | 
			
		||||
                 }));
 | 
			
		||||
        }
 | 
			
		||||
@@ -156,75 +144,63 @@ const ContentTypeDiscoverer = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._callback(mount, apps, contentTypes);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AutorunManager = new Lang.Class({
 | 
			
		||||
    Name: 'AutorunManager',
 | 
			
		||||
function AutorunManager() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AutorunManager.prototype = {
 | 
			
		||||
    _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.ckListener.sessionActive)
 | 
			
		||||
            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) {
 | 
			
		||||
@@ -260,9 +236,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());
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -270,9 +248,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());
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -280,33 +260,33 @@ 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());
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AutorunResidentSource = new Lang.Class({
 | 
			
		||||
    Name: 'AutorunResidentSource',
 | 
			
		||||
    Extends: MessageTray.Source,
 | 
			
		||||
function AutorunResidentSource() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    _init: function(manager) {
 | 
			
		||||
        this.parent(_("Removable Devices"), 'media-removable');
 | 
			
		||||
        this.showInLockScreen = false;
 | 
			
		||||
AutorunResidentSource.prototype = {
 | 
			
		||||
    __proto__: MessageTray.Source.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        MessageTray.Source.prototype._init.call(this, _("Removable Devices"));
 | 
			
		||||
 | 
			
		||||
        this._mounts = [];
 | 
			
		||||
 | 
			
		||||
        this._manager = manager;
 | 
			
		||||
        this._notification = new AutorunResidentNotification(this._manager, this);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    buildRightClickMenu: function() {
 | 
			
		||||
        return null;
 | 
			
		||||
        this._notification = new AutorunResidentNotification(this);
 | 
			
		||||
        this._setSummaryIcon(this.createNotificationIcon());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addMount: function(mount, apps) {
 | 
			
		||||
        if (!shouldAutorunMount(mount, false))
 | 
			
		||||
        if (ignoreAutorunForMount(mount))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let filtered = this._mounts.filter(function (element) {
 | 
			
		||||
@@ -345,22 +325,32 @@ const AutorunResidentSource = new Lang.Class({
 | 
			
		||||
            Main.messageTray.add(this);
 | 
			
		||||
            this.pushNotification(this._notification);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createNotificationIcon: function() {
 | 
			
		||||
        return new St.Icon ({ icon_name: 'media-removable',
 | 
			
		||||
                              icon_type: St.IconType.FULLCOLOR,
 | 
			
		||||
                              icon_size: this.ICON_SIZE });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AutorunResidentNotification = new Lang.Class({
 | 
			
		||||
    Name: 'AutorunResidentNotification',
 | 
			
		||||
    Extends: MessageTray.Notification,
 | 
			
		||||
function AutorunResidentNotification(source) {
 | 
			
		||||
    this._init(source);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    _init: function(manager, source) {
 | 
			
		||||
        this.parent(source, source.title, null, { customContent: true });
 | 
			
		||||
AutorunResidentNotification.prototype = {
 | 
			
		||||
    __proto__: MessageTray.Notification.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(source) {
 | 
			
		||||
        MessageTray.Notification.prototype._init.call(this, source,
 | 
			
		||||
                                                      source.title, null,
 | 
			
		||||
                                                      { customContent: true });
 | 
			
		||||
 | 
			
		||||
        // set the notification as resident
 | 
			
		||||
        this.setResident(true);
 | 
			
		||||
 | 
			
		||||
        this._layout = new St.BoxLayout ({ style_class: 'hotplug-resident-box',
 | 
			
		||||
                                           vertical: true });
 | 
			
		||||
        this._manager = manager;
 | 
			
		||||
 | 
			
		||||
        this.addActor(this._layout,
 | 
			
		||||
                      { x_expand: true,
 | 
			
		||||
@@ -369,7 +359,7 @@ const AutorunResidentNotification = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    updateForMounts: function(mounts) {
 | 
			
		||||
        // remove all the layout content
 | 
			
		||||
        this._layout.destroy_all_children();
 | 
			
		||||
        this._layout.destroy_children();
 | 
			
		||||
 | 
			
		||||
        for (let idx = 0; idx < mounts.length; idx++) {
 | 
			
		||||
            let element = mounts[idx];
 | 
			
		||||
@@ -408,7 +398,7 @@ const AutorunResidentNotification = new Lang.Class({
 | 
			
		||||
                                expand: true });
 | 
			
		||||
 | 
			
		||||
        let ejectIcon = 
 | 
			
		||||
            new St.Icon({ icon_name: 'media-eject-symbolic',
 | 
			
		||||
            new St.Icon({ icon_name: 'media-eject',
 | 
			
		||||
                          style_class: 'hotplug-resident-eject-icon' });
 | 
			
		||||
 | 
			
		||||
        let ejectButton =
 | 
			
		||||
@@ -423,18 +413,19 @@ const AutorunResidentNotification = new Lang.Class({
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        ejectButton.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
            this._manager.ejectMount(mount);
 | 
			
		||||
            Main.autorunManager.ejectMount(mount);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        return item;
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AutorunTransientDispatcher = new Lang.Class({
 | 
			
		||||
    Name: 'AutorunTransientDispatcher',
 | 
			
		||||
function AutorunTransientDispatcher() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    _init: function(manager) {
 | 
			
		||||
        this._manager = manager;
 | 
			
		||||
AutorunTransientDispatcher.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._sources = [];
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
 | 
			
		||||
    },
 | 
			
		||||
@@ -477,7 +468,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) {
 | 
			
		||||
@@ -486,7 +477,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]);
 | 
			
		||||
@@ -524,39 +515,47 @@ const AutorunTransientDispatcher = new Lang.Class({
 | 
			
		||||
        // destroy the notification source
 | 
			
		||||
        source.destroy();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AutorunTransientSource = new Lang.Class({
 | 
			
		||||
    Name: 'AutorunTransientSource',
 | 
			
		||||
    Extends: MessageTray.Source,
 | 
			
		||||
function AutorunTransientSource(mount, apps) {
 | 
			
		||||
    this._init(mount, apps);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AutorunTransientSource.prototype = {
 | 
			
		||||
    __proto__: MessageTray.Source.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(mount, apps) {
 | 
			
		||||
        MessageTray.Source.prototype._init.call(this, mount.get_name());
 | 
			
		||||
 | 
			
		||||
    _init: function(manager, mount, apps) {
 | 
			
		||||
        this._manager = manager;
 | 
			
		||||
        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);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getIcon: function() {
 | 
			
		||||
        return this.mount.get_icon();
 | 
			
		||||
    createNotificationIcon: function() {
 | 
			
		||||
        return new St.Icon({ gicon: this.mount.get_icon(),
 | 
			
		||||
                             icon_size: this.ICON_SIZE });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AutorunTransientNotification = new Lang.Class({
 | 
			
		||||
    Name: 'AutorunTransientNotification',
 | 
			
		||||
    Extends: MessageTray.Notification,
 | 
			
		||||
function AutorunTransientNotification(source) {
 | 
			
		||||
    this._init(source);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    _init: function(manager, source) {
 | 
			
		||||
        this.parent(source, source.title, null, { customContent: true });
 | 
			
		||||
AutorunTransientNotification.prototype = {
 | 
			
		||||
    __proto__: MessageTray.Notification.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(source) {
 | 
			
		||||
        MessageTray.Notification.prototype._init.call(this, 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);
 | 
			
		||||
@@ -588,7 +587,7 @@ const AutorunTransientNotification = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let label = new St.Bin({ y_align: St.Align.MIDDLE,
 | 
			
		||||
                                 child: new St.Label
 | 
			
		||||
                                 ({ text: _("Open with %s").format(app.get_name()) })
 | 
			
		||||
                                 ({ text: _("Open with %s").format(app.get_display_name()) })
 | 
			
		||||
                               });
 | 
			
		||||
        box.add(label);
 | 
			
		||||
 | 
			
		||||
@@ -608,7 +607,7 @@ const AutorunTransientNotification = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _buttonForEject: function() {
 | 
			
		||||
        let box = new St.BoxLayout();
 | 
			
		||||
        let icon = new St.Icon({ icon_name: 'media-eject-symbolic',
 | 
			
		||||
        let icon = new St.Icon({ icon_name: 'media-eject',
 | 
			
		||||
                                 style_class: 'hotplug-notification-item-icon' });
 | 
			
		||||
        box.add(icon);
 | 
			
		||||
 | 
			
		||||
@@ -625,11 +624,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,18 +18,16 @@ 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({
 | 
			
		||||
    Name: 'BoxPointer',
 | 
			
		||||
function BoxPointer(side, binProperties) {
 | 
			
		||||
    this._init(side, binProperties);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BoxPointer.prototype = {
 | 
			
		||||
    _init: function(arrowSide, binProperties) {
 | 
			
		||||
        this._arrowSide = arrowSide;
 | 
			
		||||
        this._userArrowSide = arrowSide;
 | 
			
		||||
        this._arrowOrigin = 0;
 | 
			
		||||
        this.actor = new St.Bin({ x_fill: true,
 | 
			
		||||
                                  y_fill: true });
 | 
			
		||||
@@ -56,36 +47,16 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
        this._xPosition = 0;
 | 
			
		||||
        this._yPosition = 0;
 | 
			
		||||
        this._sourceAlignment = 0.5;
 | 
			
		||||
        this._capturedEventId = 0;
 | 
			
		||||
        this._muteInput();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _muteInput: function() {
 | 
			
		||||
        if (this._capturedEventId == 0)
 | 
			
		||||
            this._capturedEventId = this.actor.connect('captured-event',
 | 
			
		||||
                                                       function() { return true; });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _unmuteInput: function() {
 | 
			
		||||
        if (this._capturedEventId != 0) {
 | 
			
		||||
            this.actor.disconnect(this._capturedEventId);
 | 
			
		||||
            this._capturedEventId = 0;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
@@ -106,12 +77,8 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
                                 xOffset: 0,
 | 
			
		||||
                                 yOffset: 0,
 | 
			
		||||
                                 transition: 'linear',
 | 
			
		||||
                                 onComplete: Lang.bind(this, function() {
 | 
			
		||||
                                     this._unmuteInput();
 | 
			
		||||
                                     if (onComplete)
 | 
			
		||||
                                         onComplete();
 | 
			
		||||
                                 }),
 | 
			
		||||
                                 time: animationTime });
 | 
			
		||||
                                 onComplete: onComplete,
 | 
			
		||||
                                 time: POPUP_ANIMATION_TIME });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hide: function(animate, onComplete) {
 | 
			
		||||
@@ -119,10 +86,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;
 | 
			
		||||
@@ -139,17 +104,13 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._muteInput();
 | 
			
		||||
 | 
			
		||||
        Tweener.removeTweens(this);
 | 
			
		||||
        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)
 | 
			
		||||
@@ -219,27 +180,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) {
 | 
			
		||||
@@ -253,6 +195,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();
 | 
			
		||||
@@ -263,6 +206,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
 | 
			
		||||
@@ -275,51 +219,14 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
        let [x1, y1] = [halfBorder, halfBorder];
 | 
			
		||||
        let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
 | 
			
		||||
 | 
			
		||||
        let skipTopLeft = false;
 | 
			
		||||
        let skipTopRight = false;
 | 
			
		||||
        let skipBottomLeft = false;
 | 
			
		||||
        let skipBottomRight = false;
 | 
			
		||||
 | 
			
		||||
        switch (this._arrowSide) {
 | 
			
		||||
        case St.Side.TOP:
 | 
			
		||||
            if (this._arrowOrigin == x1)
 | 
			
		||||
                skipTopLeft = true;
 | 
			
		||||
            else if (this._arrowOrigin == x2)
 | 
			
		||||
                skipTopRight = true;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case St.Side.RIGHT:
 | 
			
		||||
            if (this._arrowOrigin == y1)
 | 
			
		||||
                skipTopRight = true;
 | 
			
		||||
            else if (this._arrowOrigin == y2)
 | 
			
		||||
                skipBottomRight = true;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case St.Side.BOTTOM:
 | 
			
		||||
            if (this._arrowOrigin == x1)
 | 
			
		||||
                skipBottomLeft = true;
 | 
			
		||||
            else if (this._arrowOrigin == x2)
 | 
			
		||||
                skipBottomRight = true;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case St.Side.LEFT:
 | 
			
		||||
            if (this._arrowOrigin == y1)
 | 
			
		||||
                skipTopLeft = true;
 | 
			
		||||
            else if (this._arrowOrigin == y2)
 | 
			
		||||
                skipBottomLeft = true;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        cr.moveTo(x1 + borderRadius, y1);
 | 
			
		||||
        if (this._arrowSide == St.Side.TOP) {
 | 
			
		||||
            if (skipTopLeft) {
 | 
			
		||||
                cr.moveTo(x1, y2 - borderRadius);
 | 
			
		||||
                cr.lineTo(x1, y1 - rise);
 | 
			
		||||
                cr.lineTo(x1 + halfBase, y1);
 | 
			
		||||
            } else if (skipTopRight) {
 | 
			
		||||
                cr.lineTo(x2 - halfBase, y1);
 | 
			
		||||
                cr.lineTo(x2, y1 - rise);
 | 
			
		||||
                cr.lineTo(x2, y1 + borderRadius);
 | 
			
		||||
            if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
 | 
			
		||||
                cr.lineTo(this._arrowOrigin, y1 - rise);
 | 
			
		||||
                cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y1);
 | 
			
		||||
            } else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
 | 
			
		||||
                cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y1);
 | 
			
		||||
                cr.lineTo(this._arrowOrigin, y1 - rise);
 | 
			
		||||
            } else {
 | 
			
		||||
                cr.lineTo(this._arrowOrigin - halfBase, y1);
 | 
			
		||||
                cr.lineTo(this._arrowOrigin, y1 - rise);
 | 
			
		||||
@@ -327,20 +234,19 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!skipTopRight) {
 | 
			
		||||
            cr.lineTo(x2 - borderRadius, y1);
 | 
			
		||||
            cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
 | 
			
		||||
                   3*Math.PI/2, Math.PI*2);
 | 
			
		||||
        }
 | 
			
		||||
        cr.lineTo(x2 - borderRadius, y1);
 | 
			
		||||
 | 
			
		||||
        // top-right corner
 | 
			
		||||
        cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
 | 
			
		||||
               3*Math.PI/2, Math.PI*2);
 | 
			
		||||
 | 
			
		||||
        if (this._arrowSide == St.Side.RIGHT) {
 | 
			
		||||
            if (skipTopRight) {
 | 
			
		||||
                cr.lineTo(x2 + rise, y1);
 | 
			
		||||
                cr.lineTo(x2 + rise, y1 + halfBase);
 | 
			
		||||
            } else if (skipBottomRight) {
 | 
			
		||||
                cr.lineTo(x2, y2 - halfBase);
 | 
			
		||||
                cr.lineTo(x2 + rise, y2);
 | 
			
		||||
                cr.lineTo(x2 - borderRadius, y2);
 | 
			
		||||
            if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
 | 
			
		||||
                cr.lineTo(x2 + rise, this._arrowOrigin);
 | 
			
		||||
                cr.lineTo(x2, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
 | 
			
		||||
            } else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
 | 
			
		||||
                cr.lineTo(x2, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
 | 
			
		||||
                cr.lineTo(x2 + rise, this._arrowOrigin);
 | 
			
		||||
            } else {
 | 
			
		||||
                cr.lineTo(x2, this._arrowOrigin - halfBase);
 | 
			
		||||
                cr.lineTo(x2 + rise, this._arrowOrigin);
 | 
			
		||||
@@ -348,20 +254,19 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!skipBottomRight) {
 | 
			
		||||
            cr.lineTo(x2, y2 - borderRadius);
 | 
			
		||||
            cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
 | 
			
		||||
                   0, Math.PI/2);
 | 
			
		||||
        }
 | 
			
		||||
        cr.lineTo(x2, y2 - borderRadius);
 | 
			
		||||
 | 
			
		||||
        // bottom-right corner
 | 
			
		||||
        cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
 | 
			
		||||
               0, Math.PI/2);
 | 
			
		||||
 | 
			
		||||
        if (this._arrowSide == St.Side.BOTTOM) {
 | 
			
		||||
            if (skipBottomLeft) {
 | 
			
		||||
                cr.lineTo(x1 + halfBase, y2);
 | 
			
		||||
                cr.lineTo(x1, y2 + rise);
 | 
			
		||||
                cr.lineTo(x1, y2 - borderRadius);
 | 
			
		||||
            } else if (skipBottomRight) {
 | 
			
		||||
                cr.lineTo(x2, y2 + rise);
 | 
			
		||||
                cr.lineTo(x2 - halfBase, y2);
 | 
			
		||||
            if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
 | 
			
		||||
                cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y2);
 | 
			
		||||
                cr.lineTo(this._arrowOrigin, y2 + rise);
 | 
			
		||||
            } else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
 | 
			
		||||
                cr.lineTo(this._arrowOrigin, y2 + rise);
 | 
			
		||||
                cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y2);
 | 
			
		||||
            } else {
 | 
			
		||||
                cr.lineTo(this._arrowOrigin + halfBase, y2);
 | 
			
		||||
                cr.lineTo(this._arrowOrigin, y2 + rise);
 | 
			
		||||
@@ -369,20 +274,19 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!skipBottomLeft) {
 | 
			
		||||
            cr.lineTo(x1 + borderRadius, y2);
 | 
			
		||||
            cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
 | 
			
		||||
                   Math.PI/2, Math.PI);
 | 
			
		||||
        }
 | 
			
		||||
        cr.lineTo(x1 + borderRadius, y2);
 | 
			
		||||
 | 
			
		||||
        // bottom-left corner
 | 
			
		||||
        cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
 | 
			
		||||
               Math.PI/2, Math.PI);
 | 
			
		||||
 | 
			
		||||
        if (this._arrowSide == St.Side.LEFT) {
 | 
			
		||||
            if (skipTopLeft) {
 | 
			
		||||
                cr.lineTo(x1, y1 + halfBase);
 | 
			
		||||
                cr.lineTo(x1 - rise, y1);
 | 
			
		||||
                cr.lineTo(x1 + borderRadius, y1);
 | 
			
		||||
            } else if (skipBottomLeft) {
 | 
			
		||||
                cr.lineTo(x1 - rise, y2)
 | 
			
		||||
                cr.lineTo(x1 - rise, y2 - halfBase);
 | 
			
		||||
            if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
 | 
			
		||||
                cr.lineTo(x1, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
 | 
			
		||||
                cr.lineTo(x1 - rise, this._arrowOrigin);
 | 
			
		||||
            } else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
 | 
			
		||||
                cr.lineTo(x1 - rise, this._arrowOrigin);
 | 
			
		||||
                cr.lineTo(x1, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
 | 
			
		||||
            } else {
 | 
			
		||||
                cr.lineTo(x1, this._arrowOrigin + halfBase);
 | 
			
		||||
                cr.lineTo(x1 - rise, this._arrowOrigin);
 | 
			
		||||
@@ -390,26 +294,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();
 | 
			
		||||
@@ -426,7 +324,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) {
 | 
			
		||||
@@ -447,9 +349,10 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
        let arrowBase = themeNode.get_length('-arrow-base');
 | 
			
		||||
        let borderRadius = themeNode.get_length('-arrow-border-radius');
 | 
			
		||||
        let margin = (4 * borderRadius + borderWidth + arrowBase);
 | 
			
		||||
        let halfMargin = margin / 2;
 | 
			
		||||
 | 
			
		||||
        let themeNode = this.actor.get_theme_node();
 | 
			
		||||
        let gap = themeNode.get_length('-boxpointer-gap');
 | 
			
		||||
        let padding = themeNode.get_length('-arrow-rise');
 | 
			
		||||
 | 
			
		||||
        let resX, resY;
 | 
			
		||||
 | 
			
		||||
@@ -468,66 +371,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) {
 | 
			
		||||
@@ -561,39 +427,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();
 | 
			
		||||
@@ -619,4 +452,4 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
    get opacity() {
 | 
			
		||||
        return this.actor.opacity;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
@@ -155,24 +156,28 @@ function _getEventDayAbbreviation(dayNumber) {
 | 
			
		||||
 | 
			
		||||
// Abstraction for an appointment/event in a calendar
 | 
			
		||||
 | 
			
		||||
const CalendarEvent = new Lang.Class({
 | 
			
		||||
    Name: 'CalendarEvent',
 | 
			
		||||
function CalendarEvent(date, end, summary, allDay) {
 | 
			
		||||
    this._init(date, end, summary, allDay);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CalendarEvent.prototype = {
 | 
			
		||||
    _init: function(date, end, summary, allDay) {
 | 
			
		||||
        this.date = date;
 | 
			
		||||
        this.end = end;
 | 
			
		||||
        this.summary = summary;
 | 
			
		||||
        this.allDay = allDay;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Interface for appointments/events - e.g. the contents of a calendar
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// First, an implementation with no events
 | 
			
		||||
const EmptyEventSource = new Lang.Class({
 | 
			
		||||
    Name: 'EmptyEventSource',
 | 
			
		||||
function EmptyEventSource() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EmptyEventSource.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -187,31 +192,33 @@ const EmptyEventSource = new Lang.Class({
 | 
			
		||||
    hasEvents: function(day) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(EmptyEventSource.prototype);
 | 
			
		||||
 | 
			
		||||
const CalendarServerIface = <interface name="org.gnome.Shell.CalendarServer">
 | 
			
		||||
<method name="GetEvents">
 | 
			
		||||
    <arg type="x" direction="in" />
 | 
			
		||||
    <arg type="x" direction="in" />
 | 
			
		||||
    <arg type="b" direction="in" />
 | 
			
		||||
    <arg type="a(sssbxxa{sv})" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<signal name="Changed" />
 | 
			
		||||
</interface>;
 | 
			
		||||
const CalendarServerIface = {
 | 
			
		||||
    name: 'org.gnome.Shell.CalendarServer',
 | 
			
		||||
    methods: [{ name: 'GetEvents',
 | 
			
		||||
                inSignature: 'xxb',
 | 
			
		||||
                outSignature: 'a(sssbxxa{sv})' }],
 | 
			
		||||
    signals: [{ name: 'Changed',
 | 
			
		||||
                inSignature: '' }]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const CalendarServerInfo  = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface);
 | 
			
		||||
const CalendarServer = function () {
 | 
			
		||||
    this._init();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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 });
 | 
			
		||||
CalendarServer.prototype = {
 | 
			
		||||
     _init: function() {
 | 
			
		||||
         DBus.session.proxifyObject(this, 'org.gnome.Shell.CalendarServer', '/org/gnome/Shell/CalendarServer');
 | 
			
		||||
     }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
    self.init(null);
 | 
			
		||||
    return self;
 | 
			
		||||
DBus.proxifyPrototype(CalendarServer.prototype, CalendarServerIface);
 | 
			
		||||
 | 
			
		||||
// an implementation that reads data from a session bus service
 | 
			
		||||
function DBusEventSource(owner) {
 | 
			
		||||
    this._init(owner);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _datesEqual(a, b) {
 | 
			
		||||
@@ -232,22 +239,18 @@ function _dateIntervalsOverlap(a0, a1, b0, b1)
 | 
			
		||||
        return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// an implementation that reads data from a session bus service
 | 
			
		||||
const DBusEventSource = new Lang.Class({
 | 
			
		||||
    Name: 'DBusEventSource',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
DBusEventSource.prototype = {
 | 
			
		||||
    _init: function(owner) {
 | 
			
		||||
        this._resetCache();
 | 
			
		||||
 | 
			
		||||
        this._dbusProxy = new CalendarServer();
 | 
			
		||||
        this._dbusProxy.connectSignal('Changed', Lang.bind(this, this._onChanged));
 | 
			
		||||
        this._dbusProxy = new CalendarServer(owner);
 | 
			
		||||
        this._dbusProxy.connect('Changed', Lang.bind(this, this._onChanged));
 | 
			
		||||
 | 
			
		||||
        this._dbusProxy.connect('notify::g-name-owner', Lang.bind(this, function() {
 | 
			
		||||
            if (this._dbusProxy.g_name_owner)
 | 
			
		||||
                this._onNameAppeared();
 | 
			
		||||
            else
 | 
			
		||||
                this._onNameVanished();
 | 
			
		||||
        }));
 | 
			
		||||
        DBus.session.watch_name('org.gnome.Shell.CalendarServer',
 | 
			
		||||
                                false, // do not launch a name-owner if none exists
 | 
			
		||||
                                Lang.bind(this, this._onNameAppeared),
 | 
			
		||||
                                Lang.bind(this, this._onNameVanished));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _resetCache: function() {
 | 
			
		||||
@@ -270,9 +273,8 @@ const DBusEventSource = new Lang.Class({
 | 
			
		||||
        this._loadEvents(false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onEventsReceived: function(results, error) {
 | 
			
		||||
    _onEventsReceived: function(appointments) {
 | 
			
		||||
        let newEvents = [];
 | 
			
		||||
        let appointments = results ? results[0] : null;
 | 
			
		||||
        if (appointments != null) {
 | 
			
		||||
            for (let n = 0; n < appointments.length; n++) {
 | 
			
		||||
                let a = appointments[n];
 | 
			
		||||
@@ -294,9 +296,9 @@ const DBusEventSource = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _loadEvents: function(forceReload) {
 | 
			
		||||
        if (this._curRequestBegin && this._curRequestEnd){
 | 
			
		||||
            let callFlags = Gio.DBusCallFlags.NO_AUTO_START;
 | 
			
		||||
            let callFlags = 0;
 | 
			
		||||
            if (forceReload)
 | 
			
		||||
                callFlags = Gio.DBusCallFlags.NONE;
 | 
			
		||||
                callFlags |= DBus.CALL_FLAG_START;
 | 
			
		||||
            this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000,
 | 
			
		||||
                                            this._curRequestEnd.getTime() / 1000,
 | 
			
		||||
                                            forceReload,
 | 
			
		||||
@@ -337,13 +339,27 @@ const DBusEventSource = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(DBusEventSource.prototype);
 | 
			
		||||
 | 
			
		||||
const Calendar = new Lang.Class({
 | 
			
		||||
    Name: 'Calendar',
 | 
			
		||||
// Calendar:
 | 
			
		||||
// @eventSource: is an object implementing the EventSource API, e.g. the
 | 
			
		||||
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
 | 
			
		||||
function Calendar(eventSource) {
 | 
			
		||||
    this._init(eventSource);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Calendar.prototype = {
 | 
			
		||||
    _init: function(eventSource) {
 | 
			
		||||
        if (eventSource) {
 | 
			
		||||
            this._eventSource = eventSource;
 | 
			
		||||
 | 
			
		||||
            this._eventSource.connect('changed', Lang.bind(this,
 | 
			
		||||
                                                           function() {
 | 
			
		||||
                                                               this._update(false);
 | 
			
		||||
                                                           }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._weekStart = Shell.util_get_week_start();
 | 
			
		||||
        this._weekdate = NaN;
 | 
			
		||||
        this._digitWidth = NaN;
 | 
			
		||||
@@ -380,24 +396,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)) {
 | 
			
		||||
@@ -412,7 +410,7 @@ const Calendar = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _buildHeader: function() {
 | 
			
		||||
        let offsetCols = this._useWeekdate ? 1 : 0;
 | 
			
		||||
        this.actor.destroy_all_children();
 | 
			
		||||
        this.actor.destroy_children();
 | 
			
		||||
 | 
			
		||||
        // Top line of the calendar '<| September 2009 |>'
 | 
			
		||||
        this._topBox = new St.BoxLayout();
 | 
			
		||||
@@ -454,7 +452,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) {
 | 
			
		||||
@@ -557,7 +555,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;
 | 
			
		||||
@@ -578,10 +575,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))
 | 
			
		||||
@@ -621,32 +615,25 @@ const Calendar = new Lang.Class({
 | 
			
		||||
        if (this._eventSource)
 | 
			
		||||
            this._eventSource.requestRange(beginDate, iter, forceReload);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(Calendar.prototype);
 | 
			
		||||
 | 
			
		||||
const EventsList = new Lang.Class({
 | 
			
		||||
    Name: 'EventsList',
 | 
			
		||||
function EventsList(eventSource) {
 | 
			
		||||
    this._init(eventSource);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
EventsList.prototype = {
 | 
			
		||||
    _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) {
 | 
			
		||||
@@ -704,7 +691,7 @@ const EventsList = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showOtherDay: function(day) {
 | 
			
		||||
        this.actor.destroy_all_children();
 | 
			
		||||
        this.actor.destroy_children();
 | 
			
		||||
 | 
			
		||||
        let dayBegin = _getBeginningOfDay(day);
 | 
			
		||||
        let dayEnd = _getEndOfDay(day);
 | 
			
		||||
@@ -721,7 +708,7 @@ const EventsList = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showToday: function() {
 | 
			
		||||
        this.actor.destroy_all_children();
 | 
			
		||||
        this.actor.destroy_children();
 | 
			
		||||
 | 
			
		||||
        let now = new Date();
 | 
			
		||||
        let dayBegin = _getBeginningOfDay(now);
 | 
			
		||||
@@ -732,15 +719,13 @@ const EventsList = new Lang.Class({
 | 
			
		||||
        let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
 | 
			
		||||
        this._addPeriod(_("Tomorrow"), tomorrowBegin, tomorrowEnd, false, true);
 | 
			
		||||
 | 
			
		||||
        let dayInWeek = (dayEnd.getDay() - this._weekStart + 7) % 7;
 | 
			
		||||
 | 
			
		||||
        if (dayInWeek < 5) {
 | 
			
		||||
        if (dayEnd.getDay() <= 4 + this._weekStart) {
 | 
			
		||||
            /* If now is within the first 5 days we show "This week" and
 | 
			
		||||
             * include events up until and including Saturday/Sunday
 | 
			
		||||
             * (depending on whether a week starts on Sunday/Monday).
 | 
			
		||||
             */
 | 
			
		||||
            let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
 | 
			
		||||
            let thisWeekEnd = new Date(dayEnd.getTime() + (6 - dayInWeek) * 86400 * 1000);
 | 
			
		||||
            let thisWeekEnd = new Date(dayEnd.getTime() + (6 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
 | 
			
		||||
            this._addPeriod(_("This week"), thisWeekBegin, thisWeekEnd, true, false);
 | 
			
		||||
        } else {
 | 
			
		||||
            /* otherwise it's one of the two last days of the week ... show
 | 
			
		||||
@@ -748,7 +733,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);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -769,4 +754,4 @@ const EventsList = new Lang.Class({
 | 
			
		||||
            this._showOtherDay(this._date);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,115 +0,0 @@
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Pango = imports.gi.Pango;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
 | 
			
		||||
const CheckBoxContainer = new Lang.Class({
 | 
			
		||||
    Name: 'CheckBoxContainer',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new Shell.GenericContainer();
 | 
			
		||||
        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));
 | 
			
		||||
        this.actor.connect('style-changed', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                let node = this.actor.get_theme_node();
 | 
			
		||||
                this._spacing = node.get_length('spacing');
 | 
			
		||||
            }));
 | 
			
		||||
        this.actor.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH;
 | 
			
		||||
 | 
			
		||||
        this._box = new St.Bin();
 | 
			
		||||
        this.actor.add_actor(this._box);
 | 
			
		||||
 | 
			
		||||
        this.label = new St.Label();
 | 
			
		||||
        this.label.clutter_text.set_line_wrap(true);
 | 
			
		||||
        this.label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
 | 
			
		||||
        this.actor.add_actor(this.label);
 | 
			
		||||
 | 
			
		||||
        this._spacing = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredWidth: function(actor, forHeight, alloc) {
 | 
			
		||||
        let [minWidth, natWidth] = this._box.get_preferred_width(forHeight);
 | 
			
		||||
 | 
			
		||||
        alloc.min_size = minWidth + this._spacing;
 | 
			
		||||
        alloc.natural_size = natWidth + this._spacing;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function(actor, forWidth, alloc) {
 | 
			
		||||
        /* FIXME: StBoxlayout currently does not handle
 | 
			
		||||
           height-for-width children correctly, so hard-code
 | 
			
		||||
           two lines for the label until that problem is fixed.
 | 
			
		||||
 | 
			
		||||
           https://bugzilla.gnome.org/show_bug.cgi?id=672543 */
 | 
			
		||||
/*
 | 
			
		||||
        let [minBoxHeight, natBoxHeight] =
 | 
			
		||||
            this._box.get_preferred_height(forWidth);
 | 
			
		||||
        let [minLabelHeight, natLabelHeight] =
 | 
			
		||||
            this.label.get_preferred_height(forWidth);
 | 
			
		||||
 | 
			
		||||
        alloc.min_size = Math.max(minBoxHeight, minLabelHeight);
 | 
			
		||||
        alloc.natural_size = Math.max(natBoxHeight, natLabelHeight);
 | 
			
		||||
*/
 | 
			
		||||
        let [minBoxHeight, natBoxHeight] =
 | 
			
		||||
            this._box.get_preferred_height(-1);
 | 
			
		||||
        let [minLabelHeight, natLabelHeight] =
 | 
			
		||||
            this.label.get_preferred_height(-1);
 | 
			
		||||
 | 
			
		||||
        alloc.min_size = Math.max(minBoxHeight, 2 * minLabelHeight);
 | 
			
		||||
        alloc.natural_size = Math.max(natBoxHeight, 2 * natLabelHeight);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function(actor, box, flags) {
 | 
			
		||||
        let availWidth = box.x2 - box.x1;
 | 
			
		||||
        let availHeight = box.y2 - box.y1;
 | 
			
		||||
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
        let [minBoxWidth, natBoxWidth] =
 | 
			
		||||
            this._box.get_preferred_width(-1);
 | 
			
		||||
        let [minBoxHeight, natBoxHeight] =
 | 
			
		||||
            this._box.get_preferred_height(-1);
 | 
			
		||||
        childBox.x1 = box.x1;
 | 
			
		||||
        childBox.x2 = box.x1 + natBoxWidth;
 | 
			
		||||
        childBox.y1 = box.y1;
 | 
			
		||||
        childBox.y2 = box.y1 + natBoxHeight;
 | 
			
		||||
        this._box.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        childBox.x1 = box.x1 + natBoxWidth + this._spacing;
 | 
			
		||||
        childBox.x2 = availWidth - childBox.x1;
 | 
			
		||||
        childBox.y1 = box.y1;
 | 
			
		||||
        childBox.y2 = box.y2;
 | 
			
		||||
        this.label.allocate(childBox, flags);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const CheckBox = new Lang.Class({
 | 
			
		||||
    Name: 'CheckBox',
 | 
			
		||||
 | 
			
		||||
    _init: function(label) {
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'check-box',
 | 
			
		||||
                                     button_mask: St.ButtonMask.ONE,
 | 
			
		||||
                                     toggle_mode: true,
 | 
			
		||||
                                     can_focus: true,
 | 
			
		||||
                                     x_fill: true,
 | 
			
		||||
                                     y_fill: true });
 | 
			
		||||
        this._container = new CheckBoxContainer();
 | 
			
		||||
        this.actor.set_child(this._container.actor);
 | 
			
		||||
 | 
			
		||||
        if (label)
 | 
			
		||||
            this.setLabel(label);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setLabel: function(label) {
 | 
			
		||||
        this._container.label.set_text(label);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getLabelActor: function() {
 | 
			
		||||
        return this._container.label;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -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,227 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Pango = imports.gi.Pango;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GObject = imports.gi.GObject;
 | 
			
		||||
const Gcr = imports.gi.Gcr;
 | 
			
		||||
 | 
			
		||||
const ModalDialog = imports.ui.modalDialog;
 | 
			
		||||
const ShellEntry = imports.ui.shellEntry;
 | 
			
		||||
const CheckBox = imports.ui.checkBox;
 | 
			
		||||
 | 
			
		||||
let prompter = null;
 | 
			
		||||
 | 
			
		||||
const KeyringDialog = new Lang.Class({
 | 
			
		||||
    Name: 'KeyringDialog',
 | 
			
		||||
    Extends: ModalDialog.ModalDialog,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent({ styleClass: 'prompt-dialog' });
 | 
			
		||||
 | 
			
		||||
        this.prompt = new Shell.KeyringPrompt();
 | 
			
		||||
        this.prompt.connect('show-password', Lang.bind(this, this._onShowPassword));
 | 
			
		||||
        this.prompt.connect('show-confirm', Lang.bind(this, this._onShowConfirm));
 | 
			
		||||
        this.prompt.connect('hide-prompt', Lang.bind(this, this._onHidePrompt));
 | 
			
		||||
 | 
			
		||||
        let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
 | 
			
		||||
                                                vertical: false });
 | 
			
		||||
        this.contentLayout.add(mainContentBox);
 | 
			
		||||
 | 
			
		||||
        let icon = new St.Icon({ icon_name: 'dialog-password-symbolic' });
 | 
			
		||||
        mainContentBox.add(icon,
 | 
			
		||||
                           { x_fill:  true,
 | 
			
		||||
                             y_fill:  false,
 | 
			
		||||
                             x_align: St.Align.END,
 | 
			
		||||
                             y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        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 });
 | 
			
		||||
 | 
			
		||||
        let subject = new St.Label({ style_class: 'prompt-dialog-headline' });
 | 
			
		||||
        this.prompt.bind_property('message', subject, 'text', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
 | 
			
		||||
        this._messageBox.add(subject,
 | 
			
		||||
                             { y_fill:  false,
 | 
			
		||||
                               y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        let description = new St.Label({ style_class: 'prompt-dialog-description' });
 | 
			
		||||
        description.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
        description.clutter_text.line_wrap = true;
 | 
			
		||||
        this.prompt.bind_property('description', description, 'text', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
        this._messageBox.add(description,
 | 
			
		||||
                            { y_fill:  true,
 | 
			
		||||
                              y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._controlTable = null;
 | 
			
		||||
 | 
			
		||||
        let buttons = [{ label: '',
 | 
			
		||||
                         action: Lang.bind(this, this._onCancelButton),
 | 
			
		||||
                         key:    Clutter.Escape
 | 
			
		||||
                       },
 | 
			
		||||
                       { label: '',
 | 
			
		||||
                         action: Lang.bind(this, this._onContinueButton),
 | 
			
		||||
                         default: true
 | 
			
		||||
                       }]
 | 
			
		||||
 | 
			
		||||
        this.setButtons(buttons);
 | 
			
		||||
        this._cancelButton = buttons[0].button;
 | 
			
		||||
        this._continueButton = buttons[1].button;
 | 
			
		||||
 | 
			
		||||
        this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
        this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildControlTable: function() {
 | 
			
		||||
        let table = new St.Table({ style_class: 'keyring-dialog-control-table' });
 | 
			
		||||
        let row = 0;
 | 
			
		||||
 | 
			
		||||
        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 });
 | 
			
		||||
            this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
 | 
			
		||||
                                                 text: '',
 | 
			
		||||
                                                 can_focus: true});
 | 
			
		||||
            this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
 | 
			
		||||
            ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
 | 
			
		||||
            this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onPasswordActivate));
 | 
			
		||||
            table.add(this._passwordEntry, { row: row, col: 1, x_expand: true, x_fill: true, x_align: St.Align.START });
 | 
			
		||||
            row++;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._passwordEntry = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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 });
 | 
			
		||||
            this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
 | 
			
		||||
                                                text: '',
 | 
			
		||||
                                                can_focus: true});
 | 
			
		||||
            this._confirmEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
 | 
			
		||||
            ShellEntry.addContextMenu(this._confirmEntry, { isPassword: true });
 | 
			
		||||
            this._confirmEntry.clutter_text.connect('activate', Lang.bind(this, this._onConfirmActivate));
 | 
			
		||||
            table.add(this._confirmEntry, { row: row, col: 1, x_expand: true, x_fill: true, x_align: St.Align.START });
 | 
			
		||||
            row++;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._confirmEntry = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.prompt.set_password_actor(this._passwordEntry ? this._passwordEntry.clutter_text : null);
 | 
			
		||||
        this.prompt.set_confirm_actor(this._confirmEntry ? this._confirmEntry.clutter_text : null);
 | 
			
		||||
 | 
			
		||||
        if (this.prompt.choice_visible) {
 | 
			
		||||
            let choice = new CheckBox.CheckBox();
 | 
			
		||||
            this.prompt.bind_property('choice-label', choice.getLabelActor(), 'text', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
            this.prompt.bind_property('choice-chosen', choice.actor, 'checked', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL);
 | 
			
		||||
            table.add(choice.actor, { row: row, col: 1, x_expand: false, x_fill: true, x_align: St.Align.START });
 | 
			
		||||
            row++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let warning = new St.Label({ style_class: 'prompt-dialog-error-label' });
 | 
			
		||||
        warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
        warning.clutter_text.line_wrap = true;
 | 
			
		||||
        table.add(warning, { row: row, col: 1, x_expand: false, x_fill: false, x_align: St.Align.START });
 | 
			
		||||
        this.prompt.bind_property('warning-visible', warning, 'visible', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
        this.prompt.bind_property('warning', warning, 'text', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
 | 
			
		||||
        if (this._controlTable) {
 | 
			
		||||
            this._controlTable.destroy_all_children();
 | 
			
		||||
            this._controlTable.destroy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._controlTable = table;
 | 
			
		||||
        this._messageBox.add(table, { x_fill: true, y_fill: true });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureOpen: function() {
 | 
			
		||||
        // NOTE: ModalDialog.open() is safe to call if the dialog is
 | 
			
		||||
        // already open - it just returns true without side-effects
 | 
			
		||||
        if (this.open())
 | 
			
		||||
          return true;
 | 
			
		||||
 | 
			
		||||
        // The above fail if e.g. unable to get input grab
 | 
			
		||||
        //
 | 
			
		||||
        // In an ideal world this wouldn't happen (because the
 | 
			
		||||
        // Shell is in complete control of the session) but that's
 | 
			
		||||
        // just not how things work right now.
 | 
			
		||||
 | 
			
		||||
        log('keyringPrompt: Failed to show modal dialog.' +
 | 
			
		||||
            ' Dismissing prompt request');
 | 
			
		||||
        this.prompt.cancel()
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onShowPassword: function(prompt) {
 | 
			
		||||
        this._buildControlTable();
 | 
			
		||||
        this._ensureOpen();
 | 
			
		||||
        this._passwordEntry.grab_key_focus();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onShowConfirm: function(prompt) {
 | 
			
		||||
        this._buildControlTable();
 | 
			
		||||
        this._ensureOpen();
 | 
			
		||||
        this._continueButton.grab_key_focus();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onHidePrompt: function(prompt) {
 | 
			
		||||
        this.close();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPasswordActivate: function() {
 | 
			
		||||
        if (this.prompt.confirm_visible)
 | 
			
		||||
            this._confirmEntry.grab_key_focus();
 | 
			
		||||
        else
 | 
			
		||||
            this._onContinueButton();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onConfirmActivate: function() {
 | 
			
		||||
        this._onContinueButton();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onContinueButton: function() {
 | 
			
		||||
        this.prompt.complete();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCancelButton: function() {
 | 
			
		||||
        this.prompt.cancel();
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const KeyringPrompter = new Lang.Class({
 | 
			
		||||
    Name: 'KeyringPrompter',
 | 
			
		||||
 | 
			
		||||
    _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;
 | 
			
		||||
@@ -1,62 +0,0 @@
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
 | 
			
		||||
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() {
 | 
			
		||||
        Main.wm.addKeybinding('toggle-recording',
 | 
			
		||||
                              this._bindingSettings,
 | 
			
		||||
                              Meta.KeyBindingFlags.NONE,
 | 
			
		||||
                              Main.KeybindingMode.NORMAL |
 | 
			
		||||
                              Main.KeybindingMode.OVERVIEW,
 | 
			
		||||
                              Lang.bind(this, this._toggleRecorder));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    disable: function() {
 | 
			
		||||
        Main.wm.removeKeybinding('toggle-recording');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureRecorder: function() {
 | 
			
		||||
        if (this._recorder == null)
 | 
			
		||||
            this._recorder = new Shell.Recorder({ stage: global.stage });
 | 
			
		||||
        return this._recorder;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _toggleRecorder: function() {
 | 
			
		||||
        let recorder = this._ensureRecorder();
 | 
			
		||||
        if (recorder.is_recording()) {
 | 
			
		||||
            recorder.close();
 | 
			
		||||
            Meta.enable_unredirect_for_screen(global.screen);
 | 
			
		||||
        } else if (!this._desktopLockdownSettings.get_boolean('disable-save-to-disk')) {
 | 
			
		||||
            // read the parameters from GSettings always in case they have changed
 | 
			
		||||
            recorder.set_framerate(this._recorderSettings.get_int('framerate'));
 | 
			
		||||
            /* Translators: this is a filename used for screencast recording */
 | 
			
		||||
            // xgettext:no-c-format
 | 
			
		||||
            recorder.set_file_template(_("Screencast from %d %t") + '.' + this._recorderSettings.get_string('file-extension'));
 | 
			
		||||
            let pipeline = this._recorderSettings.get_string('pipeline');
 | 
			
		||||
 | 
			
		||||
            if (!pipeline.match(/^\s*$/))
 | 
			
		||||
                recorder.set_pipeline(pipeline);
 | 
			
		||||
            else
 | 
			
		||||
                recorder.set_pipeline(null);
 | 
			
		||||
 | 
			
		||||
            Meta.disable_unredirect_for_screen(global.screen);
 | 
			
		||||
            recorder.record();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const Component = Recorder;
 | 
			
		||||
							
								
								
									
										185
									
								
								js/ui/contactDisplay.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,185 @@
 | 
			
		||||
// -*- 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 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 */
 | 
			
		||||
function Contact(id) {
 | 
			
		||||
    this._init(id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Contact.prototype = {
 | 
			
		||||
    _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,
 | 
			
		||||
                                  track_hover: true });
 | 
			
		||||
 | 
			
		||||
        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 });
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
          default:
 | 
			
		||||
            text = _("Offline");
 | 
			
		||||
            iconName = 'user-offline';
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
        let icon = new St.Icon({ icon_name: iconName,
 | 
			
		||||
                                 icon_type: St.IconType.FULLCOLOR,
 | 
			
		||||
                                 icon_size: 16,
 | 
			
		||||
                                 style_class: 'contact-details-status-icon' });
 | 
			
		||||
        let label = new St.Label({ text: text });
 | 
			
		||||
 | 
			
		||||
        let box = new St.BoxLayout({ vertical: false,
 | 
			
		||||
                                     style_class: 'contact-details-status' });
 | 
			
		||||
        box.add(icon, { x_fill: true,
 | 
			
		||||
                        y_fill: false,
 | 
			
		||||
                        x_align: St.Align.START,
 | 
			
		||||
                        y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        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 */
 | 
			
		||||
function ContactSearchProvider() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ContactSearchProvider.prototype = {
 | 
			
		||||
    __proto__: Search.SearchProvider.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        Search.SearchProvider.prototype._init.call(this, _("CONTACTS"));
 | 
			
		||||
        this._contactSys = Shell.ContactSystem.get_default();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getResultMeta: function(id) {
 | 
			
		||||
        let contact = new Contact(id);
 | 
			
		||||
        return { 'id': id,
 | 
			
		||||
                 'name': contact.alias,
 | 
			
		||||
                 'createIcon': function(size) {
 | 
			
		||||
                         return contact.createIcon(size);
 | 
			
		||||
                 }
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getInitialResultSet: function(terms) {
 | 
			
		||||
        return this._contactSys.initial_search(terms);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getSubsearchResultSet: function(previousResults, terms) {
 | 
			
		||||
        return 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);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -22,11 +22,14 @@ const SortGroup = {
 | 
			
		||||
    BOTTOM: 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const CtrlAltTabManager = new Lang.Class({
 | 
			
		||||
    Name: 'CtrlAltTabManager',
 | 
			
		||||
function CtrlAltTabManager() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CtrlAltTabManager.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._items = [];
 | 
			
		||||
        this._focusManager = St.FocusManager.get_for_stage(global.stage);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addGroup: function(root, name, icon, params) {
 | 
			
		||||
@@ -40,11 +43,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 +57,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,
 | 
			
		||||
@@ -132,15 +134,17 @@ const CtrlAltTabManager = new Lang.Class({
 | 
			
		||||
                                      }));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function mod(a, b) {
 | 
			
		||||
    return (a + b) % b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const CtrlAltTabPopup = new Lang.Class({
 | 
			
		||||
    Name: 'CtrlAltTabPopup',
 | 
			
		||||
function CtrlAltTabPopup() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CtrlAltTabPopup.prototype = {
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        this.actor = new Shell.GenericContainer({ name: 'ctrlAltTabPopup',
 | 
			
		||||
                                                  reactive: true });
 | 
			
		||||
@@ -183,7 +187,7 @@ const CtrlAltTabPopup = new Lang.Class({
 | 
			
		||||
        let [childMinHeight, childNaturalHeight] = this._switcher.actor.get_preferred_height(primary.width - hPadding);
 | 
			
		||||
        let [childMinWidth, childNaturalWidth] = this._switcher.actor.get_preferred_width(childNaturalHeight);
 | 
			
		||||
        childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - childNaturalWidth) / 2));
 | 
			
		||||
        childBox.x2 = Math.min(primary.x + primary.width - hPadding, childBox.x1 + childNaturalWidth);
 | 
			
		||||
        childBox.x2 = Math.min(primary.width - hPadding, childBox.x1 + childNaturalWidth);
 | 
			
		||||
        childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
 | 
			
		||||
        childBox.y2 = childBox.y1 + childNaturalHeight;
 | 
			
		||||
        this._switcher.actor.allocate(childBox, flags);
 | 
			
		||||
@@ -233,7 +237,7 @@ const CtrlAltTabPopup = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _keyPressEvent : function(actor, event) {
 | 
			
		||||
        let keysym = event.get_key_symbol();
 | 
			
		||||
        let shift = (event.get_state() & Clutter.ModifierType.SHIFT_MASK);
 | 
			
		||||
        let shift = (Shell.get_event_state(event) & Clutter.ModifierType.SHIFT_MASK);
 | 
			
		||||
        if (shift && keysym == Clutter.KEY_Tab)
 | 
			
		||||
            keysym = Clutter.ISO_Left_Tab;
 | 
			
		||||
 | 
			
		||||
@@ -299,14 +303,17 @@ const CtrlAltTabPopup = new Lang.Class({
 | 
			
		||||
        this._selection = num;
 | 
			
		||||
        this._switcher.highlight(num);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const CtrlAltTabSwitcher = new Lang.Class({
 | 
			
		||||
    Name: 'CtrlAltTabSwitcher',
 | 
			
		||||
    Extends: AltTab.SwitcherList,
 | 
			
		||||
function CtrlAltTabSwitcher(items) {
 | 
			
		||||
    this._init(items);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CtrlAltTabSwitcher.prototype = {
 | 
			
		||||
    __proto__ : AltTab.SwitcherList.prototype,
 | 
			
		||||
 | 
			
		||||
    _init : function(items) {
 | 
			
		||||
        this.parent(true);
 | 
			
		||||
        AltTab.SwitcherList.prototype._init.call(this, true);
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < items.length; i++)
 | 
			
		||||
            this._addIcon(items[i]);
 | 
			
		||||
@@ -319,6 +326,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 } );
 | 
			
		||||
@@ -328,4 +336,4 @@ const CtrlAltTabSwitcher = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.addItem(box, text);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										450
									
								
								js/ui/dash.js
									
									
									
									
									
								
							
							
						
						@@ -6,7 +6,6 @@ const Lang = imports.lang;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
 | 
			
		||||
const AppDisplay = imports.ui.appDisplay;
 | 
			
		||||
const AppFavorites = imports.ui.appFavorites;
 | 
			
		||||
@@ -17,26 +16,14 @@ const Tweener = imports.ui.tweener;
 | 
			
		||||
const Workspace = imports.ui.workspace;
 | 
			
		||||
 | 
			
		||||
const DASH_ANIMATION_TIME = 0.2;
 | 
			
		||||
const DASH_ITEM_LABEL_SHOW_TIME = 0.15;
 | 
			
		||||
const DASH_ITEM_LABEL_HIDE_TIME = 0.1;
 | 
			
		||||
const DASH_ITEM_HOVER_TIMEOUT = 300;
 | 
			
		||||
 | 
			
		||||
function getAppFromSource(source) {
 | 
			
		||||
    if (source instanceof AppDisplay.AppWellIcon) {
 | 
			
		||||
        return source.app;
 | 
			
		||||
    } else if (source.metaWindow) {
 | 
			
		||||
        let tracker = Shell.WindowTracker.get_default();
 | 
			
		||||
        return tracker.get_window_app(source.metaWindow);
 | 
			
		||||
    } else {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A container like StBin, but taking the child's scale into account
 | 
			
		||||
// when requesting a size
 | 
			
		||||
const DashItemContainer = new Lang.Class({
 | 
			
		||||
    Name: 'DashItemContainer',
 | 
			
		||||
function DashItemContainer() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DashItemContainer.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new Shell.GenericContainer({ style_class: 'dash-item-container' });
 | 
			
		||||
        this.actor.connect('get-preferred-width',
 | 
			
		||||
@@ -47,12 +34,6 @@ 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.child = null;
 | 
			
		||||
        this._childScale = 1;
 | 
			
		||||
        this._childOpacity = 255;
 | 
			
		||||
@@ -105,61 +86,11 @@ const DashItemContainer = new Lang.Class({
 | 
			
		||||
        alloc.natural_size = natWidth * this.child.scale_y;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    showLabel: function() {
 | 
			
		||||
        if (!this._labelText)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.label.set_text(this._labelText);
 | 
			
		||||
        this.label.opacity = 0;
 | 
			
		||||
        this.label.show();
 | 
			
		||||
 | 
			
		||||
        let [stageX, stageY] = this.actor.get_transformed_position();
 | 
			
		||||
 | 
			
		||||
        let itemHeight = this.actor.allocation.y2 - this.actor.allocation.y1;
 | 
			
		||||
 | 
			
		||||
        let labelHeight = this.label.get_height();
 | 
			
		||||
        let yOffset = Math.floor((itemHeight - labelHeight) / 2)
 | 
			
		||||
 | 
			
		||||
        let y = stageY + yOffset;
 | 
			
		||||
 | 
			
		||||
        let node = this.label.get_theme_node();
 | 
			
		||||
        let xOffset = node.get_length('-x-offset');
 | 
			
		||||
 | 
			
		||||
        let x;
 | 
			
		||||
        if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
 | 
			
		||||
            x = stageX - this.label.get_width() - xOffset;
 | 
			
		||||
        else
 | 
			
		||||
            x = stageX + this.actor.get_width() + xOffset;
 | 
			
		||||
 | 
			
		||||
        this.label.set_position(x, y);
 | 
			
		||||
        Tweener.addTween(this.label,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           time: DASH_ITEM_LABEL_SHOW_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setLabelText: function(text) {
 | 
			
		||||
        this._labelText = text;
 | 
			
		||||
        this.child.accessible_name = text;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hideLabel: function () {
 | 
			
		||||
        Tweener.addTween(this.label,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time: DASH_ITEM_LABEL_HIDE_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this, function() {
 | 
			
		||||
                               this.label.hide();
 | 
			
		||||
                           })
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setChild: function(actor) {
 | 
			
		||||
        if (this.child == actor)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.actor.destroy_all_children();
 | 
			
		||||
        this.actor.destroy_children();
 | 
			
		||||
 | 
			
		||||
        this.child = actor;
 | 
			
		||||
        this.actor.add_actor(this.child);
 | 
			
		||||
@@ -179,17 +110,7 @@ const DashItemContainer = new Lang.Class({
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        if (this.label)
 | 
			
		||||
            this.label.destroy();
 | 
			
		||||
 | 
			
		||||
        this.actor.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    animateOutAndDestroy: function() {
 | 
			
		||||
        if (this.label)
 | 
			
		||||
            this.label.destroy();
 | 
			
		||||
 | 
			
		||||
        if (this.child == null) {
 | 
			
		||||
            this.actor.destroy();
 | 
			
		||||
            return;
 | 
			
		||||
@@ -236,78 +157,57 @@ const DashItemContainer = new Lang.Class({
 | 
			
		||||
    get childOpacity() {
 | 
			
		||||
        return this._childOpacity;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ShowAppsIcon = new Lang.Class({
 | 
			
		||||
    Name: 'ShowAppsIcon',
 | 
			
		||||
    Extends: DashItemContainer,
 | 
			
		||||
function RemoveFavoriteIcon() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RemoveFavoriteIcon.prototype = {
 | 
			
		||||
    __proto__: DashItemContainer.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent();
 | 
			
		||||
        DashItemContainer.prototype._init.call(this);
 | 
			
		||||
 | 
			
		||||
        this.toggleButton = new St.Button({ style_class: 'show-apps',
 | 
			
		||||
                                            track_hover: true,
 | 
			
		||||
                                            can_focus: true,
 | 
			
		||||
                                            toggle_mode: true });
 | 
			
		||||
        this._iconBin = new St.Bin({ style_class: 'remove-favorite' });
 | 
			
		||||
        this._iconActor = null;
 | 
			
		||||
        this.icon = new IconGrid.BaseIcon(_("Show Applications"),
 | 
			
		||||
        this.icon = new IconGrid.BaseIcon(_("Remove"),
 | 
			
		||||
                                           { setSizeManually: true,
 | 
			
		||||
                                             showLabel: false,
 | 
			
		||||
                                             createIcon: Lang.bind(this, this._createIcon) });
 | 
			
		||||
        this.toggleButton.add_actor(this.icon.actor);
 | 
			
		||||
        this.toggleButton._delegate = this;
 | 
			
		||||
        this._iconBin.set_child(this.icon.actor);
 | 
			
		||||
        this._iconBin._delegate = this;
 | 
			
		||||
 | 
			
		||||
        this.setChild(this.toggleButton);
 | 
			
		||||
        this.setDragApp(null);
 | 
			
		||||
        this.setChild(this._iconBin);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createIcon: function(size) {
 | 
			
		||||
        this._iconActor = new St.Icon({ icon_name: 'view-grid-symbolic',
 | 
			
		||||
                                        icon_size: size,
 | 
			
		||||
                                        style_class: 'show-apps-icon',
 | 
			
		||||
                                        track_hover: true });
 | 
			
		||||
        this._iconActor = new St.Icon({ icon_name: 'user-trash',
 | 
			
		||||
                                        style_class: 'remove-favorite-icon',
 | 
			
		||||
                                        icon_size: size });
 | 
			
		||||
        return this._iconActor;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _canRemoveApp: function(app) {
 | 
			
		||||
        if (app == null)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        let id = app.get_id();
 | 
			
		||||
        let isFavorite = AppFavorites.getAppFavorites().isFavorite(id);
 | 
			
		||||
        return isFavorite;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setDragApp: function(app) {
 | 
			
		||||
        let canRemove = this._canRemoveApp(app);
 | 
			
		||||
 | 
			
		||||
        this.toggleButton.set_hover(canRemove);
 | 
			
		||||
    setHover: function(hovered) {
 | 
			
		||||
        this._iconBin.set_hover(hovered);
 | 
			
		||||
        if (this._iconActor)
 | 
			
		||||
            this._iconActor.set_hover(canRemove);
 | 
			
		||||
 | 
			
		||||
        if (canRemove)
 | 
			
		||||
            this.setLabelText(_("Remove from Favorites"));
 | 
			
		||||
        else
 | 
			
		||||
            this.setLabelText(_("Show Applications"));
 | 
			
		||||
            this._iconActor.set_hover(hovered);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Rely on the dragged item being a favorite
 | 
			
		||||
    handleDragOver: function(source, actor, x, y, time) {
 | 
			
		||||
        let app = getAppFromSource(source);
 | 
			
		||||
        if (app == null)
 | 
			
		||||
            return DND.DragMotionResult.NO_DROP;
 | 
			
		||||
 | 
			
		||||
        let id = app.get_id();
 | 
			
		||||
        let isFavorite = AppFavorites.getAppFavorites().isFavorite(id);
 | 
			
		||||
        if (!isFavorite)
 | 
			
		||||
            return DND.DragMotionResult.NO_DROP;
 | 
			
		||||
 | 
			
		||||
        return DND.DragMotionResult.MOVE_DROP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    acceptDrop: function(source, actor, x, y, time) {
 | 
			
		||||
        let app = getAppFromSource(source);
 | 
			
		||||
        if (app == null)
 | 
			
		||||
            return false;
 | 
			
		||||
        let app = null;
 | 
			
		||||
        if (source instanceof AppDisplay.AppWellIcon) {
 | 
			
		||||
            let appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
            app = appSystem.lookup_app(source.getId());
 | 
			
		||||
        } else if (source.metaWindow) {
 | 
			
		||||
            let tracker = Shell.WindowTracker.get_default();
 | 
			
		||||
            app = tracker.get_window_app(source.metaWindow);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let id = app.get_id();
 | 
			
		||||
 | 
			
		||||
@@ -319,54 +219,28 @@ const ShowAppsIcon = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const DragPlaceholderItem = new Lang.Class({
 | 
			
		||||
    Name: 'DragPlaceholderItem',
 | 
			
		||||
    Extends: DashItemContainer,
 | 
			
		||||
 | 
			
		||||
function DragPlaceholderItem() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DragPlaceholderItem.prototype = {
 | 
			
		||||
    __proto__: DashItemContainer.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent();
 | 
			
		||||
        this.setChild(new St.Bin({ style_class: 'placeholder' }));
 | 
			
		||||
        DashItemContainer.prototype._init.call(this);
 | 
			
		||||
        this.setChild(new St.Bin({ style_class: 'dash-placeholder' }));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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',
 | 
			
		||||
function Dash() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Dash.prototype = {
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        this._maxHeight = -1;
 | 
			
		||||
        this.iconSize = 64;
 | 
			
		||||
@@ -375,25 +249,14 @@ const Dash = new Lang.Class({
 | 
			
		||||
        this._dragPlaceholder = null;
 | 
			
		||||
        this._dragPlaceholderPos = -1;
 | 
			
		||||
        this._animatingPlaceholdersCount = 0;
 | 
			
		||||
        this._showLabelTimeoutId = 0;
 | 
			
		||||
        this._resetHoverTimeoutId = 0;
 | 
			
		||||
        this._labelShowing = false;
 | 
			
		||||
        this._favRemoveTarget = null;
 | 
			
		||||
 | 
			
		||||
        this._container = new DashActor();
 | 
			
		||||
        this._box = new St.BoxLayout({ vertical: true,
 | 
			
		||||
        this._box = new St.BoxLayout({ name: 'dash',
 | 
			
		||||
                                       vertical: true,
 | 
			
		||||
                                       clip_to_allocation: true });
 | 
			
		||||
        this._box._delegate = this;
 | 
			
		||||
        this._container.add_actor(this._box);
 | 
			
		||||
 | 
			
		||||
        this._showAppsIcon = new ShowAppsIcon();
 | 
			
		||||
        this._showAppsIcon.icon.setIconSize(this.iconSize);
 | 
			
		||||
        this._hookUpLabel(this._showAppsIcon);
 | 
			
		||||
 | 
			
		||||
        this.showAppsButton = this._showAppsIcon.toggleButton;
 | 
			
		||||
 | 
			
		||||
        this._container.add_actor(this._showAppsIcon.actor);
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Bin({ child: this._container });
 | 
			
		||||
        this.actor = new St.Bin({ y_align: St.Align.START, child: this._box });
 | 
			
		||||
        this.actor.connect('notify::height', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                if (this._maxHeight != this.actor.height)
 | 
			
		||||
@@ -403,6 +266,7 @@ const Dash = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay));
 | 
			
		||||
 | 
			
		||||
        this._tracker = Shell.WindowTracker.get_default();
 | 
			
		||||
        this._appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
 | 
			
		||||
        this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
 | 
			
		||||
@@ -445,25 +309,53 @@ 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 &&
 | 
			
		||||
            dragEvent.source.actor &&
 | 
			
		||||
            this.actor.contains (dragEvent.source.actor) &&
 | 
			
		||||
            this._favRemoveTarget == null) {
 | 
			
		||||
                this._favRemoveTarget = new RemoveFavoriteIcon();
 | 
			
		||||
                this._favRemoveTarget.icon.setIconSize(this.iconSize);
 | 
			
		||||
                this._box.add(this._favRemoveTarget.actor);
 | 
			
		||||
                this._adjustIconSize();
 | 
			
		||||
                this._favRemoveTarget.animateIn();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let favRemoveHovered = false;
 | 
			
		||||
        if (this._favRemoveTarget)
 | 
			
		||||
            favRemoveHovered =
 | 
			
		||||
                this._favRemoveTarget.actor.contains(dragEvent.targetActor);
 | 
			
		||||
 | 
			
		||||
        if (!this._box.contains(dragEvent.targetActor) || favRemoveHovered)
 | 
			
		||||
            this._clearDragPlaceholder();
 | 
			
		||||
 | 
			
		||||
        if (showAppsHovered)
 | 
			
		||||
            this._showAppsIcon.setDragApp(app);
 | 
			
		||||
        else
 | 
			
		||||
            this._showAppsIcon.setDragApp(null);
 | 
			
		||||
        if (this._favRemoveTarget)
 | 
			
		||||
            this._favRemoveTarget.setHover(favRemoveHovered);
 | 
			
		||||
 | 
			
		||||
        return DND.DragMotionResult.CONTINUE;
 | 
			
		||||
    },
 | 
			
		||||
@@ -479,91 +371,28 @@ const Dash = new Lang.Class({
 | 
			
		||||
        Main.queueDeferredWork(this._workId);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hookUpLabel: function(item) {
 | 
			
		||||
        item.child.connect('notify::hover', Lang.bind(this, function() {
 | 
			
		||||
            this._onHover(item);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('hiding', Lang.bind(this, function() {
 | 
			
		||||
            this._labelShowing = false;
 | 
			
		||||
            item.hideLabel();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createAppItem: function(app) {
 | 
			
		||||
        let appIcon = new AppDisplay.AppWellIcon(app,
 | 
			
		||||
        let display = new AppDisplay.AppWellIcon(app,
 | 
			
		||||
                                                 { setSizeManually: true,
 | 
			
		||||
                                                   showLabel: false });
 | 
			
		||||
        appIcon._draggable.connect('drag-begin',
 | 
			
		||||
        display._draggable.connect('drag-begin',
 | 
			
		||||
                                   Lang.bind(this, function() {
 | 
			
		||||
                                       appIcon.actor.opacity = 50;
 | 
			
		||||
                                       display.actor.opacity = 50;
 | 
			
		||||
                                   }));
 | 
			
		||||
        appIcon._draggable.connect('drag-end',
 | 
			
		||||
        display._draggable.connect('drag-end',
 | 
			
		||||
                                   Lang.bind(this, function() {
 | 
			
		||||
                                       appIcon.actor.opacity = 255;
 | 
			
		||||
                                       display.actor.opacity = 255;
 | 
			
		||||
                                   }));
 | 
			
		||||
        appIcon.connect('menu-state-changed',
 | 
			
		||||
                        Lang.bind(this, function(appIcon, opened) {
 | 
			
		||||
                            this._itemMenuStateChanged(item, opened);
 | 
			
		||||
                        }));
 | 
			
		||||
        display.actor.set_tooltip_text(app.get_name());
 | 
			
		||||
 | 
			
		||||
        let item = new DashItemContainer();
 | 
			
		||||
        item.setChild(appIcon.actor);
 | 
			
		||||
        item.setChild(display.actor);
 | 
			
		||||
 | 
			
		||||
        // Override default AppWellIcon label_actor, now the
 | 
			
		||||
        // accessible_name is set at DashItemContainer.setLabelText
 | 
			
		||||
        appIcon.actor.label_actor = null;
 | 
			
		||||
        item.setLabelText(app.get_name());
 | 
			
		||||
 | 
			
		||||
        appIcon.icon.setIconSize(this.iconSize);
 | 
			
		||||
        this._hookUpLabel(item);
 | 
			
		||||
        display.icon.setIconSize(this.iconSize);
 | 
			
		||||
 | 
			
		||||
        return item;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _itemMenuStateChanged: function(item, opened) {
 | 
			
		||||
        // When the menu closes, it calls sync_hover, which means
 | 
			
		||||
        // that the notify::hover handler does everything we need to.
 | 
			
		||||
        if (opened) {
 | 
			
		||||
            if (this._showLabelTimeoutId > 0) {
 | 
			
		||||
                Mainloop.source_remove(this._showLabelTimeoutId);
 | 
			
		||||
                this._showLabelTimeoutId = 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            item.hideLabel();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onHover: function (item) {
 | 
			
		||||
        if (item.child.get_hover()) {
 | 
			
		||||
            if (this._showLabelTimeoutId == 0) {
 | 
			
		||||
                let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
 | 
			
		||||
                this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
 | 
			
		||||
                    Lang.bind(this, function() {
 | 
			
		||||
                        this._labelShowing = true;
 | 
			
		||||
                        item.showLabel();
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }));
 | 
			
		||||
                if (this._resetHoverTimeoutId > 0) {
 | 
			
		||||
                    Mainloop.source_remove(this._resetHoverTimeoutId);
 | 
			
		||||
                    this._resetHoverTimeoutId = 0;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (this._showLabelTimeoutId > 0)
 | 
			
		||||
                Mainloop.source_remove(this._showLabelTimeoutId);
 | 
			
		||||
            this._showLabelTimeoutId = 0;
 | 
			
		||||
            item.hideLabel();
 | 
			
		||||
            if (this._labelShowing) {
 | 
			
		||||
                this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT,
 | 
			
		||||
                    Lang.bind(this, function() {
 | 
			
		||||
                        this._labelShowing = false;
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _adjustIconSize: function() {
 | 
			
		||||
        // For the icon size, we only consider children which are "proper"
 | 
			
		||||
        // icons (i.e. ignoring drag placeholders) and which are not
 | 
			
		||||
@@ -576,12 +405,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.actor.get_theme_node();
 | 
			
		||||
        let maxAllocation = new Clutter.ActorBox({ x1: 0, y1: 0,
 | 
			
		||||
                                                   x2: 42 /* whatever */,
 | 
			
		||||
                                                   y2: this._maxHeight });
 | 
			
		||||
@@ -607,6 +442,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;
 | 
			
		||||
@@ -637,10 +473,8 @@ const Dash = new Lang.Class({
 | 
			
		||||
            icon.setIconSize(this.iconSize);
 | 
			
		||||
 | 
			
		||||
            // Don't animate the icon size change when the overview
 | 
			
		||||
            // is transitioning, not visible or when initially filling
 | 
			
		||||
            // the dash
 | 
			
		||||
            if (!Main.overview.visible || Main.overview.animationInProgress ||
 | 
			
		||||
                !this._shownInitially)
 | 
			
		||||
            // is not visible or when initially filling the dash
 | 
			
		||||
            if (!Main.overview.visible || !this._shownInitially)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            let [targetWidth, targetHeight] = icon.icon.get_size();
 | 
			
		||||
@@ -758,18 +592,17 @@ const Dash = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < addedItems.length; i++)
 | 
			
		||||
            this._box.insert_child_at_index(addedItems[i].item.actor,
 | 
			
		||||
                                            addedItems[i].pos);
 | 
			
		||||
            this._box.insert_actor(addedItems[i].item.actor,
 | 
			
		||||
                                   addedItems[i].pos);
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < removedActors.length; i++) {
 | 
			
		||||
            let item = removedActors[i]._delegate;
 | 
			
		||||
 | 
			
		||||
            // Don't animate item removal when the overview is transitioning
 | 
			
		||||
            // or hidden
 | 
			
		||||
            if (Main.overview.visible && !Main.overview.animationInProgress)
 | 
			
		||||
            // Don't animate item removal when the overview is hidden
 | 
			
		||||
            if (Main.overview.visible)
 | 
			
		||||
                item.animateOutAndDestroy();
 | 
			
		||||
            else
 | 
			
		||||
                item.destroy();
 | 
			
		||||
                item.actor.destroy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._adjustIconSize();
 | 
			
		||||
@@ -781,9 +614,8 @@ const Dash = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Don't animate item addition when the overview is transitioning
 | 
			
		||||
        // or hidden
 | 
			
		||||
        if (!Main.overview.visible || Main.overview.animationInProgress)
 | 
			
		||||
        // Don't animate item addition when the overview is hidden
 | 
			
		||||
        if (!Main.overview.visible)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < addedItems.length; i++)
 | 
			
		||||
@@ -799,7 +631,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())
 | 
			
		||||
@@ -822,10 +658,20 @@ const Dash = new Lang.Class({
 | 
			
		||||
            numChildren--;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let pos = Math.floor(y * numChildren / boxHeight);
 | 
			
		||||
        let pos = Math.round(y * numChildren / boxHeight);
 | 
			
		||||
 | 
			
		||||
        if (pos != this._dragPlaceholderPos && pos <= numFavorites && this._animatingPlaceholdersCount == 0) {
 | 
			
		||||
            this._dragPlaceholderPos = pos;
 | 
			
		||||
        if (pos != this._dragPlaceholderPos && pos <= numFavorites) {
 | 
			
		||||
            if (this._animatingPlaceholdersCount > 0) {
 | 
			
		||||
                let appChildren = children.filter(function(actor) {
 | 
			
		||||
                    return actor._delegate &&
 | 
			
		||||
                           actor._delegate.child &&
 | 
			
		||||
                           actor._delegate.child._delegate &&
 | 
			
		||||
                           actor._delegate.child._delegate.app;
 | 
			
		||||
                });
 | 
			
		||||
                this._dragPlaceholderPos = children.indexOf(appChildren[pos]);
 | 
			
		||||
            } else {
 | 
			
		||||
                this._dragPlaceholderPos = pos;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Don't allow positioning before or after self
 | 
			
		||||
            if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
 | 
			
		||||
@@ -856,20 +702,12 @@ const Dash = new Lang.Class({
 | 
			
		||||
            this._dragPlaceholder = new DragPlaceholderItem();
 | 
			
		||||
            this._dragPlaceholder.child.set_width (this.iconSize);
 | 
			
		||||
            this._dragPlaceholder.child.set_height (this.iconSize / 2);
 | 
			
		||||
            this._box.insert_child_at_index(this._dragPlaceholder.actor,
 | 
			
		||||
                                            this._dragPlaceholderPos);
 | 
			
		||||
            this._box.insert_actor(this._dragPlaceholder.actor,
 | 
			
		||||
                                   this._dragPlaceholderPos);
 | 
			
		||||
            if (fadeIn)
 | 
			
		||||
                this._dragPlaceholder.animateIn();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Remove the drag placeholder if we are not in the
 | 
			
		||||
        // "favorites zone"
 | 
			
		||||
        if (pos > numFavorites && this._dragPlaceholder) {
 | 
			
		||||
            this._clearDragPlaceholder();
 | 
			
		||||
        }
 | 
			
		||||
        if (!this._dragPlaceholder)
 | 
			
		||||
            return DND.DragMotionResult.NO_DROP;
 | 
			
		||||
 | 
			
		||||
        let srcIsFavorite = (favPos != -1);
 | 
			
		||||
 | 
			
		||||
        if (srcIsFavorite)
 | 
			
		||||
@@ -880,7 +718,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()) {
 | 
			
		||||
@@ -907,11 +750,6 @@ const Dash = new Lang.Class({
 | 
			
		||||
                favPos++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // No drag placeholder means we don't wan't to favorite the app
 | 
			
		||||
        // and we are dragging it to its original position
 | 
			
		||||
        if (!this._dragPlaceholder)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
 | 
			
		||||
            function () {
 | 
			
		||||
                let appFavorites = AppFavorites.getAppFavorites();
 | 
			
		||||
@@ -924,6 +762,6 @@ const Dash = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(Dash.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -2,14 +2,12 @@
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
@@ -17,6 +15,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)
 | 
			
		||||
{
 | 
			
		||||
@@ -34,27 +40,27 @@ function _onVertSepRepaint (area)
 | 
			
		||||
    cr.stroke();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const DateMenuButton = new Lang.Class({
 | 
			
		||||
    Name: 'DateMenuButton',
 | 
			
		||||
    Extends: PanelMenu.Button,
 | 
			
		||||
function DateMenuButton() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DateMenuButton.prototype = {
 | 
			
		||||
    __proto__: PanelMenu.Button.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        params = Params.parse(params, { showEvents: true });
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        let item;
 | 
			
		||||
        let hbox;
 | 
			
		||||
        let vbox;
 | 
			
		||||
 | 
			
		||||
        let menuAlignment = 0.25;
 | 
			
		||||
        if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
 | 
			
		||||
        if (St.Widget.get_default_direction() == St.TextDirection.RTL)
 | 
			
		||||
            menuAlignment = 1.0 - menuAlignment;
 | 
			
		||||
        this.parent(menuAlignment);
 | 
			
		||||
        PanelMenu.Button.prototype._init.call(this, menuAlignment);
 | 
			
		||||
 | 
			
		||||
        // At this moment calendar menu is not keyboard navigable at
 | 
			
		||||
        // all (so not accessible), so it doesn't make sense to set as
 | 
			
		||||
        // 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 +72,19 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        // Date
 | 
			
		||||
        this._date = new St.Label();
 | 
			
		||||
        this.actor.label_actor = this._clockDisplay;
 | 
			
		||||
        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 +102,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 +153,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');
 | 
			
		||||
@@ -225,4 +239,4 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										49
									
								
								js/ui/dnd.js
									
									
									
									
									
								
							
							
						
						@@ -69,9 +69,11 @@ function removeDragMonitor(monitor) {
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const _Draggable = new Lang.Class({
 | 
			
		||||
    Name: 'Draggable',
 | 
			
		||||
function _Draggable(actor, params) {
 | 
			
		||||
    this._init(actor, params);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_Draggable.prototype = {
 | 
			
		||||
    _init : function(actor, params) {
 | 
			
		||||
        params = Params.parse(params, { manualMode: false,
 | 
			
		||||
                                        restoreOnSuccess: false,
 | 
			
		||||
@@ -103,8 +105,8 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
        this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet.
 | 
			
		||||
        this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
 | 
			
		||||
 | 
			
		||||
        // During the drag, we eat enter/leave events so that actors don't prelight.
 | 
			
		||||
        // But we remember the actors that we first left/last entered so we can
 | 
			
		||||
        // During the drag, we eat enter/leave events so that actors don't prelight or show
 | 
			
		||||
        // tooltips. But we remember the actors that we first left/last entered so we can
 | 
			
		||||
        // fix up the hover state after the drag ends.
 | 
			
		||||
        this._firstLeaveActor = null;
 | 
			
		||||
        this._lastEnterActor = null;
 | 
			
		||||
@@ -120,7 +122,13 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        this._buttonDown = true;
 | 
			
		||||
        this._grabActor();
 | 
			
		||||
        // special case St.Button: grabbing the pointer would mess up the
 | 
			
		||||
        // internal state, so we start the drag manually on hover change
 | 
			
		||||
        if (this.actor instanceof St.Button)
 | 
			
		||||
            this.actor.connect('notify::hover',
 | 
			
		||||
                               Lang.bind(this, this._onButtonHoverChanged));
 | 
			
		||||
        else
 | 
			
		||||
            this._grabActor();
 | 
			
		||||
 | 
			
		||||
        let [stageX, stageY] = event.get_coords();
 | 
			
		||||
        this._dragStartX = stageX;
 | 
			
		||||
@@ -129,6 +137,15 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onButtonHoverChanged: function(button) {
 | 
			
		||||
        if (button.hover || !button.pressed)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        button.fake_release();
 | 
			
		||||
        this.startDrag(this._dragStartX, this._dragStartY,
 | 
			
		||||
                       global.get_current_time());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _grabActor: function() {
 | 
			
		||||
        Clutter.grab_pointer(this.actor);
 | 
			
		||||
        this._onEventId = this.actor.connect('event',
 | 
			
		||||
@@ -217,13 +234,6 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
        currentDraggable = this;
 | 
			
		||||
        this._dragInProgress = true;
 | 
			
		||||
 | 
			
		||||
        // Special-case St.Button: the pointer grab messes with the internal
 | 
			
		||||
        // state, so force a reset to a reasonable state here
 | 
			
		||||
        if (this.actor instanceof St.Button) {
 | 
			
		||||
            this.actor.fake_release();
 | 
			
		||||
            this.actor.hover = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('drag-begin', time);
 | 
			
		||||
        if (this._onEventId)
 | 
			
		||||
            this._ungrabActor();
 | 
			
		||||
@@ -235,10 +245,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 +273,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 +290,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;
 | 
			
		||||
@@ -591,7 +596,7 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
        this._dragActor = undefined;
 | 
			
		||||
        currentDraggable = null;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(_Draggable.prototype);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								js/ui/docDisplay.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,47 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const DocInfo = imports.misc.docInfo;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Search = imports.ui.search;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function DocSearchProvider() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DocSearchProvider.prototype = {
 | 
			
		||||
    __proto__: Search.SearchProvider.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(name) {
 | 
			
		||||
        Search.SearchProvider.prototype._init.call(this, _("RECENT ITEMS"));
 | 
			
		||||
        this._docManager = DocInfo.getDocManager();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getResultMeta: function(resultId) {
 | 
			
		||||
        let docInfo = this._docManager.lookupByUri(resultId);
 | 
			
		||||
        if (!docInfo)
 | 
			
		||||
            return null;
 | 
			
		||||
        return { 'id': resultId,
 | 
			
		||||
                 'name': docInfo.name,
 | 
			
		||||
                 'createIcon': function(size) {
 | 
			
		||||
                                   return docInfo.createIcon(size);
 | 
			
		||||
                               }
 | 
			
		||||
               };
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateResult: function(id, params) {
 | 
			
		||||
        params = Params.parse(params, { workspace: -1,
 | 
			
		||||
                                        timestamp: 0 });
 | 
			
		||||
 | 
			
		||||
        let docInfo = this._docManager.lookupByUri(id);
 | 
			
		||||
        docInfo.launch(params.workspace);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getInitialResultSet: function(terms) {
 | 
			
		||||
        return this._docManager.initialSearch(terms);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getSubsearchResultSet: function(previousResults, terms) {
 | 
			
		||||
        return this._docManager.subsearch(previousResults, terms);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -18,23 +18,23 @@
 | 
			
		||||
 * 02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const AccountsService = imports.gi.AccountsService;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
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 GnomeSession = imports.misc.gnomeSession;
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
@@ -43,23 +43,22 @@ const _DIALOG_ICON_SIZE = 32;
 | 
			
		||||
 | 
			
		||||
const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2;
 | 
			
		||||
 | 
			
		||||
const EndSessionDialogIface = <interface name="org.gnome.SessionManager.EndSessionDialog">
 | 
			
		||||
<method name="Open">
 | 
			
		||||
    <arg type="u" direction="in" />
 | 
			
		||||
    <arg type="u" direction="in" />
 | 
			
		||||
    <arg type="u" direction="in" />
 | 
			
		||||
    <arg type="ao" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
<signal name="ConfirmedLogout" />
 | 
			
		||||
<signal name="ConfirmedReboot" />
 | 
			
		||||
<signal name="ConfirmedShutdown" />
 | 
			
		||||
<signal name="Canceled" />
 | 
			
		||||
<signal name="Closed" />
 | 
			
		||||
</interface>;
 | 
			
		||||
const EndSessionDialogIface = {
 | 
			
		||||
    name: 'org.gnome.SessionManager.EndSessionDialog',
 | 
			
		||||
    methods: [{ name: 'Open',
 | 
			
		||||
                inSignature: 'uuuao',
 | 
			
		||||
                outSignature: ''
 | 
			
		||||
              }
 | 
			
		||||
             ],
 | 
			
		||||
    signals: [{ name: 'Canceled',
 | 
			
		||||
                inSignature: '',
 | 
			
		||||
              }],
 | 
			
		||||
    properties: []
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const logoutDialogContent = {
 | 
			
		||||
    subjectWithUser: C_("title", "Log Out %s"),
 | 
			
		||||
    subject: C_("title", "Log Out"),
 | 
			
		||||
    subjectWithUser: _("Log Out %s"),
 | 
			
		||||
    subject: _("Log Out"),
 | 
			
		||||
    inhibitedDescription: _("Click Log Out to quit these applications and log out of the system."),
 | 
			
		||||
    uninhibitedDescriptionWithUser: function(user, seconds) {
 | 
			
		||||
        return ngettext("%s will be logged out automatically in %d second.",
 | 
			
		||||
@@ -73,12 +72,12 @@ const logoutDialogContent = {
 | 
			
		||||
    },
 | 
			
		||||
    endDescription: _("Logging out of the system."),
 | 
			
		||||
    confirmButtons: [{ signal: 'ConfirmedLogout',
 | 
			
		||||
                       label:  C_("button", "Log Out") }],
 | 
			
		||||
                       label:  _("Log Out") }],
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-logout-icon'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const shutdownDialogContent = {
 | 
			
		||||
    subject: C_("title", "Power Off"),
 | 
			
		||||
    subject: _("Power Off"),
 | 
			
		||||
    inhibitedDescription: _("Click Power Off to quit these applications and power off the system."),
 | 
			
		||||
    uninhibitedDescription: function(seconds) {
 | 
			
		||||
        return ngettext("The system will power off automatically in %d second.",
 | 
			
		||||
@@ -87,15 +86,15 @@ const shutdownDialogContent = {
 | 
			
		||||
    },
 | 
			
		||||
    endDescription: _("Powering off the system."),
 | 
			
		||||
    confirmButtons: [{ signal: 'ConfirmedReboot',
 | 
			
		||||
                       label:  C_("button", "Restart") },
 | 
			
		||||
                       label:  _("Restart") },
 | 
			
		||||
                     { signal: 'ConfirmedShutdown',
 | 
			
		||||
                       label:  C_("button", "Power Off") }],
 | 
			
		||||
    iconName: 'system-shutdown-symbolic',
 | 
			
		||||
                       label:  _("Power Off") }],
 | 
			
		||||
    iconName: 'system-shutdown',
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-shutdown-icon'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const restartDialogContent = {
 | 
			
		||||
    subject: C_("title", "Restart"),
 | 
			
		||||
    subject: _("Restart"),
 | 
			
		||||
    inhibitedDescription: _("Click Restart to quit these applications and restart the system."),
 | 
			
		||||
    uninhibitedDescription: function(seconds) {
 | 
			
		||||
        return ngettext("The system will restart automatically in %d second.",
 | 
			
		||||
@@ -104,8 +103,8 @@ const restartDialogContent = {
 | 
			
		||||
    },
 | 
			
		||||
    endDescription: _("Restarting the system."),
 | 
			
		||||
    confirmButtons: [{ signal: 'ConfirmedReboot',
 | 
			
		||||
                       label:  C_("button", "Restart") }],
 | 
			
		||||
    iconName: 'view-refresh-symbolic',
 | 
			
		||||
                       label:  _("Restart") }],
 | 
			
		||||
    iconName: 'system-shutdown',
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-shutdown-icon'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -116,17 +115,37 @@ const DialogContent = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function findAppFromInhibitor(inhibitor) {
 | 
			
		||||
    let [desktopFile] = inhibitor.GetAppIdSync();
 | 
			
		||||
    let desktopFile = inhibitor.app_id;
 | 
			
		||||
 | 
			
		||||
    if (!GLib.str_has_suffix(desktopFile, '.desktop'))
 | 
			
		||||
        desktopFile += '.desktop';
 | 
			
		||||
      desktopFile += '.desktop';
 | 
			
		||||
 | 
			
		||||
    return Shell.AppSystem.get_default().lookup_heuristic_basename(desktopFile);
 | 
			
		||||
    let candidateDesktopFiles = [];
 | 
			
		||||
 | 
			
		||||
    candidateDesktopFiles.push(desktopFile);
 | 
			
		||||
    candidateDesktopFiles.push('gnome-' + desktopFile);
 | 
			
		||||
 | 
			
		||||
    let appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
    let app = null;
 | 
			
		||||
    for (let i = 0; i < candidateDesktopFiles.length; i++) {
 | 
			
		||||
        try {
 | 
			
		||||
            app = appSystem.lookup_app(candidateDesktopFiles[i]);
 | 
			
		||||
 | 
			
		||||
            if (app)
 | 
			
		||||
                break;
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            // ignore errors
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return app;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ListItem = new Lang.Class({
 | 
			
		||||
    Name: 'ListItem',
 | 
			
		||||
function ListItem(app, reason) {
 | 
			
		||||
    this._init(app, reason);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ListItem.prototype = {
 | 
			
		||||
    _init: function(app, reason) {
 | 
			
		||||
        this._app = app;
 | 
			
		||||
        this._reason = reason;
 | 
			
		||||
@@ -161,7 +180,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 });
 | 
			
		||||
@@ -173,7 +191,7 @@ const ListItem = new Lang.Class({
 | 
			
		||||
        this.emit('activate');
 | 
			
		||||
        this._app.activate();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(ListItem.prototype);
 | 
			
		||||
 | 
			
		||||
// The logout timer only shows updates every 10 seconds
 | 
			
		||||
@@ -211,19 +229,29 @@ function _setLabelText(label, text) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function EndSessionDialog() {
 | 
			
		||||
    if (_endSessionDialog == null) {
 | 
			
		||||
        this._init();
 | 
			
		||||
        DBus.session.exportObject('/org/gnome/SessionManager/EndSessionDialog',
 | 
			
		||||
                                  this);
 | 
			
		||||
        _endSessionDialog = this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return _endSessionDialog;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function init() {
 | 
			
		||||
    // This always returns the same singleton object
 | 
			
		||||
    // By instantiating it initially, we register the
 | 
			
		||||
    // bus object, etc.
 | 
			
		||||
    _endSessionDialog = new EndSessionDialog();
 | 
			
		||||
    let dialog = new EndSessionDialog();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const EndSessionDialog = new Lang.Class({
 | 
			
		||||
    Name: 'EndSessionDialog',
 | 
			
		||||
    Extends: ModalDialog.ModalDialog,
 | 
			
		||||
EndSessionDialog.prototype = {
 | 
			
		||||
    __proto__: ModalDialog.ModalDialog.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent({ styleClass: 'end-session-dialog' });
 | 
			
		||||
        ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'end-session-dialog' });
 | 
			
		||||
 | 
			
		||||
        this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name());
 | 
			
		||||
 | 
			
		||||
@@ -281,22 +309,23 @@ 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();
 | 
			
		||||
                                      }));
 | 
			
		||||
 | 
			
		||||
        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(EndSessionDialogIface, this);
 | 
			
		||||
        this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDestroy: function() {
 | 
			
		||||
@@ -304,7 +333,41 @@ 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 + '");');
 | 
			
		||||
        } 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 +377,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 +420,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),
 | 
			
		||||
@@ -386,20 +439,26 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    close: function() {
 | 
			
		||||
        this.parent();
 | 
			
		||||
        this._dbusImpl.emit_signal('Closed', null);
 | 
			
		||||
        ModalDialog.ModalDialog.prototype.close.call(this);
 | 
			
		||||
        DBus.session.emit_signal('/org/gnome/SessionManager/EndSessionDialog',
 | 
			
		||||
                                 'org.gnome.SessionManager.EndSessionDialog',
 | 
			
		||||
                                 'Closed', '', []);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancel: function() {
 | 
			
		||||
        this._stopTimer();
 | 
			
		||||
        this._dbusImpl.emit_signal('Canceled', null);
 | 
			
		||||
        DBus.session.emit_signal('/org/gnome/SessionManager/EndSessionDialog',
 | 
			
		||||
                                 'org.gnome.SessionManager.EndSessionDialog',
 | 
			
		||||
                                 'Canceled', '', []);
 | 
			
		||||
        this.close(global.get_current_time());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _confirm: function(signal) {
 | 
			
		||||
        this._fadeOutDialog();
 | 
			
		||||
        this._stopTimer();
 | 
			
		||||
        this._dbusImpl.emit_signal(signal, null);
 | 
			
		||||
        DBus.session.emit_signal('/org/gnome/SessionManager/EndSessionDialog',
 | 
			
		||||
                                 'org.gnome.SessionManager.EndSessionDialog',
 | 
			
		||||
                                 signal, '', []);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOpened: function() {
 | 
			
		||||
@@ -413,7 +472,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];
 | 
			
		||||
@@ -436,8 +495,7 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
        let app = findAppFromInhibitor(inhibitor);
 | 
			
		||||
 | 
			
		||||
        if (app) {
 | 
			
		||||
            let [reason] = inhibitor.GetReasonSync();
 | 
			
		||||
            let item = new ListItem(app, reason);
 | 
			
		||||
            let item = new ListItem(app, inhibitor.reason);
 | 
			
		||||
            item.connect('activate',
 | 
			
		||||
                         Lang.bind(this, function() {
 | 
			
		||||
                             this.close(global.get_current_time());
 | 
			
		||||
@@ -452,41 +510,39 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
        this._updateContent();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    OpenAsync: function(parameters, invocation) {
 | 
			
		||||
        let [type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths] = parameters;
 | 
			
		||||
    OpenAsync: function(type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths, callback) {
 | 
			
		||||
        this._totalSecondsToStayOpen = totalSecondsToStayOpen;
 | 
			
		||||
        this._inhibitors = [];
 | 
			
		||||
        this._applicationList.destroy_all_children();
 | 
			
		||||
        this._applicationList.destroy_children();
 | 
			
		||||
        this._type = type;
 | 
			
		||||
 | 
			
		||||
        if (!(this._type in DialogContent)) {
 | 
			
		||||
            invocation.return_dbus_error('org.gnome.Shell.ModalDialog.TypeError',
 | 
			
		||||
                                         "Unknown dialog type requested");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (!(this._type in DialogContent))
 | 
			
		||||
            throw new DBus.DBusError('org.gnome.Shell.ModalDialog.TypeError',
 | 
			
		||||
                                     "Unknown dialog type requested");
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < inhibitorObjectPaths.length; i++) {
 | 
			
		||||
            let inhibitor = new GnomeSession.Inhibitor(inhibitorObjectPaths[i], Lang.bind(this, function(proxy, error) {
 | 
			
		||||
                this._onInhibitorLoaded(proxy);
 | 
			
		||||
            }));
 | 
			
		||||
            let inhibitor = new GnomeSession.Inhibitor(inhibitorObjectPaths[i]);
 | 
			
		||||
 | 
			
		||||
            inhibitor.connect('is-loaded',
 | 
			
		||||
                              Lang.bind(this, function() {
 | 
			
		||||
                                  this._onInhibitorLoaded(inhibitor);
 | 
			
		||||
                              }));
 | 
			
		||||
            this._inhibitors.push(inhibitor);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._updateButtons();
 | 
			
		||||
 | 
			
		||||
        if (!this.open(timestamp)) {
 | 
			
		||||
            invocation.return_dbus_error('org.gnome.Shell.ModalDialog.GrabError',
 | 
			
		||||
                                         "Cannot grab pointer and keyboard");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (!this.open(timestamp))
 | 
			
		||||
            throw new DBus.DBusError('org.gnome.Shell.ModalDialog.GrabError',
 | 
			
		||||
                                     "Cannot grab pointer and keyboard");
 | 
			
		||||
 | 
			
		||||
        this._updateContent();
 | 
			
		||||
 | 
			
		||||
        let signalId = this.connect('opened',
 | 
			
		||||
                                    Lang.bind(this, function() {
 | 
			
		||||
                                        invocation.return_value(null);
 | 
			
		||||
                                        callback();
 | 
			
		||||
                                        this.disconnect(signalId);
 | 
			
		||||
                                    }));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
DBus.conformExport(EndSessionDialog.prototype, EndSessionDialogIface);
 | 
			
		||||
 
 | 
			
		||||
@@ -39,23 +39,20 @@ function _patchContainerClass(containerClass) {
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _makeLoggingFunc(func) {
 | 
			
		||||
    return function() {
 | 
			
		||||
        return func([].join.call(arguments, ', '));
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function init() {
 | 
			
		||||
    // Add some bindings to the global JS namespace; (gjs keeps the web
 | 
			
		||||
    // browser convention of having that namespace be called 'window'.)
 | 
			
		||||
    window.global = Shell.Global.get();
 | 
			
		||||
 | 
			
		||||
    window.log = _makeLoggingFunc(window.log);
 | 
			
		||||
 | 
			
		||||
    window._ = Gettext.gettext;
 | 
			
		||||
    window.C_ = Gettext.pgettext;
 | 
			
		||||
    window.ngettext = Gettext.ngettext;
 | 
			
		||||
 | 
			
		||||
    // Set the default direction for St widgets (this needs to be done before any use of St)
 | 
			
		||||
    if (Gtk.Widget.get_default_direction() == Gtk.TextDirection.RTL) {
 | 
			
		||||
        St.Widget.set_default_direction(St.TextDirection.RTL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Miscellaneous monkeypatching
 | 
			
		||||
    _patchContainerClass(St.BoxLayout);
 | 
			
		||||
    _patchContainerClass(St.Table);
 | 
			
		||||
@@ -67,14 +64,10 @@ function init() {
 | 
			
		||||
    let origToString = Object.prototype.toString;
 | 
			
		||||
    Object.prototype.toString = function() {
 | 
			
		||||
        let base = origToString.call(this);
 | 
			
		||||
        try {
 | 
			
		||||
            if ('actor' in this && this.actor instanceof Clutter.Actor)
 | 
			
		||||
                return base.replace(/\]$/, ' delegate for ' + this.actor.toString().substring(1));
 | 
			
		||||
            else
 | 
			
		||||
                return base;
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
        if ('actor' in this && this.actor instanceof Clutter.Actor)
 | 
			
		||||
            return base.replace(/\]$/, ' delegate for ' + this.actor.toString().substring(1));
 | 
			
		||||
        else
 | 
			
		||||
            return base;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=508783
 | 
			
		||||
@@ -90,7 +83,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,18 @@
 | 
			
		||||
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 ExtensionUtils = imports.misc.extensionUtils;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Config = imports.misc.config;
 | 
			
		||||
const FileUtils = imports.misc.fileUtils;
 | 
			
		||||
const ModalDialog = imports.ui.modalDialog;
 | 
			
		||||
 | 
			
		||||
const API_VERSION = 1;
 | 
			
		||||
 | 
			
		||||
const ExtensionState = {
 | 
			
		||||
    ENABLED: 1,
 | 
			
		||||
@@ -16,17 +22,50 @@ const ExtensionState = {
 | 
			
		||||
    ERROR: 3,
 | 
			
		||||
    OUT_OF_DATE: 4,
 | 
			
		||||
    DOWNLOADING: 5,
 | 
			
		||||
    INITIALIZED: 6,
 | 
			
		||||
 | 
			
		||||
    // Used as an error state for operations on unknown extensions,
 | 
			
		||||
    // should never be in a real extensionMeta object.
 | 
			
		||||
    UNINSTALLED: 99
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ExtensionType = {
 | 
			
		||||
    SYSTEM: 1,
 | 
			
		||||
    PER_USER: 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
 | 
			
		||||
// Maps uuid -> metadata object
 | 
			
		||||
const extensionMeta = {};
 | 
			
		||||
// Maps uuid -> importer object (extension directory tree)
 | 
			
		||||
const extensions = {};
 | 
			
		||||
// Maps uuid -> extension state object (returned from init())
 | 
			
		||||
const extensionStateObjs = {};
 | 
			
		||||
// Arrays of uuids
 | 
			
		||||
var enabledExtensions;
 | 
			
		||||
// Contains the order that extensions were enabled in.
 | 
			
		||||
const extensionOrder = [];
 | 
			
		||||
// GFile for user extensions
 | 
			
		||||
var userExtensionsDir = null;
 | 
			
		||||
 | 
			
		||||
// We don't really have a class to add signals on. So, create
 | 
			
		||||
// a simple dummy object, add the signal methods, and export those
 | 
			
		||||
@@ -37,202 +76,330 @@ Signals.addSignalMethods(_signals);
 | 
			
		||||
const connect = Lang.bind(_signals, _signals.connect);
 | 
			
		||||
const disconnect = Lang.bind(_signals, _signals.disconnect);
 | 
			
		||||
 | 
			
		||||
// UUID => Array of error messages
 | 
			
		||||
var errors = {};
 | 
			
		||||
 | 
			
		||||
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
 | 
			
		||||
 | 
			
		||||
var initted = false;
 | 
			
		||||
var enabled;
 | 
			
		||||
 | 
			
		||||
function disableExtension(uuid) {
 | 
			
		||||
    let extension = ExtensionUtils.extensions[uuid];
 | 
			
		||||
    if (!extension)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (extension.state != ExtensionState.ENABLED)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    // "Rebase" the extension order by disabling and then enabling extensions
 | 
			
		||||
    // in order to help prevent conflicts.
 | 
			
		||||
 | 
			
		||||
    // Example:
 | 
			
		||||
    //   order = [A, B, C, D, E]
 | 
			
		||||
    //   user disables C
 | 
			
		||||
    //   this should: disable E, disable D, disable C, enable D, enable E
 | 
			
		||||
 | 
			
		||||
    let orderIdx = extensionOrder.indexOf(uuid);
 | 
			
		||||
    let order = extensionOrder.slice(orderIdx + 1);
 | 
			
		||||
    let orderReversed = order.slice().reverse();
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < orderReversed.length; i++) {
 | 
			
		||||
        let uuid = orderReversed[i];
 | 
			
		||||
        try {
 | 
			
		||||
            ExtensionUtils.extensions[uuid].stateObj.disable();
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            logExtensionError(uuid, e);
 | 
			
		||||
        }
 | 
			
		||||
/**
 | 
			
		||||
 * versionCheck:
 | 
			
		||||
 * @required: an array of versions we're compatible with
 | 
			
		||||
 * @current: the version we have
 | 
			
		||||
 *
 | 
			
		||||
 * Check if a component is compatible for an extension.
 | 
			
		||||
 * @required is an array, and at least one version must match.
 | 
			
		||||
 * @current must be in the format <major>.<minor>.<point>.<micro>
 | 
			
		||||
 * <micro> is always ignored
 | 
			
		||||
 * <point> is ignored if <minor> is even (so you can target the
 | 
			
		||||
 * whole stable release)
 | 
			
		||||
 * <minor> and <major> must match
 | 
			
		||||
 * Each target version must be at least <major> and <minor>
 | 
			
		||||
 */
 | 
			
		||||
function versionCheck(required, current) {
 | 
			
		||||
    let currentArray = current.split('.');
 | 
			
		||||
    let major = currentArray[0];
 | 
			
		||||
    let minor = currentArray[1];
 | 
			
		||||
    let point = currentArray[2];
 | 
			
		||||
    for (let i = 0; i < required.length; i++) {
 | 
			
		||||
        let requiredArray = required[i].split('.');
 | 
			
		||||
        if (requiredArray[0] == major &&
 | 
			
		||||
            requiredArray[1] == minor &&
 | 
			
		||||
            (requiredArray[2] == point ||
 | 
			
		||||
             (requiredArray[2] == undefined && parseInt(minor) % 2 == 0)))
 | 
			
		||||
            return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (extension.stylesheet) {
 | 
			
		||||
        let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
 | 
			
		||||
        theme.unload_stylesheet(extension.stylesheet.get_path());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    extensionOrder.splice(orderIdx, 1);
 | 
			
		||||
 | 
			
		||||
    extension.state = ExtensionState.DISABLED;
 | 
			
		||||
    _signals.emit('extension-state-changed', extension);
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function enableExtension(uuid) {
 | 
			
		||||
    let extension = ExtensionUtils.extensions[uuid];
 | 
			
		||||
    if (!extension)
 | 
			
		||||
        return;
 | 
			
		||||
function installExtensionFromUUID(uuid, version_tag) {
 | 
			
		||||
    let params = { uuid: uuid,
 | 
			
		||||
                   version_tag: version_tag,
 | 
			
		||||
                   shell_version: Config.PACKAGE_VERSION,
 | 
			
		||||
                   api_version: API_VERSION.toString() };
 | 
			
		||||
 | 
			
		||||
    if (extension.state == ExtensionState.INITIALIZED)
 | 
			
		||||
        initExtension(uuid);
 | 
			
		||||
    let message = Soup.form_request_new_from_hash('GET', REPOSITORY_URL_INFO, params);
 | 
			
		||||
 | 
			
		||||
    if (extension.state != ExtensionState.DISABLED)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    extension.stateObj.enable();
 | 
			
		||||
 | 
			
		||||
    extension.state = ExtensionState.ENABLED;
 | 
			
		||||
    _signals.emit('extension-state-changed', extension);
 | 
			
		||||
    _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 logExtensionError(uuid, error) {
 | 
			
		||||
    let extension = ExtensionUtils.extensions[uuid];
 | 
			
		||||
    if (!extension)
 | 
			
		||||
        return;
 | 
			
		||||
function uninstallExtensionFromUUID(uuid) {
 | 
			
		||||
    let meta = extensionMeta[uuid];
 | 
			
		||||
    if (!meta)
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    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));
 | 
			
		||||
    _signals.emit('extension-state-changed', { uuid: uuid,
 | 
			
		||||
                                               error: message,
 | 
			
		||||
                                               state: extension.state });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function loadExtension(extension) {
 | 
			
		||||
    // 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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
 | 
			
		||||
    if (enabled) {
 | 
			
		||||
        initExtension(extension.uuid);
 | 
			
		||||
        if (extension.state == ExtensionState.DISABLED)
 | 
			
		||||
            enableExtension(extension.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);
 | 
			
		||||
    disableExtension(uuid);
 | 
			
		||||
 | 
			
		||||
    extension.state = ExtensionState.UNINSTALLED;
 | 
			
		||||
    _signals.emit('extension-state-changed', extension);
 | 
			
		||||
    // Don't try to uninstall system extensions
 | 
			
		||||
    if (meta.type != ExtensionType.PER_USER)
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    meta.state = ExtensionState.UNINSTALLED;
 | 
			
		||||
    _signals.emit('extension-state-changed', meta);
 | 
			
		||||
 | 
			
		||||
    delete extensionMeta[uuid];
 | 
			
		||||
 | 
			
		||||
    // Importers are marked as PERMANENT, so we can't do this.
 | 
			
		||||
    // delete extensions[uuid];
 | 
			
		||||
    extensions[uuid] = undefined;
 | 
			
		||||
 | 
			
		||||
    delete extensionStateObjs[uuid];
 | 
			
		||||
    delete errors[uuid];
 | 
			
		||||
 | 
			
		||||
    FileUtils.recursivelyDeleteDir(Gio.file_new_for_path(meta.path));
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
function gotExtensionZipFile(session, message, uuid) {
 | 
			
		||||
    if (message.status_code != Soup.KnownStatusCode.OK) {
 | 
			
		||||
        logExtensionError(uuid, 'downloading extension: ' + message.status_code);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Then unload the old extension.
 | 
			
		||||
    unloadExtension(oldExtension);
 | 
			
		||||
    // 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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Now, recreate the extension and load it.
 | 
			
		||||
    let newExtension = ExtensionUtils.createExtensionObject(uuid, dir, type);
 | 
			
		||||
    loadExtension(newExtension);
 | 
			
		||||
    let stream = new Gio.UnixOutputStream({ fd: fd });
 | 
			
		||||
    let dir = 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, true, ExtensionType.PER_USER);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function initExtension(uuid) {
 | 
			
		||||
    let extension = ExtensionUtils.extensions[uuid];
 | 
			
		||||
    let dir = extension.dir;
 | 
			
		||||
function disableExtension(uuid) {
 | 
			
		||||
    let meta = extensionMeta[uuid];
 | 
			
		||||
    if (!meta)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (!extension)
 | 
			
		||||
        throw new Error("Extension was not properly created. Call loadExtension first");
 | 
			
		||||
    if (meta.state != ExtensionState.ENABLED)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    let extensionState = extensionStateObjs[uuid];
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        extensionState.disable();
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
        logExtensionError(uuid, e.toString());
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    meta.state = ExtensionState.DISABLED;
 | 
			
		||||
    _signals.emit('extension-state-changed', meta);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function enableExtension(uuid) {
 | 
			
		||||
    let meta = extensionMeta[uuid];
 | 
			
		||||
    if (!meta)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (meta.state != ExtensionState.DISABLED)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    let extensionState = extensionStateObjs[uuid];
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        extensionState.enable();
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
        logExtensionError(uuid, e.toString());
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    meta.state = ExtensionState.ENABLED;
 | 
			
		||||
    _signals.emit('extension-state-changed', meta);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function logExtensionError(uuid, message, state) {
 | 
			
		||||
    if (!errors[uuid]) errors[uuid] = [];
 | 
			
		||||
    errors[uuid].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: state });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function loadExtension(dir, enabled, type) {
 | 
			
		||||
    let info;
 | 
			
		||||
    let uuid = dir.get_basename();
 | 
			
		||||
 | 
			
		||||
    let metadataFile = dir.get_child('metadata.json');
 | 
			
		||||
    if (!metadataFile.query_exists(null)) {
 | 
			
		||||
        logExtensionError(uuid, 'Missing metadata.json');
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let metadataContents;
 | 
			
		||||
    try {
 | 
			
		||||
        metadataContents = Shell.get_file_contents_utf8_sync(metadataFile.get_path());
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        logExtensionError(uuid, 'Failed to load metadata.json: ' + e);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    let meta;
 | 
			
		||||
    try {
 | 
			
		||||
        meta = JSON.parse(metadataContents);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        logExtensionError(uuid, 'Failed to parse metadata.json: ' + e);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
 | 
			
		||||
    for (let i = 0; i < requiredProperties.length; i++) {
 | 
			
		||||
        let prop = requiredProperties[i];
 | 
			
		||||
        if (!meta[prop]) {
 | 
			
		||||
            logExtensionError(uuid, 'missing "' + prop + '" property in metadata.json');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (extensions[uuid] != undefined) {
 | 
			
		||||
        logExtensionError(uuid, 'extension already loaded');
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Encourage people to add this
 | 
			
		||||
    if (!meta['url']) {
 | 
			
		||||
        global.log('Warning: Missing "url" property in metadata.json');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (uuid != meta.uuid) {
 | 
			
		||||
        logExtensionError(uuid, 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
 | 
			
		||||
        (meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
 | 
			
		||||
        logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version');
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    extensionMeta[uuid] = meta;
 | 
			
		||||
    meta.type = type;
 | 
			
		||||
    meta.path = dir.get_path();
 | 
			
		||||
    meta.error = '';
 | 
			
		||||
 | 
			
		||||
    // Default to error, we set success as the last step
 | 
			
		||||
    meta.state = ExtensionState.ERROR;
 | 
			
		||||
 | 
			
		||||
    if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
 | 
			
		||||
        (meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
 | 
			
		||||
        logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version', ExtensionState.OUT_OF_DATE);
 | 
			
		||||
        meta.state = ExtensionState.OUT_OF_DATE;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 {
 | 
			
		||||
        global.add_extension_importer('imports.ui.extensionSystem.extensions', meta.uuid, dir.get_path());
 | 
			
		||||
        extensionModule = extensions[meta.uuid].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) {
 | 
			
		||||
        extensionState = extensionModule.init(extension);
 | 
			
		||||
    try {
 | 
			
		||||
        extensionState = extensionModule.init(meta);
 | 
			
		||||
    } 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;
 | 
			
		||||
    extensionStateObjs[uuid] = extensionState;
 | 
			
		||||
 | 
			
		||||
    extension.state = ExtensionState.DISABLED;
 | 
			
		||||
    _signals.emit('extension-loaded', uuid);
 | 
			
		||||
    if (!extensionState.enable) {
 | 
			
		||||
        logExtensionError(uuid, 'missing \'enable\' function');
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (!extensionState.disable) {
 | 
			
		||||
        logExtensionError(uuid, 'missing \'disable\' function');
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    meta.state = ExtensionState.DISABLED;
 | 
			
		||||
 | 
			
		||||
    if (enabled)
 | 
			
		||||
        enableExtension(uuid);
 | 
			
		||||
 | 
			
		||||
    _signals.emit('extension-loaded', meta.uuid);
 | 
			
		||||
    _signals.emit('extension-state-changed', meta);
 | 
			
		||||
    global.log('Loaded extension ' + meta.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 +407,125 @@ 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() {
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    finder.scanExtensions();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function enableAllExtensions() {
 | 
			
		||||
    if (enabled)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (!initted) {
 | 
			
		||||
        _loadExtensions();
 | 
			
		||||
        initted = true;
 | 
			
		||||
    } else {
 | 
			
		||||
        enabledExtensions.forEach(function(uuid) {
 | 
			
		||||
            enableExtension(uuid);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    enabled = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function disableAllExtensions() {
 | 
			
		||||
    if (!enabled)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (initted) {
 | 
			
		||||
        enabledExtensions.forEach(function(uuid) {
 | 
			
		||||
            disableExtension(uuid);
 | 
			
		||||
        });
 | 
			
		||||
function _loadExtensionsIn(dir, type) {
 | 
			
		||||
    let fileEnum;
 | 
			
		||||
    let file, info;
 | 
			
		||||
    try {
 | 
			
		||||
        fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        global.logError('' + e);
 | 
			
		||||
       return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    enabled = false;
 | 
			
		||||
    while ((info = fileEnum.next_file(null)) != null) {
 | 
			
		||||
        let fileType = info.get_file_type();
 | 
			
		||||
        if (fileType != Gio.FileType.DIRECTORY)
 | 
			
		||||
            continue;
 | 
			
		||||
        let name = info.get_name();
 | 
			
		||||
        let child = dir.get_child(name);
 | 
			
		||||
        let enabled = enabledExtensions.indexOf(name) != -1;
 | 
			
		||||
        loadExtension(child, enabled, type);
 | 
			
		||||
    }
 | 
			
		||||
    fileEnum.close(null);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _sessionUpdated() {
 | 
			
		||||
    if (Main.sessionMode.allowExtensions)
 | 
			
		||||
        enableAllExtensions();
 | 
			
		||||
    else
 | 
			
		||||
        disableAllExtensions();
 | 
			
		||||
function loadExtensions() {
 | 
			
		||||
    let systemDataDirs = GLib.get_system_data_dirs();
 | 
			
		||||
    for (let i = 0; i < systemDataDirs.length; i++) {
 | 
			
		||||
        let dirPath = systemDataDirs[i] + '/gnome-shell/extensions';
 | 
			
		||||
        let dir = Gio.file_new_for_path(dirPath);
 | 
			
		||||
        if (dir.query_exists(null))
 | 
			
		||||
            _loadExtensionsIn(dir, ExtensionType.SYSTEM);
 | 
			
		||||
    }
 | 
			
		||||
    _loadExtensionsIn(userExtensionsDir, ExtensionType.PER_USER);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function init() {
 | 
			
		||||
    Main.sessionMode.connect('updated', _sessionUpdated);
 | 
			
		||||
    _sessionUpdated();
 | 
			
		||||
function InstallExtensionDialog(uuid, version_tag, name) {
 | 
			
		||||
    this._init(uuid, version_tag, name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
InstallExtensionDialog.prototype = {
 | 
			
		||||
    __proto__: ModalDialog.ModalDialog.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(uuid, version_tag, name) {
 | 
			
		||||
        ModalDialog.ModalDialog.prototype._init.call(this, { 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 meta = { uuid: this._uuid,
 | 
			
		||||
                     state: ExtensionState.DOWNLOADING,
 | 
			
		||||
                     error: '' };
 | 
			
		||||
 | 
			
		||||
        extensionMeta[this._uuid] = meta;
 | 
			
		||||
 | 
			
		||||
        _signals.emit('extension-state-changed', meta);
 | 
			
		||||
 | 
			
		||||
        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());
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,45 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
 | 
			
		||||
const Lightbox = imports.ui.lightbox;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const FLASHSPOT_ANIMATION_TIME = 0.25; // seconds
 | 
			
		||||
 | 
			
		||||
const Flashspot = new Lang.Class({
 | 
			
		||||
    Name: 'Flashspot',
 | 
			
		||||
    Extends: Lightbox.Lightbox,
 | 
			
		||||
 | 
			
		||||
    _init: function(area) {
 | 
			
		||||
        this.parent(Main.uiGroup, { inhibitEvents: true,
 | 
			
		||||
                                    width: area.width,
 | 
			
		||||
                                    height: area.height });
 | 
			
		||||
 | 
			
		||||
        this.actor.style_class = 'flashspot';
 | 
			
		||||
        this.actor.set_position(area.x, area.y);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    fire: function() {
 | 
			
		||||
        this.actor.opacity = 0;
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           time: FLASHSPOT_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'linear',
 | 
			
		||||
                           onComplete: Lang.bind(this, this._onFireShowComplete)
 | 
			
		||||
                         });
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onFireShowComplete: function() {
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time: FLASHSPOT_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'linear',
 | 
			
		||||
                           onComplete: Lang.bind(this, function() {
 | 
			
		||||
                               this.destroy();
 | 
			
		||||
                           })
 | 
			
		||||
                         });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -1,360 +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
 | 
			
		||||
// @params: optional parameters to pass to Main.pushModal()
 | 
			
		||||
//
 | 
			
		||||
// 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, params) {
 | 
			
		||||
        this._owner = owner;
 | 
			
		||||
        this._modalParams = params;
 | 
			
		||||
 | 
			
		||||
        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, this._modalParams))
 | 
			
		||||
                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());
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -10,9 +10,11 @@ const Params = imports.misc.params;
 | 
			
		||||
const ICON_SIZE = 48;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const BaseIcon = new Lang.Class({
 | 
			
		||||
    Name: 'BaseIcon',
 | 
			
		||||
function BaseIcon(label, createIcon) {
 | 
			
		||||
    this._init(label, createIcon);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BaseIcon.prototype = {
 | 
			
		||||
    _init : function(label, params) {
 | 
			
		||||
        params = Params.parse(params, { createIcon: null,
 | 
			
		||||
                                        setSizeManually: false,
 | 
			
		||||
@@ -35,8 +37,7 @@ const BaseIcon = new Lang.Class({
 | 
			
		||||
        this.actor.set_child(box);
 | 
			
		||||
 | 
			
		||||
        this.iconSize = ICON_SIZE;
 | 
			
		||||
        this._iconBin = new St.Bin({ x_align: St.Align.MIDDLE,
 | 
			
		||||
                                     y_align: St.Align.MIDDLE });
 | 
			
		||||
        this._iconBin = new St.Bin();
 | 
			
		||||
 | 
			
		||||
        box.add_actor(this._iconBin);
 | 
			
		||||
 | 
			
		||||
@@ -126,12 +127,12 @@ const BaseIcon = new Lang.Class({
 | 
			
		||||
        this.iconSize = size;
 | 
			
		||||
        this.icon = this.createIcon(this.iconSize);
 | 
			
		||||
 | 
			
		||||
        this._iconBin.child = this.icon;
 | 
			
		||||
 | 
			
		||||
        // The icon returned by createIcon() might actually be smaller than
 | 
			
		||||
        // the requested icon size (for instance StTextureCache does this
 | 
			
		||||
        // for fallback icons), so set the size explicitly.
 | 
			
		||||
        this._iconBin.set_size(this.iconSize, this.iconSize);
 | 
			
		||||
        this.icon.set_size(this.iconSize, this.iconSize);
 | 
			
		||||
 | 
			
		||||
        this._iconBin.child = this.icon;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onStyleChanged: function() {
 | 
			
		||||
@@ -146,13 +147,20 @@ const BaseIcon = new Lang.Class({
 | 
			
		||||
            size = found ? len : ICON_SIZE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // don't create icons unnecessarily
 | 
			
		||||
        if (size == this.iconSize &&
 | 
			
		||||
            this._iconBin.child)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._createIconTexture(size);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const IconGrid = new Lang.Class({
 | 
			
		||||
    Name: 'IconGrid',
 | 
			
		||||
function IconGrid(params) {
 | 
			
		||||
    this._init(params);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IconGrid.prototype = {
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        params = Params.parse(params, { rowLimit: null,
 | 
			
		||||
                                        columnLimit: null,
 | 
			
		||||
@@ -165,7 +173,7 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
                                        vertical: true });
 | 
			
		||||
        // Pulled from CSS, but hardcode some defaults here
 | 
			
		||||
        this._spacing = 0;
 | 
			
		||||
        this._hItemSize = this._vItemSize = ICON_SIZE;
 | 
			
		||||
        this._item_size = ICON_SIZE;
 | 
			
		||||
        this._grid = new Shell.GenericContainer();
 | 
			
		||||
        this.actor.add(this._grid, { expand: true, y_align: St.Align.START });
 | 
			
		||||
        this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
 | 
			
		||||
@@ -184,8 +192,8 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
        // Kind of a lie, but not really an issue right now.  If
 | 
			
		||||
        // we wanted to support some sort of hidden/overflow that would
 | 
			
		||||
        // need higher level design
 | 
			
		||||
        alloc.min_size = this._hItemSize;
 | 
			
		||||
        alloc.natural_size = nColumns * this._hItemSize + totalSpacing;
 | 
			
		||||
        alloc.min_size = this._item_size;
 | 
			
		||||
        alloc.natural_size = nColumns * this._item_size + totalSpacing;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getVisibleChildren: function() {
 | 
			
		||||
@@ -207,7 +215,7 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
        if (this._rowLimit)
 | 
			
		||||
            nRows = Math.min(nRows, this._rowLimit);
 | 
			
		||||
        let totalSpacing = Math.max(0, nRows - 1) * this._spacing;
 | 
			
		||||
        let height = nRows * this._vItemSize + totalSpacing;
 | 
			
		||||
        let height = nRows * this._item_size + totalSpacing;
 | 
			
		||||
        alloc.min_size = height;
 | 
			
		||||
        alloc.natural_size = height;
 | 
			
		||||
    },
 | 
			
		||||
@@ -240,13 +248,13 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
                = children[i].get_preferred_size();
 | 
			
		||||
 | 
			
		||||
            /* Center the item in its allocation horizontally */
 | 
			
		||||
            let width = Math.min(this._hItemSize, childNaturalWidth);
 | 
			
		||||
            let width = Math.min(this._item_size, childNaturalWidth);
 | 
			
		||||
            let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
 | 
			
		||||
            let height = Math.min(this._vItemSize, childNaturalHeight);
 | 
			
		||||
            let height = Math.min(this._item_size, childNaturalHeight);
 | 
			
		||||
            let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
 | 
			
		||||
 | 
			
		||||
            let childBox = new Clutter.ActorBox();
 | 
			
		||||
            if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
 | 
			
		||||
            if (St.Widget.get_default_direction() == St.TextDirection.RTL) {
 | 
			
		||||
                let _x = box.x2 - (x + width);
 | 
			
		||||
                childBox.x1 = Math.floor(_x - childXSpacing);
 | 
			
		||||
            } else {
 | 
			
		||||
@@ -270,10 +278,10 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (columnIndex == 0) {
 | 
			
		||||
                y += this._vItemSize + this._spacing;
 | 
			
		||||
                y += this._item_size + this._spacing;
 | 
			
		||||
                x = box.x1 + leftPadding;
 | 
			
		||||
            } else {
 | 
			
		||||
                x += this._hItemSize + this._spacing;
 | 
			
		||||
                x += this._item_size + this._spacing;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -282,16 +290,12 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
        return this._computeLayout(rowWidth)[0];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getRowLimit: function() {
 | 
			
		||||
        return this._rowLimit;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _computeLayout: function (forWidth) {
 | 
			
		||||
        let nColumns = 0;
 | 
			
		||||
        let usedWidth = 0;
 | 
			
		||||
        while ((this._colLimit == null || nColumns < this._colLimit) &&
 | 
			
		||||
               (usedWidth + this._hItemSize <= forWidth)) {
 | 
			
		||||
            usedWidth += this._hItemSize + this._spacing;
 | 
			
		||||
               (usedWidth + this._item_size <= forWidth)) {
 | 
			
		||||
            usedWidth += this._item_size + this._spacing;
 | 
			
		||||
            nColumns += 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -304,27 +308,25 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
    _onStyleChanged: function() {
 | 
			
		||||
        let themeNode = this.actor.get_theme_node();
 | 
			
		||||
        this._spacing = themeNode.get_length('spacing');
 | 
			
		||||
        this._hItemSize = themeNode.get_length('-shell-grid-horizontal-item-size') || ICON_SIZE;
 | 
			
		||||
        this._vItemSize = themeNode.get_length('-shell-grid-vertical-item-size') || ICON_SIZE;
 | 
			
		||||
        this._item_size = themeNode.get_length('-shell-grid-item-size');
 | 
			
		||||
        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();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -20,31 +20,53 @@ const KEYBOARD_TYPE = 'keyboard-type';
 | 
			
		||||
const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
 | 
			
		||||
const SHOW_KEYBOARD = 'screen-keyboard-enabled';
 | 
			
		||||
 | 
			
		||||
const CaribouKeyboardIface = <interface name='org.gnome.Caribou.Keyboard'>
 | 
			
		||||
<method name='Show'>
 | 
			
		||||
    <arg type='u' direction='in' />
 | 
			
		||||
</method>
 | 
			
		||||
<method name='Hide'>
 | 
			
		||||
    <arg type='u' direction='in' />
 | 
			
		||||
</method>
 | 
			
		||||
<method name='SetCursorLocation'>
 | 
			
		||||
    <arg type='i' direction='in' />
 | 
			
		||||
    <arg type='i' direction='in' />
 | 
			
		||||
    <arg type='i' direction='in' />
 | 
			
		||||
    <arg type='i' direction='in' />
 | 
			
		||||
</method>
 | 
			
		||||
<method name='SetEntryLocation'>
 | 
			
		||||
    <arg type='i' direction='in' />
 | 
			
		||||
    <arg type='i' direction='in' />
 | 
			
		||||
    <arg type='i' direction='in' />
 | 
			
		||||
    <arg type='i' direction='in' />
 | 
			
		||||
</method>
 | 
			
		||||
<property name='Name' access='read' type='s' />
 | 
			
		||||
</interface>;
 | 
			
		||||
// Key constants taken from Antler
 | 
			
		||||
// FIXME: ought to be moved into libcaribou
 | 
			
		||||
const PRETTY_KEYS = {
 | 
			
		||||
    'BackSpace': '\u232b',
 | 
			
		||||
    'space': ' ',
 | 
			
		||||
    'Return': '\u23ce',
 | 
			
		||||
    'Caribou_Prefs': '\u2328',
 | 
			
		||||
    'Caribou_ShiftUp': '\u2b06',
 | 
			
		||||
    'Caribou_ShiftDown': '\u2b07',
 | 
			
		||||
    'Caribou_Emoticons': '\u263a',
 | 
			
		||||
    'Caribou_Symbols': '123',
 | 
			
		||||
    'Caribou_Symbols_More': '{#*',
 | 
			
		||||
    'Caribou_Alpha': 'Abc',
 | 
			
		||||
    'Tab': 'Tab',
 | 
			
		||||
    'Escape': 'Esc',
 | 
			
		||||
    'Control_L': 'Ctrl',
 | 
			
		||||
    'Alt_L': 'Alt'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Key = new Lang.Class({
 | 
			
		||||
    Name: 'Key',
 | 
			
		||||
const CaribouKeyboardIface = {
 | 
			
		||||
    name: 'org.gnome.Caribou.Keyboard',
 | 
			
		||||
    methods:    [ { name: 'Show',
 | 
			
		||||
                    inSignature: 'u',
 | 
			
		||||
                    outSignature: ''
 | 
			
		||||
                  },
 | 
			
		||||
                  { name: 'Hide',
 | 
			
		||||
                    inSignature: 'u',
 | 
			
		||||
                    outSignature: ''
 | 
			
		||||
                  },
 | 
			
		||||
                  { name: 'SetCursorLocation',
 | 
			
		||||
                    inSignature: 'iiii',
 | 
			
		||||
                    outSignature: ''
 | 
			
		||||
                  },
 | 
			
		||||
                  { name: 'SetEntryLocation',
 | 
			
		||||
                    inSignature: 'iiii',
 | 
			
		||||
                    outSignature: ''
 | 
			
		||||
                  } ],
 | 
			
		||||
    properties: [ { name: 'Name',
 | 
			
		||||
                    signature: 's',
 | 
			
		||||
                    access: 'read' } ]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function Key() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Key.prototype = {
 | 
			
		||||
    _init : function(key) {
 | 
			
		||||
        this._key = key;
 | 
			
		||||
 | 
			
		||||
@@ -74,12 +96,22 @@ 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 });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _makeKey: function () {
 | 
			
		||||
        let label = GLib.markup_escape_text(this._key.label, -1);
 | 
			
		||||
        let label = this._key.name;
 | 
			
		||||
 | 
			
		||||
        if (label.length > 1) {
 | 
			
		||||
            let pretty = PRETTY_KEYS[label];
 | 
			
		||||
            if (pretty)
 | 
			
		||||
                label = pretty;
 | 
			
		||||
            else
 | 
			
		||||
                label = this._getUnichar(this._key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        label = GLib.markup_escape_text(label, -1);
 | 
			
		||||
        let button = new St.Button ({ label: label,
 | 
			
		||||
                                      style_class: 'keyboard-key' });
 | 
			
		||||
 | 
			
		||||
@@ -146,7 +178,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);
 | 
			
		||||
@@ -157,24 +189,22 @@ const Key = new Lang.Class({
 | 
			
		||||
        } else {
 | 
			
		||||
            if (this._grabbed)
 | 
			
		||||
                this._ungrab();
 | 
			
		||||
            this._boxPointer.hide(BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
            this._boxPointer.hide(true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Keyboard = new Lang.Class({
 | 
			
		||||
    // HACK: we can't set Name, because it collides with Name dbus property
 | 
			
		||||
    // Name: 'Keyboard',
 | 
			
		||||
function Keyboard() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Keyboard.prototype = {
 | 
			
		||||
    _init: function () {
 | 
			
		||||
        this._impl = Gio.DBusExportedObject.wrapJSObject(CaribouKeyboardIface, this);
 | 
			
		||||
        this._impl.export(Gio.DBus.session, '/org/gnome/Caribou/Keyboard');
 | 
			
		||||
        DBus.session.exportObject('/org/gnome/Caribou/Keyboard', this);
 | 
			
		||||
 | 
			
		||||
        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 });
 | 
			
		||||
@@ -242,11 +272,6 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._addKeys();
 | 
			
		||||
 | 
			
		||||
        // Keys should be layout according to the group, not the
 | 
			
		||||
        // locale; as Caribou already provides the expected layout,
 | 
			
		||||
        // this means enforcing LTR for all locales.
 | 
			
		||||
        this.actor.text_direction = Clutter.TextDirection.LTR;
 | 
			
		||||
 | 
			
		||||
        this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged));
 | 
			
		||||
        this._focusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
 | 
			
		||||
 | 
			
		||||
@@ -261,18 +286,10 @@ 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)
 | 
			
		||||
        if (focus && (focus._extended_keys || (focus._key && focus._key.extended_key)))
 | 
			
		||||
            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)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let time = global.get_current_time();
 | 
			
		||||
        let time = global.current_event_time();
 | 
			
		||||
        if (focus instanceof Clutter.Text)
 | 
			
		||||
            this.Show(time);
 | 
			
		||||
        else
 | 
			
		||||
@@ -320,13 +337,6 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
            trayButton.reactive = true;
 | 
			
		||||
            trayButton.remove_style_pseudo_class('grayed');
 | 
			
		||||
        }));
 | 
			
		||||
        Main.sessionMode.connect('updated', Lang.bind(this, function() {
 | 
			
		||||
            trayButton.reactive = !Main.sessionMode.isLocked;
 | 
			
		||||
            if (Main.sessionMode.isLocked)
 | 
			
		||||
                trayButton.add_style_pseudo_class('grayed');
 | 
			
		||||
            else
 | 
			
		||||
                trayButton.remove_style_pseudo_class('grayed');
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        return trayButton;
 | 
			
		||||
    },
 | 
			
		||||
@@ -456,12 +466,6 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    shouldTakeEvent: function(event) {
 | 
			
		||||
        let actor = event.get_source();
 | 
			
		||||
        return Main.layoutManager.keyboardBox.contains(actor) ||
 | 
			
		||||
               actor._extended_keys || actor.extended_key;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function () {
 | 
			
		||||
        this._redraw();
 | 
			
		||||
 | 
			
		||||
@@ -488,30 +492,15 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
            this._moveTemporarily();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // _compareTimestamp:
 | 
			
		||||
    //
 | 
			
		||||
    // Compare two timestamps taking into account
 | 
			
		||||
    // CURRENT_TIME (0)
 | 
			
		||||
    _compareTimestamp: function(one, two) {
 | 
			
		||||
        if (one == two)
 | 
			
		||||
            return 0;
 | 
			
		||||
        if (one == Clutter.CURRENT_TIME)
 | 
			
		||||
            return 1;
 | 
			
		||||
        if (two == Clutter.CURRENT_TIME)
 | 
			
		||||
            return -1;
 | 
			
		||||
        return one - two;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // D-Bus methods
 | 
			
		||||
    Show: function(timestamp) {
 | 
			
		||||
        if (!this._enableKeyboard)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._compareTimestamp(timestamp, this._timestamp) < 0)
 | 
			
		||||
        if (timestamp - this._timestamp < 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (timestamp != Clutter.CURRENT_TIME)
 | 
			
		||||
            this._timestamp = timestamp;
 | 
			
		||||
        this._timestamp = timestamp;
 | 
			
		||||
        this.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -519,11 +508,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();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -544,19 +532,30 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
    get Name() {
 | 
			
		||||
        return 'gnome-shell';
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
DBus.conformExport(Keyboard.prototype, CaribouKeyboardIface);
 | 
			
		||||
 | 
			
		||||
const KeyboardSource = new Lang.Class({
 | 
			
		||||
    Name: 'KeyboardSource',
 | 
			
		||||
    Extends: MessageTray.Source,
 | 
			
		||||
function KeyboardSource() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
KeyboardSource.prototype = {
 | 
			
		||||
    __proto__: MessageTray.Source.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(keyboard) {
 | 
			
		||||
        this._keyboard = keyboard;
 | 
			
		||||
        this.parent(_("Keyboard"), 'input-keyboard-symbolic');
 | 
			
		||||
        this.keepTrayOnSummaryClick = true;
 | 
			
		||||
        MessageTray.Source.prototype._init.call(this, _("Keyboard"));
 | 
			
		||||
 | 
			
		||||
        this._setSummaryIcon(this.createNotificationIcon());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    handleSummaryClick: function() {
 | 
			
		||||
    createNotificationIcon: function() {
 | 
			
		||||
        return new St.Icon({ icon_name: 'input-keyboard',
 | 
			
		||||
                             icon_type: St.IconType.SYMBOLIC,
 | 
			
		||||
                             icon_size: this.ICON_SIZE });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
     handleSummaryClick: function() {
 | 
			
		||||
        let event = Clutter.get_current_event();
 | 
			
		||||
        if (event.type() != Clutter.EventType.BUTTON_RELEASE)
 | 
			
		||||
            return false;
 | 
			
		||||
@@ -568,4 +567,4 @@ const KeyboardSource = new Lang.Class({
 | 
			
		||||
    open: function() {
 | 
			
		||||
        this._keyboard.show();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										285
									
								
								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;
 | 
			
		||||
@@ -9,139 +8,52 @@ const Shell = imports.gi.Shell;
 | 
			
		||||
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 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',
 | 
			
		||||
function LayoutManager() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LayoutManager.prototype = {
 | 
			
		||||
    _init: function () {
 | 
			
		||||
        this._rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
 | 
			
		||||
        this._rtl = (St.Widget.get_default_direction() == St.TextDirection.RTL);
 | 
			
		||||
        this.monitors = [];
 | 
			
		||||
        this.primaryMonitor = null;
 | 
			
		||||
        this.primaryIndex = -1;
 | 
			
		||||
        this._hotCorners = [];
 | 
			
		||||
        this._background = 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;
 | 
			
		||||
@@ -234,9 +146,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);
 | 
			
		||||
 | 
			
		||||
@@ -278,6 +187,22 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateTrayBarrier: function() {
 | 
			
		||||
        let monitor = this.bottomMonitor;
 | 
			
		||||
 | 
			
		||||
        if (this._trayBarrier)
 | 
			
		||||
            global.destroy_pointer_barrier(this._trayBarrier);
 | 
			
		||||
 | 
			
		||||
        if (Main.messageTray) {
 | 
			
		||||
            this._trayBarrier =
 | 
			
		||||
                global.create_pointer_barrier(monitor.x + monitor.width, monitor.y + monitor.height - Main.messageTray.actor.height,
 | 
			
		||||
                                              monitor.x + monitor.width, monitor.y + monitor.height,
 | 
			
		||||
                                              4 /* BarrierNegativeX */);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._trayBarrier = 0;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _monitorsChanged: function() {
 | 
			
		||||
        this._updateMonitors();
 | 
			
		||||
        this._updateBoxes();
 | 
			
		||||
@@ -300,47 +225,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._background = Meta.BackgroundActor.new_for_screen(global.screen);
 | 
			
		||||
            if (this._background != null) {
 | 
			
		||||
                Main.uiGroup.add_actor(this._background);
 | 
			
		||||
                Tweener.addTween(this._background,
 | 
			
		||||
                                 { opacity: 0,
 | 
			
		||||
                                   time: PLYMOUTH_TRANSITION_TIME,
 | 
			
		||||
                                   transition: 'linear',
 | 
			
		||||
                                   onComplete: this._fadeBackgroundComplete,
 | 
			
		||||
                                   onCompleteScope: this });
 | 
			
		||||
                plymouthTransitionRunning = true;
 | 
			
		||||
                if (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._fadeBackgroundComplete();
 | 
			
		||||
        return this.primaryIndex;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _fadeBackgroundComplete: function() {
 | 
			
		||||
    get focusMonitor() {
 | 
			
		||||
        return this.monitors[this.focusIndex];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startupAnimation: function() {
 | 
			
		||||
        // Don't animate the strut
 | 
			
		||||
        this._chrome.freezeUpdateRegions();
 | 
			
		||||
 | 
			
		||||
        if (this._background != null) {
 | 
			
		||||
            this._background.destroy();
 | 
			
		||||
            this._background = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.panelBox.anchor_y = this.panelBox.height;
 | 
			
		||||
        Tweener.addTween(this.panelBox,
 | 
			
		||||
                         { anchor_y: 0,
 | 
			
		||||
                           time: STARTUP_ANIMATION_TIME,
 | 
			
		||||
@@ -355,6 +266,7 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    showKeyboard: function () {
 | 
			
		||||
        Main.messageTray.hide();
 | 
			
		||||
        this.keyboardBox.raise_top();
 | 
			
		||||
        Tweener.addTween(this.keyboardBox,
 | 
			
		||||
                         { anchor_y: this.keyboardBox.height,
 | 
			
		||||
@@ -368,8 +280,6 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
                           time: KEYBOARD_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad'
 | 
			
		||||
                         });
 | 
			
		||||
 | 
			
		||||
        this.emit('keyboard-visible-changed', true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showKeyboardComplete: function() {
 | 
			
		||||
@@ -384,6 +294,7 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hideKeyboard: function (immediate) {
 | 
			
		||||
        Main.messageTray.hide();
 | 
			
		||||
        if (this._keyboardHeightNotifyId) {
 | 
			
		||||
            this.keyboardBox.disconnect(this._keyboardHeightNotifyId);
 | 
			
		||||
            this._keyboardHeightNotifyId = 0;
 | 
			
		||||
@@ -400,8 +311,6 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
                           time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad'
 | 
			
		||||
                         });
 | 
			
		||||
 | 
			
		||||
        this.emit('keyboard-visible-changed', false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hideKeyboardComplete: function() {
 | 
			
		||||
@@ -423,10 +332,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);
 | 
			
		||||
    },
 | 
			
		||||
@@ -440,8 +347,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);
 | 
			
		||||
    },
 | 
			
		||||
@@ -465,7 +374,7 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
    findMonitorForActor: function(actor) {
 | 
			
		||||
        return this._chrome.findMonitorForActor(actor);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(LayoutManager.prototype);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -473,9 +382,11 @@ Signals.addSignalMethods(LayoutManager.prototype);
 | 
			
		||||
//
 | 
			
		||||
// This class manages a "hot corner" that can toggle switching to
 | 
			
		||||
// overview.
 | 
			
		||||
const HotCorner = new Lang.Class({
 | 
			
		||||
    Name: 'HotCorner',
 | 
			
		||||
function HotCorner() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HotCorner.prototype = {
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        // We use this flag to mark the case where the user has entered the
 | 
			
		||||
        // hot corner and has not left both the hot corner and a surrounding
 | 
			
		||||
@@ -497,7 +408,7 @@ const HotCorner = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.actor.add_actor(this._corner);
 | 
			
		||||
 | 
			
		||||
        if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
 | 
			
		||||
        if (St.Widget.get_default_direction() == St.TextDirection.RTL) {
 | 
			
		||||
            this._corner.set_position(this.actor.width - this._corner.width, 0);
 | 
			
		||||
            this.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -526,9 +437,9 @@ const HotCorner = new Lang.Class({
 | 
			
		||||
                             Lang.bind(this, this._onCornerLeft));
 | 
			
		||||
 | 
			
		||||
        // Cache the three ripples instead of dynamically creating and destroying them.
 | 
			
		||||
        this._ripple1 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false });
 | 
			
		||||
        this._ripple2 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false });
 | 
			
		||||
        this._ripple3 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false });
 | 
			
		||||
        this._ripple1 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
 | 
			
		||||
        this._ripple2 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
 | 
			
		||||
        this._ripple3 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
 | 
			
		||||
 | 
			
		||||
        Main.uiGroup.add_actor(this._ripple1);
 | 
			
		||||
        Main.uiGroup.add_actor(this._ripple2);
 | 
			
		||||
@@ -549,7 +460,7 @@ const HotCorner = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        ripple._opacity = startOpacity;
 | 
			
		||||
 | 
			
		||||
        if (ripple.get_text_direction() == Clutter.TextDirection.RTL)
 | 
			
		||||
        if (ripple.get_direction() == St.TextDirection.RTL)
 | 
			
		||||
            ripple.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
 | 
			
		||||
 | 
			
		||||
        ripple.visible = true;
 | 
			
		||||
@@ -583,15 +494,13 @@ const HotCorner = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    handleDragOver: function(source, actor, x, y, time) {
 | 
			
		||||
        if (source != Main.xdndHandler)
 | 
			
		||||
            return DND.DragMotionResult.CONTINUE;
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!Main.overview.visible && !Main.overview.animationInProgress) {
 | 
			
		||||
            this.rippleAnimation();
 | 
			
		||||
            Main.overview.showTemporarily();
 | 
			
		||||
            Main.overview.beginItemDrag(actor);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return DND.DragMotionResult.CONTINUE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCornerEntered : function() {
 | 
			
		||||
@@ -639,7 +548,7 @@ const HotCorner = new Lang.Class({
 | 
			
		||||
            return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// This manages the shell "chrome"; the UI that's visible in the
 | 
			
		||||
@@ -647,14 +556,16 @@ const HotCorner = new Lang.Class({
 | 
			
		||||
// workspace content.
 | 
			
		||||
 | 
			
		||||
const defaultParams = {
 | 
			
		||||
    trackFullscreen: false,
 | 
			
		||||
    visibleInFullscreen: false,
 | 
			
		||||
    affectsStruts: false,
 | 
			
		||||
    affectsInputRegion: true
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Chrome = new Lang.Class({
 | 
			
		||||
    Name: 'Chrome',
 | 
			
		||||
function Chrome() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Chrome.prototype = {
 | 
			
		||||
    _init: function(layoutManager) {
 | 
			
		||||
        this._layoutManager = layoutManager;
 | 
			
		||||
 | 
			
		||||
@@ -674,13 +585,23 @@ 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.connect('ActiveChanged', Lang.bind(this, this._onScreenSaverActiveChanged));
 | 
			
		||||
        this._screenSaverProxy.GetActiveRemote(Lang.bind(this,
 | 
			
		||||
            function(result, err) {
 | 
			
		||||
                if (!err)
 | 
			
		||||
                    this._onScreenSaverActiveChanged(this._screenSaverProxy, result);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        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) {
 | 
			
		||||
@@ -766,30 +687,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);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -805,11 +724,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;
 | 
			
		||||
@@ -819,6 +733,12 @@ const Chrome = new Lang.Class({
 | 
			
		||||
        this._queueUpdateRegions();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onScreenSaverActiveChanged: function(proxy, 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;
 | 
			
		||||
@@ -934,8 +854,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;
 | 
			
		||||
@@ -945,15 +863,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() {
 | 
			
		||||
@@ -1067,6 +980,4 @@ const Chrome = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(Chrome.prototype);
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
@@ -8,8 +7,6 @@ const St = imports.gi.St;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const DEFAULT_FADE_FACTOR = 0.4;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Lightbox:
 | 
			
		||||
 * @container: parent Clutter.Container
 | 
			
		||||
@@ -17,8 +14,7 @@ const DEFAULT_FADE_FACTOR = 0.4;
 | 
			
		||||
 *           - inhibitEvents: whether to inhibit events for @container
 | 
			
		||||
 *           - width: shade actor width
 | 
			
		||||
 *           - height: shade actor height
 | 
			
		||||
 *           - fadeInTime: seconds used to fade in
 | 
			
		||||
 *           - fadeOutTime: seconds used to fade out
 | 
			
		||||
 *           - fadeTime: seconds used to fade in/out
 | 
			
		||||
 *
 | 
			
		||||
 * Lightbox creates a dark translucent "shade" actor to hide the
 | 
			
		||||
 * contents of @container, and allows you to specify particular actors
 | 
			
		||||
@@ -34,23 +30,21 @@ const DEFAULT_FADE_FACTOR = 0.4;
 | 
			
		||||
 * @container and will track any changes in its size. You can override
 | 
			
		||||
 * this by passing an explicit width and height in @params.
 | 
			
		||||
 */
 | 
			
		||||
const Lightbox = new Lang.Class({
 | 
			
		||||
    Name: 'Lightbox',
 | 
			
		||||
function Lightbox(container, params) {
 | 
			
		||||
    this._init(container, params);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Lightbox.prototype = {
 | 
			
		||||
    _init : function(container, params) {
 | 
			
		||||
        params = Params.parse(params, { inhibitEvents: false,
 | 
			
		||||
                                        width: null,
 | 
			
		||||
                                        height: null,
 | 
			
		||||
                                        fadeInTime: null,
 | 
			
		||||
                                        fadeOutTime: null,
 | 
			
		||||
                                        fadeFactor: DEFAULT_FADE_FACTOR
 | 
			
		||||
                                        fadeTime: null
 | 
			
		||||
                                      });
 | 
			
		||||
 | 
			
		||||
        this._container = container;
 | 
			
		||||
        this._children = container.get_children();
 | 
			
		||||
        this._fadeInTime = params.fadeInTime;
 | 
			
		||||
        this._fadeOutTime = params.fadeOutTime;
 | 
			
		||||
        this._fadeFactor = params.fadeFactor;
 | 
			
		||||
        this._fadeTime = params.fadeTime;
 | 
			
		||||
        this.actor = new St.Bin({ x: 0,
 | 
			
		||||
                                  y: 0,
 | 
			
		||||
                                  style_class: 'lightbox',
 | 
			
		||||
@@ -59,17 +53,17 @@ const Lightbox = new Lang.Class({
 | 
			
		||||
        container.add_actor(this.actor);
 | 
			
		||||
        this.actor.raise_top();
 | 
			
		||||
        this.actor.hide();
 | 
			
		||||
        this.shown = false;
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 | 
			
		||||
 | 
			
		||||
        if (params.width && params.height) {
 | 
			
		||||
            this.actor.width = params.width;
 | 
			
		||||
            this.actor.height = params.height;
 | 
			
		||||
            this._allocationChangedSignalId = 0;
 | 
			
		||||
        } else {
 | 
			
		||||
            let constraint = new Clutter.BindConstraint({ source: container,
 | 
			
		||||
                                                          coordinate: Clutter.BindCoordinate.ALL });
 | 
			
		||||
            this.actor.add_constraint(constraint);
 | 
			
		||||
            this.actor.width = container.width;
 | 
			
		||||
            this.actor.height = container.height;
 | 
			
		||||
            this._allocationChangedSignalId = container.connect('allocation-changed', Lang.bind(this, this._allocationChanged));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._actorAddedSignalId = container.connect('actor-added', Lang.bind(this, this._actorAdded));
 | 
			
		||||
@@ -78,6 +72,16 @@ const Lightbox = new Lang.Class({
 | 
			
		||||
        this._highlighted = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocationChanged : function(container, box, flags) {
 | 
			
		||||
        Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
 | 
			
		||||
            this.actor.width = this.width;
 | 
			
		||||
            this.actor.height = this.height;
 | 
			
		||||
            return false;
 | 
			
		||||
        }));
 | 
			
		||||
        this.width = this._container.width;
 | 
			
		||||
        this.height = this._container.height;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _actorAdded : function(container, newChild) {
 | 
			
		||||
        let children = this._container.get_children();
 | 
			
		||||
        let myIndex = children.indexOf(this.actor);
 | 
			
		||||
@@ -101,30 +105,24 @@ const Lightbox = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function() {
 | 
			
		||||
        if (this._fadeInTime) {
 | 
			
		||||
            this.shown = false;
 | 
			
		||||
        if (this._fadeTime) {
 | 
			
		||||
            this.actor.opacity = 0;
 | 
			
		||||
            Tweener.addTween(this.actor,
 | 
			
		||||
                             { opacity: 255 * this._fadeFactor,
 | 
			
		||||
                               time: this._fadeInTime,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onComplete: Lang.bind(this, function() {
 | 
			
		||||
                                   this.shown = true;
 | 
			
		||||
                               })
 | 
			
		||||
                             { opacity: 255,
 | 
			
		||||
                               time: this._fadeTime,
 | 
			
		||||
                               transition: 'easeOutQuad'
 | 
			
		||||
                             });
 | 
			
		||||
        } else {
 | 
			
		||||
            this.actor.opacity = 255 * this._fadeFactor;
 | 
			
		||||
            this.shown = true;
 | 
			
		||||
            this.actor.opacity = 255;
 | 
			
		||||
        }
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hide: function() {
 | 
			
		||||
        this.shown = false;
 | 
			
		||||
        if (this._fadeOutTime) {
 | 
			
		||||
        if (this._fadeTime) {
 | 
			
		||||
            Tweener.addTween(this.actor,
 | 
			
		||||
                             { opacity: 0,
 | 
			
		||||
                               time: this._fadeOutTime,
 | 
			
		||||
                               time: this._fadeTime,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onComplete: Lang.bind(this, function() {
 | 
			
		||||
                                   this.actor.hide();
 | 
			
		||||
@@ -191,9 +189,11 @@ const Lightbox = new Lang.Class({
 | 
			
		||||
     * by destroying its container or by explicitly calling this.destroy().
 | 
			
		||||
     */
 | 
			
		||||
    _onDestroy: function() {
 | 
			
		||||
        if (this._allocationChangedSignalId != 0)
 | 
			
		||||
            this._container.disconnect(this._allocationChangedSignalId);
 | 
			
		||||
        this._container.disconnect(this._actorAddedSignalId);
 | 
			
		||||
        this._container.disconnect(this._actorRemovedSignalId);
 | 
			
		||||
 | 
			
		||||
        this.highlight(null);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								js/ui/link.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,24 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
function Link(props) {
 | 
			
		||||
    this._init(props);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Link.prototype = {
 | 
			
		||||
    _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);
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Cogl = imports.gi.Cogl;
 | 
			
		||||
const GConf = imports.gi.GConf;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
@@ -12,17 +13,13 @@ 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; ' +
 | 
			
		||||
@@ -38,93 +35,18 @@ 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';
 | 
			
		||||
// Time between tabs for them to count as a double-tab event
 | 
			
		||||
const AUTO_COMPLETE_DOUBLE_TAB_DELAY = 500;
 | 
			
		||||
const AUTO_COMPLETE_SHOW_COMPLETION_ANIMATION_DURATION = 0.2;
 | 
			
		||||
const AUTO_COMPLETE_GLOBAL_KEYWORDS = _getAutoCompleteGlobalKeywords();
 | 
			
		||||
 | 
			
		||||
function _getAutoCompleteGlobalKeywords() {
 | 
			
		||||
    const keywords = ['true', 'false', 'null', 'new'];
 | 
			
		||||
    // Don't add the private properties of window (i.e., ones starting with '_')
 | 
			
		||||
    const windowProperties = Object.getOwnPropertyNames(window).filter(function(a){ return a.charAt(0) != '_' });
 | 
			
		||||
    const headerProperties = JsParse.getDeclaredConstants(commandHeader);
 | 
			
		||||
 | 
			
		||||
    return keywords.concat(windowProperties).concat(headerProperties);
 | 
			
		||||
function Notebook() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AutoComplete = new Lang.Class({
 | 
			
		||||
    Name: 'AutoComplete',
 | 
			
		||||
 | 
			
		||||
    _init: function(entry) {
 | 
			
		||||
        this._entry = entry;
 | 
			
		||||
        this._entry.connect('key-press-event', Lang.bind(this, this._entryKeyPressEvent));
 | 
			
		||||
        this._lastTabTime = global.get_current_time();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _processCompletionRequest: function(event) {
 | 
			
		||||
        if (event.completions.length == 0) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        // Unique match = go ahead and complete; multiple matches + single tab = complete the common starting string;
 | 
			
		||||
        // multiple matches + double tab = emit a suggest event with all possible options
 | 
			
		||||
        if (event.completions.length == 1) {
 | 
			
		||||
            this.additionalCompletionText(event.completions[0], event.attrHead);
 | 
			
		||||
            this.emit('completion', { completion: event.completions[0], type: 'whole-word' });
 | 
			
		||||
        } else if (event.completions.length > 1 && event.tabType === 'single') {
 | 
			
		||||
            let commonPrefix = JsParse.getCommonPrefix(event.completions);
 | 
			
		||||
 | 
			
		||||
            if (commonPrefix.length > 0) {
 | 
			
		||||
                this.additionalCompletionText(commonPrefix, event.attrHead);
 | 
			
		||||
                this.emit('completion', { completion: commonPrefix, type: 'prefix' });
 | 
			
		||||
                this.emit('suggest', { completions: event.completions});
 | 
			
		||||
            }
 | 
			
		||||
        } else if (event.completions.length > 1 && event.tabType === 'double') {
 | 
			
		||||
            this.emit('suggest', { completions: event.completions});
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _entryKeyPressEvent: function(actor, event) {
 | 
			
		||||
        let cursorPos = this._entry.clutter_text.get_cursor_position();
 | 
			
		||||
        let text = this._entry.get_text();
 | 
			
		||||
        if (cursorPos != -1) {
 | 
			
		||||
            text = text.slice(0, cursorPos);
 | 
			
		||||
        }
 | 
			
		||||
        if (event.get_key_symbol() == Clutter.Tab) {
 | 
			
		||||
            let [completions, attrHead] = JsParse.getCompletions(text, commandHeader, AUTO_COMPLETE_GLOBAL_KEYWORDS);
 | 
			
		||||
            let currTime = global.get_current_time();
 | 
			
		||||
            if ((currTime - this._lastTabTime) < AUTO_COMPLETE_DOUBLE_TAB_DELAY) {
 | 
			
		||||
                this._processCompletionRequest({ tabType: 'double',
 | 
			
		||||
                                                 completions: completions,
 | 
			
		||||
                                                 attrHead: attrHead });
 | 
			
		||||
            } else {
 | 
			
		||||
                this._processCompletionRequest({ tabType: 'single',
 | 
			
		||||
                                                 completions: completions,
 | 
			
		||||
                                                 attrHead: attrHead });
 | 
			
		||||
            }
 | 
			
		||||
            this._lastTabTime = currTime;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Insert characters of text not already included in head at cursor position.  i.e., if text="abc" and head="a",
 | 
			
		||||
    // the string "bc" will be appended to this._entry
 | 
			
		||||
    additionalCompletionText: function(text, head) {
 | 
			
		||||
        let additionalCompletionText = text.slice(head.length);
 | 
			
		||||
        let cursorPos = this._entry.clutter_text.get_cursor_position();
 | 
			
		||||
 | 
			
		||||
        this._entry.clutter_text.insert_text(additionalCompletionText, cursorPos);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(AutoComplete.prototype);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const Notebook = new Lang.Class({
 | 
			
		||||
    Name: 'Notebook',
 | 
			
		||||
 | 
			
		||||
Notebook.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new St.BoxLayout({ vertical: true });
 | 
			
		||||
 | 
			
		||||
@@ -229,26 +151,8 @@ const Notebook = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
        let vAdjust = tabData.scrollView.vscroll.adjustment;
 | 
			
		||||
        vAdjust.value = vAdjust.upper - vAdjust.page_size;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    nextTab: function() {
 | 
			
		||||
        let nextIndex = this._selectedIndex;
 | 
			
		||||
        if (nextIndex < this._tabs.length - 1) {
 | 
			
		||||
            ++nextIndex;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.selectIndex(nextIndex);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    prevTab: function() {
 | 
			
		||||
        let prevIndex = this._selectedIndex;
 | 
			
		||||
        if (prevIndex > 0) {
 | 
			
		||||
            --prevIndex;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.selectIndex(prevIndex);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(Notebook.prototype);
 | 
			
		||||
 | 
			
		||||
function objectToString(o) {
 | 
			
		||||
@@ -260,10 +164,14 @@ function objectToString(o) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ObjLink = new Lang.Class({
 | 
			
		||||
    Name: 'ObjLink',
 | 
			
		||||
function ObjLink(o, title) {
 | 
			
		||||
    this._init(o, title);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    _init: function(lookingGlass, o, title) {
 | 
			
		||||
ObjLink.prototype = {
 | 
			
		||||
    __proto__: Link.Link,
 | 
			
		||||
 | 
			
		||||
    _init: function(o, title) {
 | 
			
		||||
        let text;
 | 
			
		||||
        if (title)
 | 
			
		||||
            text = title;
 | 
			
		||||
@@ -271,31 +179,26 @@ const ObjLink = new Lang.Class({
 | 
			
		||||
            text = objectToString(o);
 | 
			
		||||
        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 });
 | 
			
		||||
        Link.Link.prototype._init.call(this, { 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',
 | 
			
		||||
function Result(command, o, index) {
 | 
			
		||||
    this._init(command, o, index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    _init: function(lookingGlass, command, o, index) {
 | 
			
		||||
Result.prototype = {
 | 
			
		||||
    _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,30 +208,30 @@ 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 });
 | 
			
		||||
        padBin.add_actor(line);
 | 
			
		||||
        this.actor.add(padBin);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const WindowList = new Lang.Class({
 | 
			
		||||
    Name: 'WindowList',
 | 
			
		||||
function WindowList() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    _init: function(lookingGlass) {
 | 
			
		||||
WindowList.prototype = {
 | 
			
		||||
    _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 +243,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 +254,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 {
 | 
			
		||||
@@ -359,27 +262,26 @@ const WindowList = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(WindowList.prototype);
 | 
			
		||||
 | 
			
		||||
const ObjInspector = new Lang.Class({
 | 
			
		||||
    Name: 'ObjInspector',
 | 
			
		||||
function ObjInspector() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    _init: function(lookingGlass) {
 | 
			
		||||
ObjInspector.prototype = {
 | 
			
		||||
    _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 +291,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);
 | 
			
		||||
@@ -411,19 +313,12 @@ const ObjInspector = new Lang.Class({
 | 
			
		||||
        button.connect('clicked', Lang.bind(this, this.close));
 | 
			
		||||
        hbox.add(button);
 | 
			
		||||
        if (typeof(obj) == typeof({})) {
 | 
			
		||||
            let properties = [];
 | 
			
		||||
            for (let propName in obj) {
 | 
			
		||||
                properties.push(propName);
 | 
			
		||||
            }
 | 
			
		||||
            properties.sort();
 | 
			
		||||
 | 
			
		||||
            for (let i = 0; i < properties.length; i++) {
 | 
			
		||||
                let propName = properties[i];
 | 
			
		||||
                let valueStr;
 | 
			
		||||
                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 +339,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,44 +363,44 @@ const ObjInspector = new Lang.Class({
 | 
			
		||||
    _onInsert: function() {
 | 
			
		||||
        let obj = this._obj;
 | 
			
		||||
        this.close();
 | 
			
		||||
        this._lookingGlass.insertObject(obj);
 | 
			
		||||
        Main.lookingGlass.insertObject(obj);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onBack: function() {
 | 
			
		||||
        this.selectObject(this._previousObj, true);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
    actor.queue_redraw();
 | 
			
		||||
    return signalId;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
        // 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);
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
function Inspector() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Inspector = new Lang.Class({
 | 
			
		||||
    Name: 'Inspector',
 | 
			
		||||
 | 
			
		||||
    _init: function(lookingGlass) {
 | 
			
		||||
Inspector.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        let container = new Shell.GenericContainer({ width: 0,
 | 
			
		||||
                                                     height: 0 });
 | 
			
		||||
        container.connect('allocate', Lang.bind(this, this._allocate));
 | 
			
		||||
@@ -515,6 +414,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 +431,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 +451,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,15 +531,63 @@ 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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(Inspector.prototype);
 | 
			
		||||
 | 
			
		||||
const Memory = new Lang.Class({
 | 
			
		||||
    Name: 'Memory',
 | 
			
		||||
function ErrorLog() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ErrorLog.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new St.BoxLayout();
 | 
			
		||||
        this.text = new St.Label();
 | 
			
		||||
        this.actor.add(this.text);
 | 
			
		||||
        // We need to override StLabel's default ellipsization when
 | 
			
		||||
        // using line_wrap; otherwise ClutterText's layout is going
 | 
			
		||||
        // to constrain both the width and height, which prevents
 | 
			
		||||
        // scrolling.
 | 
			
		||||
        this.text.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
        this.text.clutter_text.line_wrap = true;
 | 
			
		||||
        this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _formatTime: function(d){
 | 
			
		||||
        function pad(n) { return n < 10 ? '0' + n : n; }
 | 
			
		||||
        return d.getUTCFullYear()+'-'
 | 
			
		||||
            + pad(d.getUTCMonth()+1)+'-'
 | 
			
		||||
            + pad(d.getUTCDate())+'T'
 | 
			
		||||
            + pad(d.getUTCHours())+':'
 | 
			
		||||
            + pad(d.getUTCMinutes())+':'
 | 
			
		||||
            + pad(d.getUTCSeconds())+'Z';
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _renderText: function() {
 | 
			
		||||
        if (!this.actor.mapped)
 | 
			
		||||
            return;
 | 
			
		||||
        let text = this.text.text;
 | 
			
		||||
        let stack = Main._getAndClearErrorStack();
 | 
			
		||||
        for (let i = 0; i < stack.length; i++) {
 | 
			
		||||
            let logItem = stack[i];
 | 
			
		||||
            text += logItem.category + ' t=' + this._formatTime(new Date(logItem.timestamp)) + ' ' + logItem.message + '\n';
 | 
			
		||||
        }
 | 
			
		||||
        this.text.text = text;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function Memory() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Memory.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        this._glibc_uordblks = new St.Label();
 | 
			
		||||
@@ -660,7 +613,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 });
 | 
			
		||||
 | 
			
		||||
@@ -679,11 +632,13 @@ const Memory = new Lang.Class({
 | 
			
		||||
        this._gjs_closure.text = 'gjs_closure: ' + memInfo.gjs_closure;
 | 
			
		||||
        this._last_gc_seconds_ago.text = 'last_gc_seconds_ago: ' + memInfo.last_gc_seconds_ago;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Extensions = new Lang.Class({
 | 
			
		||||
    Name: 'Extensions',
 | 
			
		||||
function Extensions() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Extensions.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new St.BoxLayout({ vertical: true,
 | 
			
		||||
                                        name: 'lookingGlassExtensions' });
 | 
			
		||||
@@ -695,7 +650,7 @@ const Extensions = new Lang.Class({
 | 
			
		||||
        this._extensionsList.add(this._noExtensions);
 | 
			
		||||
        this.actor.add(this._extensionsList);
 | 
			
		||||
 | 
			
		||||
        for (let uuid in ExtensionUtils.extensions)
 | 
			
		||||
        for (let uuid in ExtensionSystem.extensionMeta)
 | 
			
		||||
            this._loadExtension(null, uuid);
 | 
			
		||||
 | 
			
		||||
        ExtensionSystem.connect('extension-loaded',
 | 
			
		||||
@@ -703,10 +658,10 @@ const Extensions = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loadExtension: function(o, uuid) {
 | 
			
		||||
        let extension = ExtensionUtils.extensions[uuid];
 | 
			
		||||
        let extension = ExtensionSystem.extensionMeta[uuid];
 | 
			
		||||
        // There can be cases where we create dummy extension metadata
 | 
			
		||||
        // that's not really a proper extension. Don't bother with these.
 | 
			
		||||
        if (!extension.metadata.name)
 | 
			
		||||
        if (!extension.name)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let extensionDisplay = this._createExtensionDisplay(extension);
 | 
			
		||||
@@ -718,44 +673,17 @@ const Extensions = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onViewSource: function (actor) {
 | 
			
		||||
        let extension = actor._extension;
 | 
			
		||||
        let uri = extension.dir.get_uri();
 | 
			
		||||
        let meta = actor._extensionMeta;
 | 
			
		||||
        let file = Gio.file_new_for_path(meta.path);
 | 
			
		||||
        let uri = file.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();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onViewErrors: function (actor) {
 | 
			
		||||
        let extension = actor._extension;
 | 
			
		||||
        let shouldShow = !actor._isShowing;
 | 
			
		||||
 | 
			
		||||
        if (shouldShow) {
 | 
			
		||||
            let errors = extension.errors;
 | 
			
		||||
            let errorDisplay = new St.BoxLayout({ vertical: true });
 | 
			
		||||
            if (errors && errors.length) {
 | 
			
		||||
                for (let i = 0; i < errors.length; i ++)
 | 
			
		||||
                    errorDisplay.add(new St.Label({ text: errors[i] }));
 | 
			
		||||
            } else {
 | 
			
		||||
                /* Translators: argument is an extension UUID. */
 | 
			
		||||
                let message = _("%s has not emitted any errors.").format(extension.uuid);
 | 
			
		||||
                errorDisplay.add(new St.Label({ text: message }));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            actor._errorDisplay = errorDisplay;
 | 
			
		||||
            actor._parentBox.add(errorDisplay);
 | 
			
		||||
            actor.label = _("Hide Errors");
 | 
			
		||||
        } else {
 | 
			
		||||
            actor._errorDisplay.destroy();
 | 
			
		||||
            actor._errorDisplay = null;
 | 
			
		||||
            actor.label = _("Show Errors");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        actor._isShowing = shouldShow;
 | 
			
		||||
        let meta = actor._extensionMeta;
 | 
			
		||||
        Gio.app_info_launch_default_for_uri(meta.url, global.create_app_launch_context());
 | 
			
		||||
        Main.lookingGlass.close();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _stateToString: function(extensionState) {
 | 
			
		||||
@@ -763,7 +691,6 @@ const Extensions = new Lang.Class({
 | 
			
		||||
            case ExtensionSystem.ExtensionState.ENABLED:
 | 
			
		||||
                return _("Enabled");
 | 
			
		||||
            case ExtensionSystem.ExtensionState.DISABLED:
 | 
			
		||||
            case ExtensionSystem.ExtensionState.INITIALIZED:
 | 
			
		||||
                return _("Disabled");
 | 
			
		||||
            case ExtensionSystem.ExtensionState.ERROR:
 | 
			
		||||
                return _("Error");
 | 
			
		||||
@@ -775,60 +702,47 @@ const Extensions = new Lang.Class({
 | 
			
		||||
        return 'Unknown'; // Not translated, shouldn't appear
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createExtensionDisplay: function(extension) {
 | 
			
		||||
    _createExtensionDisplay: function(meta) {
 | 
			
		||||
        let box = new St.BoxLayout({ style_class: 'lg-extension', vertical: true });
 | 
			
		||||
        let name = new St.Label({ style_class: 'lg-extension-name',
 | 
			
		||||
                                   text: extension.metadata.name });
 | 
			
		||||
                                   text: meta.name });
 | 
			
		||||
        box.add(name, { expand: true });
 | 
			
		||||
        let description = new St.Label({ style_class: 'lg-extension-description',
 | 
			
		||||
                                         text: extension.metadata.description || 'No description' });
 | 
			
		||||
                                         text: meta.description || 'No description' });
 | 
			
		||||
        box.add(description, { expand: true });
 | 
			
		||||
 | 
			
		||||
        let metaBox = new St.BoxLayout({ style_class: 'lg-extension-meta' });
 | 
			
		||||
        box.add(metaBox);
 | 
			
		||||
        let stateString = this._stateToString(extension.state);
 | 
			
		||||
        let stateString = this._stateToString(meta.state);
 | 
			
		||||
        let state = new St.Label({ style_class: 'lg-extension-state',
 | 
			
		||||
                                   text: this._stateToString(extension.state) });
 | 
			
		||||
                                   text: this._stateToString(meta.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._extensionMeta = meta;
 | 
			
		||||
        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);
 | 
			
		||||
        if (meta.url) {
 | 
			
		||||
            let webpage = new Link.Link({ label: _("Web Page") });
 | 
			
		||||
            webpage.actor._extensionMeta = meta;
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
        return box;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const LookingGlass = new Lang.Class({
 | 
			
		||||
    Name: 'LookingGlass',
 | 
			
		||||
function LookingGlass() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LookingGlass.prototype = {
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        this._borderPaintTarget = null;
 | 
			
		||||
        this._redBorderEffect = new RedBorderEffect();
 | 
			
		||||
        this._borderPaintId = 0;
 | 
			
		||||
        this._borderDestroyId = 0;
 | 
			
		||||
 | 
			
		||||
        this._open = false;
 | 
			
		||||
 | 
			
		||||
@@ -841,8 +755,7 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
        this.actor = new St.BoxLayout({ name: 'LookingGlassDialog',
 | 
			
		||||
                                        style_class: 'lg-dialog',
 | 
			
		||||
                                        vertical: true,
 | 
			
		||||
                                        visible: false,
 | 
			
		||||
                                        reactive: true });
 | 
			
		||||
                                        visible: false });
 | 
			
		||||
        this.actor.connect('key-press-event', Lang.bind(this, this._globalKeyPressEvent));
 | 
			
		||||
 | 
			
		||||
        this._interfaceSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
 | 
			
		||||
@@ -858,18 +771,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);
 | 
			
		||||
@@ -896,19 +810,26 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
        this._resultsArea = new St.BoxLayout({ name: 'ResultsArea', vertical: true });
 | 
			
		||||
        this._evalBox.add(this._resultsArea, { expand: true });
 | 
			
		||||
 | 
			
		||||
        this._entryArea = new St.BoxLayout({ name: 'EntryArea' });
 | 
			
		||||
        this._evalBox.add_actor(this._entryArea);
 | 
			
		||||
        let entryArea = new St.BoxLayout({ name: 'EntryArea' });
 | 
			
		||||
        this._evalBox.add_actor(entryArea);
 | 
			
		||||
 | 
			
		||||
        let label = new St.Label({ text: CHEVRON });
 | 
			
		||||
        this._entryArea.add(label);
 | 
			
		||||
        let label = new St.Label({ text: 'js>>> ' });
 | 
			
		||||
        entryArea.add(label);
 | 
			
		||||
 | 
			
		||||
        this._entry = new St.Entry({ can_focus: true });
 | 
			
		||||
        ShellEntry.addContextMenu(this._entry);
 | 
			
		||||
        this._entryArea.add(this._entry, { expand: true });
 | 
			
		||||
        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._errorLog = new ErrorLog();
 | 
			
		||||
        notebook.appendPage('Errors', this._errorLog.actor);
 | 
			
		||||
 | 
			
		||||
        this._memory = new Memory();
 | 
			
		||||
        notebook.appendPage('Memory', this._memory.actor);
 | 
			
		||||
 | 
			
		||||
@@ -916,9 +837,6 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
        notebook.appendPage('Extensions', this._extensions.actor);
 | 
			
		||||
 | 
			
		||||
        this._entry.clutter_text.connect('activate', Lang.bind(this, function (o, e) {
 | 
			
		||||
            // Hide any completions we are currently showing
 | 
			
		||||
            this._hideCompletions();
 | 
			
		||||
 | 
			
		||||
            let text = o.get_text();
 | 
			
		||||
            // Ensure we don't get newlines in the command; the history file is
 | 
			
		||||
            // newline-separated.
 | 
			
		||||
@@ -934,17 +852,6 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
        this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY, 
 | 
			
		||||
                                                     entry: this._entry.clutter_text });
 | 
			
		||||
 | 
			
		||||
        this._autoComplete = new AutoComplete(this._entry);
 | 
			
		||||
        this._autoComplete.connect('suggest', Lang.bind(this, function(a,e) {
 | 
			
		||||
            this._showCompletions(e.completions);
 | 
			
		||||
        }));
 | 
			
		||||
        // If a completion is completed unambiguously, the currently-displayed completion
 | 
			
		||||
        // suggestions become irrelevant.
 | 
			
		||||
        this._autoComplete.connect('completion', Lang.bind(this, function(a,e) {
 | 
			
		||||
            if (e.type == 'whole-word')
 | 
			
		||||
                this._hideCompletions();
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._resize();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -960,22 +867,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();
 | 
			
		||||
@@ -988,59 +896,6 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
        this._notebook.scrollToBottom(0);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showCompletions: function(completions) {
 | 
			
		||||
        if (!this._completionActor) {
 | 
			
		||||
            let actor = new St.BoxLayout({ vertical: true });
 | 
			
		||||
 | 
			
		||||
            this._completionText = new St.Label({ name: 'LookingGlassAutoCompletionText', style_class: 'lg-completions-text' });
 | 
			
		||||
            this._completionText.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
            this._completionText.clutter_text.line_wrap = true;
 | 
			
		||||
            actor.add(this._completionText);
 | 
			
		||||
 | 
			
		||||
            let line = new Clutter.Rectangle();
 | 
			
		||||
            let padBin = new St.Bin({ x_fill: true, y_fill: true });
 | 
			
		||||
            padBin.add_actor(line);
 | 
			
		||||
            actor.add(padBin);
 | 
			
		||||
 | 
			
		||||
            this._completionActor = actor;
 | 
			
		||||
            this._evalBox.insert_child_below(this._completionActor, this._entryArea);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._completionText.set_text(completions.join(', '));
 | 
			
		||||
 | 
			
		||||
        // Setting the height to -1 allows us to get its actual preferred height rather than
 | 
			
		||||
        // whatever was last given in set_height by Tweener.
 | 
			
		||||
        this._completionActor.set_height(-1);
 | 
			
		||||
        let [minHeight, naturalHeight] = this._completionText.get_preferred_height(this._resultsArea.get_width());
 | 
			
		||||
 | 
			
		||||
        // Don't reanimate if we are already visible
 | 
			
		||||
        if (this._completionActor.visible) {
 | 
			
		||||
            this._completionActor.height = naturalHeight;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._completionActor.show();
 | 
			
		||||
            Tweener.removeTweens(this._completionActor);
 | 
			
		||||
            Tweener.addTween(this._completionActor, { time: AUTO_COMPLETE_SHOW_COMPLETION_ANIMATION_DURATION / St.get_slow_down_factor(),
 | 
			
		||||
                                                      transition: 'easeOutQuad',
 | 
			
		||||
                                                      height: naturalHeight,
 | 
			
		||||
                                                      opacity: 255
 | 
			
		||||
                                                    });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hideCompletions: function() {
 | 
			
		||||
        if (this._completionActor) {
 | 
			
		||||
            Tweener.removeTweens(this._completionActor);
 | 
			
		||||
            Tweener.addTween(this._completionActor, { time: AUTO_COMPLETE_SHOW_COMPLETION_ANIMATION_DURATION / St.get_slow_down_factor(),
 | 
			
		||||
                                                      transition: 'easeOutQuad',
 | 
			
		||||
                                                      height: 0,
 | 
			
		||||
                                                      opacity: 0,
 | 
			
		||||
                                                      onComplete: Lang.bind(this, function () {
 | 
			
		||||
                                                          this._completionActor.hide();
 | 
			
		||||
                                                      })
 | 
			
		||||
                                                    });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _evaluate : function(command) {
 | 
			
		||||
        this._history.addItem(command);
 | 
			
		||||
 | 
			
		||||
@@ -1089,8 +944,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) {
 | 
			
		||||
@@ -1105,7 +960,6 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
    // Handle key events which are relevant for all tabs of the LookingGlass
 | 
			
		||||
    _globalKeyPressEvent : function(actor, event) {
 | 
			
		||||
        let symbol = event.get_key_symbol();
 | 
			
		||||
        let modifierState = event.get_state();
 | 
			
		||||
        if (symbol == Clutter.Escape) {
 | 
			
		||||
            if (this._objInspector.actor.visible) {
 | 
			
		||||
                this._objInspector.close();
 | 
			
		||||
@@ -1114,14 +968,6 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        // Ctrl+PgUp and Ctrl+PgDown switches tabs in the notebook view
 | 
			
		||||
        if (modifierState & Clutter.ModifierType.CONTROL_MASK) {
 | 
			
		||||
            if (symbol == Clutter.KEY_Page_Up) {
 | 
			
		||||
                this._notebook.prevTab();
 | 
			
		||||
            } else if (symbol == Clutter.KEY_Page_Down) {
 | 
			
		||||
                this._notebook.nextTab();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -1129,7 +975,7 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
        if (this._open)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!Main.pushModal(this._entry, { keybindingMode: Main.KeybindingMode.LOOKING_GLASS }))
 | 
			
		||||
        if (!Main.pushModal(this._entry))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._notebook.selectIndex(0);
 | 
			
		||||
@@ -1156,7 +1002,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);
 | 
			
		||||
 | 
			
		||||
@@ -1168,5 +1018,5 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
                                       })
 | 
			
		||||
                                     });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(LookingGlass.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const GDesktopEnums = imports.gi.GDesktopEnums;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
@@ -12,11 +11,26 @@ const Signals = imports.signals;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MagnifierDBus = imports.ui.magnifierDBus;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const PointerWatcher = imports.ui.pointerWatcher;
 | 
			
		||||
 | 
			
		||||
// Keep enums in sync with GSettings schemas
 | 
			
		||||
const MouseTrackingMode = {
 | 
			
		||||
    NONE: 0,
 | 
			
		||||
    CENTERED: 1,
 | 
			
		||||
    PROPORTIONAL: 2,
 | 
			
		||||
    PUSH: 3
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ScreenPosition = {
 | 
			
		||||
    NONE: 0,
 | 
			
		||||
    FULL_SCREEN: 1,
 | 
			
		||||
    TOP_HALF: 2,
 | 
			
		||||
    BOTTOM_HALF: 3,
 | 
			
		||||
    LEFT_HALF: 4,
 | 
			
		||||
    RIGHT_HALF: 5
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const MOUSE_POLL_FREQUENCY = 50;
 | 
			
		||||
const CROSSHAIRS_CLIP_SIZE = [100, 100];
 | 
			
		||||
const NO_CHANGE = 0.0;
 | 
			
		||||
 | 
			
		||||
// Settings
 | 
			
		||||
const APPLICATIONS_SCHEMA       = 'org.gnome.desktop.a11y.applications';
 | 
			
		||||
@@ -25,14 +39,6 @@ const SHOW_KEY                  = 'screen-magnifier-enabled';
 | 
			
		||||
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';
 | 
			
		||||
const CONTRAST_RED_KEY          = 'contrast-red';
 | 
			
		||||
const CONTRAST_GREEN_KEY        = 'contrast-green';
 | 
			
		||||
const CONTRAST_BLUE_KEY         = 'contrast-blue';
 | 
			
		||||
const LENS_MODE_KEY             = 'lens-mode';
 | 
			
		||||
const CLAMP_MODE_KEY            = 'scroll-at-edges';
 | 
			
		||||
const MOUSE_TRACKING_KEY        = 'mouse-tracking';
 | 
			
		||||
@@ -45,18 +51,20 @@ const CROSS_HAIRS_CLIP_KEY      = 'cross-hairs-clip';
 | 
			
		||||
 | 
			
		||||
let magDBusService = null;
 | 
			
		||||
 | 
			
		||||
const Magnifier = new Lang.Class({
 | 
			
		||||
    Name: 'Magnifier',
 | 
			
		||||
function Magnifier() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Magnifier.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        // Magnifier is a manager of ZoomRegions.
 | 
			
		||||
        this._zoomRegions = [];
 | 
			
		||||
 | 
			
		||||
        // Create small clutter tree for the magnified mouse.
 | 
			
		||||
        let xfixesCursor = Shell.XFixesCursor.get_for_stage(global.stage);
 | 
			
		||||
        let xfixesCursor = Shell.XFixesCursor.get_default();
 | 
			
		||||
        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 +144,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 +156,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 +311,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);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -448,25 +460,6 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
            aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
 | 
			
		||||
            if (aPref)
 | 
			
		||||
                zoomRegion.setMouseTrackingMode(aPref);
 | 
			
		||||
 | 
			
		||||
            aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
 | 
			
		||||
            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);
 | 
			
		||||
            bc.b = this._settings.get_double(BRIGHT_BLUE_KEY);
 | 
			
		||||
            zoomRegion.setBrightness(bc);
 | 
			
		||||
 | 
			
		||||
            bc.r = this._settings.get_double(CONTRAST_RED_KEY);
 | 
			
		||||
            bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
 | 
			
		||||
            bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
 | 
			
		||||
            zoomRegion.setContrast(bc);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY);
 | 
			
		||||
@@ -489,25 +482,6 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
        this._settings.connect('changed::' + MOUSE_TRACKING_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateMouseTrackingMode));
 | 
			
		||||
 | 
			
		||||
        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));
 | 
			
		||||
        this._settings.connect('changed::' + BRIGHT_GREEN_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateBrightness));
 | 
			
		||||
        this._settings.connect('changed::' + BRIGHT_BLUE_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateBrightness));
 | 
			
		||||
 | 
			
		||||
        this._settings.connect('changed::' + CONTRAST_RED_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateContrast));
 | 
			
		||||
        this._settings.connect('changed::' + CONTRAST_GREEN_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateContrast));
 | 
			
		||||
        this._settings.connect('changed::' + CONTRAST_BLUE_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateContrast));
 | 
			
		||||
 | 
			
		||||
        this._settings.connect('changed::' + SHOW_CROSS_HAIRS_KEY,
 | 
			
		||||
                               Lang.bind(this, function() {
 | 
			
		||||
            this.setCrosshairsVisible(this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY));
 | 
			
		||||
@@ -546,7 +520,7 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
        if (this._zoomRegions.length) {
 | 
			
		||||
            let position = this._settings.get_enum(SCREEN_POSITION_KEY);
 | 
			
		||||
            this._zoomRegions[0].setScreenPosition(position);
 | 
			
		||||
            if (position != GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN)
 | 
			
		||||
            if (position != ScreenPosition.FULL_SCREEN)
 | 
			
		||||
                this._updateLensMode();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -583,67 +557,24 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
                this._settings.get_enum(MOUSE_TRACKING_KEY)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateInvertLightness: function() {
 | 
			
		||||
        // Applies only to the first zoom region.
 | 
			
		||||
        if (this._zoomRegions.length) {
 | 
			
		||||
            this._zoomRegions[0].setInvertLightness(
 | 
			
		||||
                this._settings.get_boolean(INVERT_LIGHTNESS_KEY)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _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) {
 | 
			
		||||
            let brightness = {};
 | 
			
		||||
            brightness.r = this._settings.get_double(BRIGHT_RED_KEY);
 | 
			
		||||
            brightness.g = this._settings.get_double(BRIGHT_GREEN_KEY);
 | 
			
		||||
            brightness.b = this._settings.get_double(BRIGHT_BLUE_KEY);
 | 
			
		||||
            this._zoomRegions[0].setBrightness(brightness);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateContrast: function() {
 | 
			
		||||
        // Applies only to the first zoom region.
 | 
			
		||||
        if (this._zoomRegions.length) {
 | 
			
		||||
            let contrast = {};
 | 
			
		||||
            contrast.r = this._settings.get_double(CONTRAST_RED_KEY);
 | 
			
		||||
            contrast.g = this._settings.get_double(CONTRAST_GREEN_KEY);
 | 
			
		||||
            contrast.b = this._settings.get_double(CONTRAST_BLUE_KEY);
 | 
			
		||||
            this._zoomRegions[0].setContrast(contrast);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(Magnifier.prototype);
 | 
			
		||||
 | 
			
		||||
const ZoomRegion = new Lang.Class({
 | 
			
		||||
    Name: 'ZoomRegion',
 | 
			
		||||
function ZoomRegion(magnifier, mouseSourceActor) {
 | 
			
		||||
    this._init(magnifier, mouseSourceActor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ZoomRegion.prototype = {
 | 
			
		||||
    _init: function(magnifier, mouseSourceActor) {
 | 
			
		||||
        this._magnifier = magnifier;
 | 
			
		||||
 | 
			
		||||
        this._mouseTrackingMode = GDesktopEnums.MagnifierMouseTrackingMode.NONE;
 | 
			
		||||
        this._mouseTrackingMode = MouseTrackingMode.NONE;
 | 
			
		||||
        this._clampScrollingAtEdges = false;
 | 
			
		||||
        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 };
 | 
			
		||||
        this._screenPosition = ScreenPosition.FULL_SCREEN;
 | 
			
		||||
 | 
			
		||||
        this._magView = null;
 | 
			
		||||
        this._background = null;
 | 
			
		||||
        this._uiGroupClone = null;
 | 
			
		||||
        this._mouseSourceActor = mouseSourceActor;
 | 
			
		||||
        this._mouseActor  = null;
 | 
			
		||||
@@ -653,15 +584,12 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        this._viewPortX = 0;
 | 
			
		||||
        this._viewPortY = 0;
 | 
			
		||||
        this._viewPortWidth = global.screen_width;
 | 
			
		||||
        this._viewPortHeight = global.screen_height;
 | 
			
		||||
        this._viewPortWidth = global.screen_height;
 | 
			
		||||
        this._xCenter = this._viewPortWidth / 2;
 | 
			
		||||
        this._yCenter = this._viewPortHeight / 2;
 | 
			
		||||
        this._xMagFactor = 1;
 | 
			
		||||
        this._yMagFactor = 1;
 | 
			
		||||
        this._followingCursor = false;
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed',
 | 
			
		||||
                                   Lang.bind(this, this._monitorsChanged));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -719,8 +647,7 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
     * @mode:     One of the enum MouseTrackingMode values.
 | 
			
		||||
     */
 | 
			
		||||
    setMouseTrackingMode: function(mode) {
 | 
			
		||||
        if (mode >= GDesktopEnums.MagnifierMouseTrackingMode.NONE &&
 | 
			
		||||
            mode <= GDesktopEnums.MagnifierMouseTrackingMode.PUSH)
 | 
			
		||||
        if (mode >= MouseTrackingMode.NONE && mode <= MouseTrackingMode.PUSH)
 | 
			
		||||
            this._mouseTrackingMode = mode;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -741,7 +668,7 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
     */
 | 
			
		||||
    setViewPort: function(viewPort) {
 | 
			
		||||
        this._setViewPort(viewPort);
 | 
			
		||||
        this._screenPosition = GDesktopEnums.MagnifierScreenPosition.NONE;
 | 
			
		||||
        this._screenPosition = ScreenPosition.NONE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -823,7 +750,7 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        viewPort.width = global.screen_width;
 | 
			
		||||
        viewPort.height = global.screen_height/2;
 | 
			
		||||
        this._setViewPort(viewPort);
 | 
			
		||||
        this._screenPosition = GDesktopEnums.MagnifierScreenPosition.TOP_HALF;
 | 
			
		||||
        this._screenPosition = ScreenPosition.TOP_HALF;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -837,7 +764,7 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        viewPort.width = global.screen_width;
 | 
			
		||||
        viewPort.height = global.screen_height/2;
 | 
			
		||||
        this._setViewPort(viewPort);
 | 
			
		||||
        this._screenPosition = GDesktopEnums.MagnifierScreenPosition.BOTTOM_HALF;
 | 
			
		||||
        this._screenPosition = ScreenPosition.BOTTOM_HALF;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -851,7 +778,7 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        viewPort.width = global.screen_width/2;
 | 
			
		||||
        viewPort.height = global.screen_height;
 | 
			
		||||
        this._setViewPort(viewPort);
 | 
			
		||||
        this._screenPosition = GDesktopEnums.MagnifierScreenPosition.LEFT_HALF;
 | 
			
		||||
        this._screenPosition = ScreenPosition.LEFT_HALF;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -865,7 +792,7 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        viewPort.width = global.screen_width/2;
 | 
			
		||||
        viewPort.height = global.screen_height;
 | 
			
		||||
        this._setViewPort(viewPort);
 | 
			
		||||
        this._screenPosition = GDesktopEnums.MagnifierScreenPosition.RIGHT_HALF;
 | 
			
		||||
        this._screenPosition = ScreenPosition.RIGHT_HALF;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -881,7 +808,7 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        viewPort.height = global.screen_height;
 | 
			
		||||
        this.setViewPort(viewPort);
 | 
			
		||||
 | 
			
		||||
        this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
 | 
			
		||||
        this._screenPosition = ScreenPosition.FULL_SCREEN;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -894,19 +821,19 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
     */
 | 
			
		||||
    setScreenPosition: function(inPosition) {
 | 
			
		||||
        switch (inPosition) {
 | 
			
		||||
            case GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN:
 | 
			
		||||
            case ScreenPosition.FULL_SCREEN:
 | 
			
		||||
                this.setFullScreenMode();
 | 
			
		||||
                break;
 | 
			
		||||
            case GDesktopEnums.MagnifierScreenPosition.TOP_HALF:
 | 
			
		||||
            case ScreenPosition.TOP_HALF:
 | 
			
		||||
                this.setTopHalf();
 | 
			
		||||
                break;
 | 
			
		||||
            case GDesktopEnums.MagnifierScreenPosition.BOTTOM_HALF:
 | 
			
		||||
            case ScreenPosition.BOTTOM_HALF:
 | 
			
		||||
                this.setBottomHalf();
 | 
			
		||||
                break;
 | 
			
		||||
            case GDesktopEnums.MagnifierScreenPosition.LEFT_HALF:
 | 
			
		||||
            case ScreenPosition.LEFT_HALF:
 | 
			
		||||
                this.setLeftHalf();
 | 
			
		||||
                break;
 | 
			
		||||
            case GDesktopEnums.MagnifierScreenPosition.RIGHT_HALF:
 | 
			
		||||
            case ScreenPosition.RIGHT_HALF:
 | 
			
		||||
                this.setRightHalf();
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -929,7 +856,7 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
     */
 | 
			
		||||
    scrollToMousePos: function() {
 | 
			
		||||
        this._followingCursor = true;
 | 
			
		||||
        if (this._mouseTrackingMode != GDesktopEnums.MagnifierMouseTrackingMode.NONE)
 | 
			
		||||
        if (this._mouseTrackingMode != MouseTrackingMode.NONE)
 | 
			
		||||
            this._changeROI({ redoCursorTracking: true });
 | 
			
		||||
        else
 | 
			
		||||
            this._updateMousePosition();
 | 
			
		||||
@@ -966,107 +893,6 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * setInvertLightness:
 | 
			
		||||
     * Set whether to invert the lightness of the magnified view.
 | 
			
		||||
     * @flag    Boolean to either invert brightness (true), or not (false).
 | 
			
		||||
     */
 | 
			
		||||
    setInvertLightness: function(flag) {
 | 
			
		||||
        this._invertLightness = flag;
 | 
			
		||||
        if (this._magShaderEffects)
 | 
			
		||||
            this._magShaderEffects.setInvertLightness(this._invertLightness);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getInvertLightness:
 | 
			
		||||
     * Retrieve whether the lightness is inverted.
 | 
			
		||||
     * @return    Boolean indicating inversion (true), or not (false).
 | 
			
		||||
     */
 | 
			
		||||
    getInvertLightness: function() {
 | 
			
		||||
        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.
 | 
			
		||||
     * @brightness  Object containing the contrast for the red, green,
 | 
			
		||||
     *              and blue channels.  Values of 0.0 represent "standard"
 | 
			
		||||
     *              brightness (no change), whereas values less or greater than
 | 
			
		||||
     *              0.0 indicate decreased or incresaed brightness, respectively.
 | 
			
		||||
     */
 | 
			
		||||
    setBrightness: function(brightness) {
 | 
			
		||||
        this._brightness.r = brightness.r;
 | 
			
		||||
        this._brightness.g = brightness.g;
 | 
			
		||||
        this._brightness.b = brightness.b;
 | 
			
		||||
        if (this._magShaderEffects)
 | 
			
		||||
            this._magShaderEffects.setBrightness(this._brightness);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getBrightness:
 | 
			
		||||
     * Retrive the current brightness of the Zoom Region.
 | 
			
		||||
     * @return  Object containing the brightness change for the red, green,
 | 
			
		||||
     *          and blue channels.
 | 
			
		||||
     */
 | 
			
		||||
    getBrightness: function() {
 | 
			
		||||
        let brightness = {};
 | 
			
		||||
        brightness.r = this._brightness.r;
 | 
			
		||||
        brightness.g = this._brightness.g;
 | 
			
		||||
        brightness.b = this._brightness.b;
 | 
			
		||||
        return brightness;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * setContrast:
 | 
			
		||||
     * Alter the contrast of the magnified view.
 | 
			
		||||
     * @contrast    Object containing the contrast for the red, green,
 | 
			
		||||
     *              and blue channels.  Values of 0.0 represent "standard"
 | 
			
		||||
     *              contrast (no change), whereas values less or greater than
 | 
			
		||||
     *              0.0 indicate decreased or incresaed contrast, respectively.
 | 
			
		||||
     */
 | 
			
		||||
    setContrast: function(contrast) {
 | 
			
		||||
        this._contrast.r = contrast.r;
 | 
			
		||||
        this._contrast.g = contrast.g;
 | 
			
		||||
        this._contrast.b = contrast.b;
 | 
			
		||||
        if (this._magShaderEffects)
 | 
			
		||||
            this._magShaderEffects.setContrast(this._contrast);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getContrast:
 | 
			
		||||
     * Retreive the contrast of the magnified view.
 | 
			
		||||
     * @return  Object containing the contrast for the red, green,
 | 
			
		||||
     *          and blue channels.
 | 
			
		||||
     */
 | 
			
		||||
    getContrast: function() {
 | 
			
		||||
        let contrast = {};
 | 
			
		||||
        contrast.r = this._contrast.r;
 | 
			
		||||
        contrast.g = this._contrast.g;
 | 
			
		||||
        contrast.b = this._contrast.b;
 | 
			
		||||
        return contrast;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Private methods ////
 | 
			
		||||
 | 
			
		||||
    _createActors: function() {
 | 
			
		||||
@@ -1077,21 +903,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 });
 | 
			
		||||
        mainGroup.add_actor(this._background);
 | 
			
		||||
        let background = new Clutter.Rectangle({ color: Main.DEFAULT_BACKGROUND_COLOR });
 | 
			
		||||
        mainGroup.add_actor(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);
 | 
			
		||||
        background.set_size(global.screen_width, global.screen_height);
 | 
			
		||||
 | 
			
		||||
        // Add either the given mouseSourceActor to the ZoomRegion, or a clone of
 | 
			
		||||
        // it.
 | 
			
		||||
@@ -1105,13 +931,6 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
            this._crossHairsActor = this._crossHairs.addToZoomRegion(this, this._mouseActor);
 | 
			
		||||
        else
 | 
			
		||||
            this._crossHairsActor = null;
 | 
			
		||||
 | 
			
		||||
        // 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);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _destroyActors: function() {
 | 
			
		||||
@@ -1120,11 +939,8 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        if (this._crossHairs)
 | 
			
		||||
            this._crossHairs.removeFromParent(this._crossHairsActor);
 | 
			
		||||
 | 
			
		||||
        this._magShaderEffects.destroyEffects();
 | 
			
		||||
        this._magShaderEffects = null;
 | 
			
		||||
        this._magView.destroy();
 | 
			
		||||
        this._magView = null;
 | 
			
		||||
        this._background = null;
 | 
			
		||||
        this._uiGroupClone = null;
 | 
			
		||||
        this._mouseActor = null;
 | 
			
		||||
        this._crossHairsActor = null;
 | 
			
		||||
@@ -1175,7 +991,7 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        this._yMagFactor = params.yMagFactor;
 | 
			
		||||
 | 
			
		||||
        if (params.redoCursorTracking &&
 | 
			
		||||
            this._mouseTrackingMode != GDesktopEnums.MagnifierMouseTrackingMode.NONE) {
 | 
			
		||||
            this._mouseTrackingMode != MouseTrackingMode.NONE) {
 | 
			
		||||
            // This depends on this.xMagFactor/yMagFactor already being updated
 | 
			
		||||
            [params.xCenter, params.yCenter] = this._centerFromMousePosition();
 | 
			
		||||
        }
 | 
			
		||||
@@ -1225,7 +1041,7 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
    _isFullScreen: function() {
 | 
			
		||||
        // Does the magnified view occupy the whole screen? Note that this
 | 
			
		||||
        // doesn't necessarily imply
 | 
			
		||||
        // this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
 | 
			
		||||
        // this._screenPosition = ScreenPosition.FULL_SCREEN;
 | 
			
		||||
 | 
			
		||||
        if (this._viewPortX != 0 || this._viewPortY != 0)
 | 
			
		||||
            return false;
 | 
			
		||||
@@ -1242,13 +1058,13 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        let xMouse = this._magnifier.xMouse;
 | 
			
		||||
        let yMouse = this._magnifier.yMouse;
 | 
			
		||||
 | 
			
		||||
        if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PROPORTIONAL) {
 | 
			
		||||
        if (this._mouseTrackingMode == MouseTrackingMode.PROPORTIONAL) {
 | 
			
		||||
            return this._centerFromMouseProportional(xMouse, yMouse);
 | 
			
		||||
        }
 | 
			
		||||
        else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PUSH) {
 | 
			
		||||
        else if (this._mouseTrackingMode == MouseTrackingMode.PUSH) {
 | 
			
		||||
            return this._centerFromMousePush(xMouse, yMouse);
 | 
			
		||||
        }
 | 
			
		||||
        else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.CENTERED) {
 | 
			
		||||
        else if (this._mouseTrackingMode == MouseTrackingMode.CENTERED) {
 | 
			
		||||
            return this._centerFromMouseCentered(xMouse, yMouse);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -1325,7 +1141,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();
 | 
			
		||||
    },
 | 
			
		||||
@@ -1347,27 +1163,14 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
            this._crossHairsActor.set_position(xMagMouse - groupWidth / 2,
 | 
			
		||||
                                               yMagMouse - groupHeight / 2);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _monitorsChanged: function() {
 | 
			
		||||
        if (!this.isActive())
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._background.set_size(global.screen_width, global.screen_height);
 | 
			
		||||
 | 
			
		||||
        if (this._screenPosition == GDesktopEnums.MagnifierScreenPosition.NONE)
 | 
			
		||||
            this._setViewPort({ x: this._viewPortX,
 | 
			
		||||
                                y: this._viewPortY,
 | 
			
		||||
                                width: this._viewPortWidth,
 | 
			
		||||
                                height: this._viewPortHeight });
 | 
			
		||||
        else
 | 
			
		||||
            this.setScreenPosition(this._screenPosition);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Crosshairs = new Lang.Class({
 | 
			
		||||
    Name: 'Crosshairs',
 | 
			
		||||
function Crosshairs() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Crosshairs.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
 | 
			
		||||
        // Set the group containing the crosshairs to three times the desktop
 | 
			
		||||
@@ -1376,15 +1179,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);
 | 
			
		||||
@@ -1392,14 +1195,6 @@ const Crosshairs = new Lang.Class({
 | 
			
		||||
        this._clipSize = [0, 0];
 | 
			
		||||
        this._clones = [];
 | 
			
		||||
        this.reCenter();
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed',
 | 
			
		||||
                                   Lang.bind(this, this._monitorsChanged));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _monitorsChanged: function() {
 | 
			
		||||
        this._actor.set_size(global.screen_width * 3, global.screen_height * 3);
 | 
			
		||||
        this.reCenter();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
   /**
 | 
			
		||||
@@ -1424,7 +1219,10 @@ const Crosshairs = new Lang.Class({
 | 
			
		||||
                    crosshairsActor = new Clutter.Clone({ source: this._actor });
 | 
			
		||||
                    this._clones.push(crosshairsActor);
 | 
			
		||||
                }
 | 
			
		||||
                crosshairsActor.visible = this._actor.visible;
 | 
			
		||||
                if (this._actor.visible)
 | 
			
		||||
                    crosshairsActor.show();
 | 
			
		||||
                else
 | 
			
		||||
                    crosshairsActor.hide();
 | 
			
		||||
 | 
			
		||||
                container.add_actor(crosshairsActor);
 | 
			
		||||
                container.raise_child(magnifiedMouse, crosshairsActor);
 | 
			
		||||
@@ -1455,10 +1253,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 +1265,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;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -1626,145 +1426,4 @@ const Crosshairs = new Lang.Class({
 | 
			
		||||
        this._vertTopHair.set_position((groupWidth - thickness) / 2, top);
 | 
			
		||||
        this._vertBottomHair.set_position((groupWidth - thickness) / 2, bottom);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const MagShaderEffects = new Lang.Class({
 | 
			
		||||
    Name: 'MagShaderEffects',
 | 
			
		||||
 | 
			
		||||
    _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);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * destroyEffects:
 | 
			
		||||
     * Remove contrast and brightness effects from the magnified view, and
 | 
			
		||||
     * lose the reference to the actor they were applied to.  Don't use this
 | 
			
		||||
     * object after calling this.
 | 
			
		||||
     */
 | 
			
		||||
    destroyEffects: function() {
 | 
			
		||||
        this._magView.clear_effects();
 | 
			
		||||
        this._colorDesaturation = null;
 | 
			
		||||
        this._brightnessContrast = null;
 | 
			
		||||
        this._inverse = null;
 | 
			
		||||
        this._magView = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * setInvertLightness:
 | 
			
		||||
     * Enable/disable invert lightness effect.
 | 
			
		||||
     * @invertFlag:     Enabled flag.
 | 
			
		||||
     */
 | 
			
		||||
    setInvertLightness: function(invertFlag) {
 | 
			
		||||
        this._inverse.set_enabled(invertFlag);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getInvertLightness:
 | 
			
		||||
     * Report whether the inversion effect is enabled.
 | 
			
		||||
     * @return:     Boolean.
 | 
			
		||||
     */
 | 
			
		||||
    getInvertLightness: function() {
 | 
			
		||||
        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.
 | 
			
		||||
     * @brightness: Object containing the brightness for the red, green,
 | 
			
		||||
     *              and blue channels.  Values of 0.0 represent "standard"
 | 
			
		||||
     *              brightness (no change), whereas values less or greater than
 | 
			
		||||
     *              0.0 indicate decreased or incresaed brightness,
 | 
			
		||||
     *              respectively.
 | 
			
		||||
     */
 | 
			
		||||
    setBrightness: function(brightness) {
 | 
			
		||||
        let bRed = brightness.r;
 | 
			
		||||
        let bGreen = brightness.g;
 | 
			
		||||
        let bBlue = brightness.b;
 | 
			
		||||
        this._brightnessContrast.set_brightness_full(bRed, bGreen, bBlue);
 | 
			
		||||
 | 
			
		||||
        // Enable the effect if the brightness OR contrast change are such that
 | 
			
		||||
        // it modifies the brightness and/or contrast.
 | 
			
		||||
        let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast();
 | 
			
		||||
        this._brightnessContrast.set_enabled(
 | 
			
		||||
            (bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE ||
 | 
			
		||||
             cRed != NO_CHANGE || cGreen != NO_CHANGE || cBlue != NO_CHANGE)
 | 
			
		||||
        );
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getBrightness:
 | 
			
		||||
     * Retrieve current brightness of the magnified view.
 | 
			
		||||
     * @return: Object containing the brightness for the red, green,
 | 
			
		||||
     *          and blue channels.  Values of 0.0 represent "standard" 
 | 
			
		||||
     *          brightness (no change), whereas values less or greater than
 | 
			
		||||
     *          0.0 indicate decreased or incresaed brightness, respectively.
 | 
			
		||||
     */
 | 
			
		||||
    getBrightness: function() {
 | 
			
		||||
        let result = {};
 | 
			
		||||
        let [bRed, bGreen, bBlue] = this._brightnessContrast.get_brightness();
 | 
			
		||||
        result.r = bRed;
 | 
			
		||||
        result.g = bGreen;
 | 
			
		||||
        result.b = bBlue;
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the contrast of the magnified view.
 | 
			
		||||
     * @contrast:   Object containing the contrast for the red, green,
 | 
			
		||||
     *              and blue channels.  Values of 0.0 represent "standard"
 | 
			
		||||
     *              contrast (no change), whereas values less or greater than
 | 
			
		||||
     *              0.0 indicate decreased or incresaed contrast, respectively.
 | 
			
		||||
     */
 | 
			
		||||
    setContrast: function(contrast) {
 | 
			
		||||
        let cRed = contrast.r;
 | 
			
		||||
        let cGreen = contrast.g;
 | 
			
		||||
        let cBlue = contrast.b;
 | 
			
		||||
 | 
			
		||||
        this._brightnessContrast.set_contrast_full(cRed, cGreen, cBlue);
 | 
			
		||||
 | 
			
		||||
        // Enable the effect if the contrast OR brightness change are such that
 | 
			
		||||
        // it modifies the brightness and/or contrast.
 | 
			
		||||
        // should be able to use Clutter.color_equal(), but that complains of
 | 
			
		||||
        // a null first argument.
 | 
			
		||||
        let [bRed, bGreen, bBlue] = this._brightnessContrast.get_brightness();
 | 
			
		||||
        this._brightnessContrast.set_enabled(
 | 
			
		||||
             cRed != NO_CHANGE || cGreen != NO_CHANGE || cBlue != NO_CHANGE ||
 | 
			
		||||
             bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE
 | 
			
		||||
        );
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieve current contrast of the magnified view.
 | 
			
		||||
     * @return: Object containing the contrast for the red, green,
 | 
			
		||||
     *          and blue channels.  Values of 0.0 represent "standard"
 | 
			
		||||
     *          contrast (no change), whereas values less or greater than
 | 
			
		||||
     *          0.0 indicate decreased or incresaed contrast, respectively.
 | 
			
		||||
     */
 | 
			
		||||
    getContrast: function() {
 | 
			
		||||
        let resutl = {};
 | 
			
		||||
        let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast();
 | 
			
		||||
        result.r = cRed;
 | 
			
		||||
        result.g = cGreen;
 | 
			
		||||
        result.b = cBlue;
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||