Compare commits

..

7 Commits

Author SHA1 Message Date
d4802861ed status/keyboard: Adapt to changes in the settings schema 2012-05-15 02:24:23 +02:00
a8fa0b8146 candidatePanel: Use the Lang.Class framework 2012-05-03 13:20:01 +02:00
cf0bb62f40 candidatePanel: Remove the PopupSeparatorMenuItem copy
No reason to not use the real deal.
2012-05-03 13:20:01 +02:00
de8106967d candidatePanel: Remove hardcoded styling
The styling should be done in the CSS file.
2012-05-03 13:19:58 +02:00
1a24f061cf candidatePanel: A candidate popup for IBus input methods
This is an implementation of the org.freedesktop.IBus.Panel API which
shows a shell style popup (BoxPointer) when using an IBus input
method.

The original code is from the ibus-gjs project[1] with minor
modifications.

[1] https://github.com/fujiwarat/ibus-gjs
2012-05-03 13:19:53 +02:00
e038845458 status/keyboard: Add the input source switcher keybindings 2012-05-03 13:19:53 +02:00
b03273c765 status/keyboard: Initial port to the new input sources settings 2012-05-03 13:19:53 +02:00
271 changed files with 57544 additions and 66684 deletions

5
.gitignore vendored
View File

@ -16,7 +16,6 @@ config.log
config.status
config
configure
data/50-gnome-shell-*.xml
data/gnome-shell.desktop
data/gnome-shell.desktop.in
data/gnome-shell-extension-prefs.desktop
@ -45,14 +44,12 @@ 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,8 +62,6 @@ src/*-enum-types.[ch]
src/*-marshal.[ch]
src/Makefile
src/Makefile.in
src/calendar-server/evolution-calendar.desktop
src/calendar-server/evolution-calendar.desktop.in
src/calendar-server/org.gnome.Shell.CalendarServer.service
src/gnome-shell
src/gnome-shell-calendar-server

331
HACKING
View File

@ -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' });

View File

@ -1,11 +1,7 @@
# Point to our macro directory and pick up user flags from the environment
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
SUBDIRS = data js src browser-plugin tests po docs
if ENABLE_MAN
SUBDIRS += man
endif
SUBDIRS = data js src browser-plugin tests po man docs
EXTRA_DIST = \
.project \
@ -17,7 +13,6 @@ EXTRA_DIST = \
DIST_EXCLUDE = \
.gitignore \
gnome-shell.doap \
HACKING \
MAINTAINERS \
tools/build/*
@ -25,4 +20,4 @@ distcheck-hook:
@echo "Checking disted files against files in git"
@$(srcdir)/tools/check-for-missing.py $(srcdir) $(distdir) $(DIST_EXCLUDE)
DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc --enable-man
DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc

525
NEWS
View File

@ -1,528 +1,3 @@
3.6.3.1
=======
* Fix regression in reentrancy fix for 3.6.3 [Giovanni; #689295]
Contributors:
Giovanni Campagna
Translations:
Rafael Ferreira [pt_BR]
3.6.3
=====
* recorder: Set frame duration to fix broken video headers [Adel; #688487]
* Block cancellation of unlock dialog during drags [Giovanni; #686800]
* Honor lock-delay GSettings key [Giovanni, Matthias; #690766, #691170]
* Fix reentrancy problem causing the tray to disappear [Giovanni; #683986]
Contributors:
Giovanni Campagna, Matthias Clasen, Adel Gadllah,
Translations:
Mattias Põldaru [et], Sandeep Sheshrao Shedmake [mr], Wouter Bolsterlee [nl],
Rajesh Ranjan [hi], Nilamdyuti Goswami [as], Ani Peter [ml],
Andika Triwidada [id], Krishnababu Krothapalli [te], Shankar Prasad [kn],
Dr.T.Vasudevan [ta], Runa Bhattacharjee [bn_IN], Rafael Ferreira [pt_BR],
Balázs Úr [hu], Gheyret Kenji [ug], Yasumichi Akahoshi [ja], Yoji TOYODA [ja],
Alexandre Franke [fr]
3.6.2
=====
* Implement org.gnome.ScreenSaver.GetActiveTime [Giovanni; #686064]
* Explicitly load gnome-screensaver when not running GDM [Tim; #683060]
* Set Empathy as preferred handler when delegating channels [Xavier; #686296]
* Tweak screenShield animations [Rui; #686745]
* Restore Fittsability of summary items in message tray [Florian; #686474]
* App search: Match GenericName too [Matthias; #687121]
* Add setting to force log out menuitem [Matthias; #686057]
* Fix auto-scroll to bottom in chat notifications [Sjoerd; #686571]
* Update man page [Matthias; #680601]
* Hide notifications when closed button is clicked [Jasper, Florian; #682237]
* Fix screenshots on Big Endian [Owen; #685915]
* Implement 'disable-user-list' in login screen [Ray; #660660]
* Misc bugfixes [Jasper, Florian, Giovanni, Matthew, Matthias, Alejandro,
Sebastian; #686233, #686484, #686487, #686630, #686728, #686574, #686574,
#686574, #686879, #686763, #685239, #687189, #686736, #686583, #687132,
#687958, #687020, #687708, #686888, #686530, #684810, #688181]
Contributors:
Matthew Barnes, Giovanni Campagna, Xavier Claessens, Matthias Clasen,
Sebastian Keller, Tim Lunn, Rui Matos, Florian Müllner, Alejandro Piñeiro,
Sjoerd Simons, Jasper St. Pierre, Ray Strode, Owen Taylor
Translations:
Takayuki KUSANO [ja], Dušan Kazik [sk], Mattias Põldaru [et],
Tom Tryfonidis [el], Sweta Kothari [gu], Matej Urbančič [sl], Piotr Drąg [pl],
Fran Diéguez [gl], Aurimas Černius [lt], Kjartan Maraas [nb],
Ihar Hrachyshka [be], Мирослав Николић [sr, sr@latin], Gil Forcada [ca],
A S Alam [pa], Yuri Myasoedov [ru], Marek Černocký [cs], Sweta Kothari [gu],
Wolfgang Stöggl [de], Daniel Mustieles [es]
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

View File

@ -41,7 +41,7 @@
"It can be used only by extensions.gnome.org"
#define PLUGIN_MIME_STRING "application/x-gnome-shell-integration::Gnome Shell Integration Dummy Content-Type";
#define PLUGIN_API_VERSION 5
#define PLUGIN_API_VERSION 3
typedef struct {
GDBusProxy *proxy;
@ -163,7 +163,6 @@ NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin)
plugin->newp = NPP_New;
plugin->destroy = NPP_Destroy;
plugin->getvalue = NPP_GetValue;
plugin->setwindow = NPP_SetWindow;
return NPERR_NO_ERROR;
}
@ -225,7 +224,7 @@ NPP_New(NPMIMEType mimetype,
NULL, /* interface info */
"org.gnome.Shell",
"/org/gnome/Shell",
"org.gnome.Shell.Extensions",
"org.gnome.Shell",
NULL, /* GCancellable */
&error);
if (!data->proxy)
@ -268,7 +267,6 @@ typedef struct {
NPObject parent;
NPP instance;
GDBusProxy *proxy;
GSettings *settings;
NPObject *listener;
NPObject *restart_listener;
gint signal_id;
@ -325,9 +323,6 @@ on_shell_appeared (GDBusConnection *connection,
}
}
#define SHELL_SCHEMA "org.gnome.shell"
#define ENABLED_EXTENSIONS_KEY "enabled-extensions"
static NPObject *
plugin_object_allocate (NPP instance,
NPClass *klass)
@ -337,7 +332,6 @@ plugin_object_allocate (NPP instance,
obj->instance = instance;
obj->proxy = g_object_ref (data->proxy);
obj->settings = g_settings_new (SHELL_SCHEMA);
obj->signal_id = g_signal_connect (obj->proxy, "g-signal",
G_CALLBACK (on_shell_signal), obj);
@ -373,14 +367,39 @@ plugin_object_deallocate (NPObject *npobj)
g_slice_free (PluginObject, obj);
}
static NPIdentifier api_version_id;
static NPIdentifier shell_version_id;
static NPIdentifier get_info_id;
static NPIdentifier list_extensions_id;
static NPIdentifier enable_extension_id;
static NPIdentifier install_extension_id;
static NPIdentifier uninstall_extension_id;
static NPIdentifier onextension_changed_id;
static NPIdentifier onrestart_id;
static NPIdentifier get_errors_id;
static NPIdentifier launch_extension_prefs_id;
static bool
plugin_object_has_method (NPObject *npobj,
NPIdentifier name)
{
return (name == get_info_id ||
name == list_extensions_id ||
name == enable_extension_id ||
name == install_extension_id ||
name == uninstall_extension_id ||
name == get_errors_id ||
name == launch_extension_prefs_id);
}
static inline gboolean
uuid_is_valid (NPString string)
uuid_is_valid (const gchar *uuid)
{
gsize i;
for (i = 0; i < string.UTF8Length; i++)
for (i = 0; uuid[i]; i ++)
{
gchar c = string.UTF8Characters[i];
gchar c = uuid[i];
if (c < 32 || c >= 127)
return FALSE;
@ -444,73 +463,8 @@ jsonify_variant (GVariant *variant,
}
static gboolean
parse_args (const gchar *format_str,
uint32_t argc,
const NPVariant *argv,
...)
{
va_list args;
gsize i;
gboolean ret = FALSE;
if (strlen (format_str) != argc)
return FALSE;
va_start (args, argv);
for (i = 0; format_str[i]; i++)
{
gpointer arg_location;
const NPVariant arg = argv[i];
arg_location = va_arg (args, gpointer);
switch (format_str[i])
{
case 'u':
{
NPString string;
if (!NPVARIANT_IS_STRING (arg))
goto out;
string = NPVARIANT_TO_STRING (arg);
if (!uuid_is_valid (string))
goto out;
*(gchar **) arg_location = g_strndup (string.UTF8Characters, string.UTF8Length);
}
break;
case 'b':
if (!NPVARIANT_IS_BOOLEAN (arg))
goto out;
*(gboolean *) arg_location = NPVARIANT_TO_BOOLEAN (arg);
break;
case 'o':
if (!NPVARIANT_IS_OBJECT (arg))
goto out;
*(NPObject **) arg_location = NPVARIANT_TO_OBJECT (arg);
}
}
ret = TRUE;
out:
va_end (args);
return ret;
}
static gboolean
plugin_list_extensions (PluginObject *obj,
uint32_t argc,
const NPVariant *args,
NPVariant *result)
plugin_list_extensions (PluginObject *obj,
NPVariant *result)
{
GError *error = NULL;
GVariant *res;
@ -534,159 +488,91 @@ plugin_list_extensions (PluginObject *obj,
}
static gboolean
plugin_enable_extension (PluginObject *obj,
uint32_t argc,
const NPVariant *argv,
NPVariant *result)
plugin_enable_extension (PluginObject *obj,
NPString uuid,
gboolean enabled)
{
gboolean ret;
gchar *uuid;
gboolean enabled;
gsize length;
gchar **uuids;
const gchar **new_uuids;
if (!parse_args ("ub", argc, argv, &uuid, &enabled))
return FALSE;
uuids = g_settings_get_strv (obj->settings, ENABLED_EXTENSIONS_KEY);
length = g_strv_length (uuids);
if (enabled)
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
if (!uuid_is_valid (uuid_str))
{
new_uuids = g_new (const gchar *, length + 2); /* New key, NULL */
memcpy (new_uuids, uuids, length * sizeof (*new_uuids));
new_uuids[length] = uuid;
new_uuids[length + 1] = NULL;
g_free (uuid_str);
return FALSE;
}
else
{
gsize i = 0, j = 0;
new_uuids = g_new (const gchar *, length);
for (i = 0; i < length; i ++)
{
if (g_str_equal (uuids[i], uuid))
continue;
new_uuids[j] = uuids[i];
j++;
}
new_uuids[j] = NULL;
}
ret = g_settings_set_strv (obj->settings,
ENABLED_EXTENSIONS_KEY,
new_uuids);
g_strfreev (uuids);
g_free (new_uuids);
g_free (uuid);
return ret;
}
typedef struct _AsyncClosure AsyncClosure;
struct _AsyncClosure {
PluginObject *obj;
NPObject *callback;
NPObject *errback;
};
static void
install_extension_cb (GObject *proxy,
GAsyncResult *async_res,
gpointer user_data)
{
AsyncClosure *async_closure = (AsyncClosure *) user_data;
GError *error = NULL;
GVariant *res;
NPVariant args[1];
NPVariant result = { NPVariantType_Void };
NPObject *callback;
res = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), async_res, &error);
if (res == NULL)
{
if (g_dbus_error_is_remote_error (error))
g_dbus_error_strip_remote_error (error);
STRINGZ_TO_NPVARIANT (error->message, args[0]);
callback = async_closure->errback;
}
else
{
char *string_result;
g_variant_get (res, "(&s)", &string_result);
STRINGZ_TO_NPVARIANT (string_result, args[0]);
callback = async_closure->callback;
}
funcs.invokeDefault (async_closure->obj->instance,
callback, args, 1, &result);
funcs.releasevariantvalue (&result);
funcs.releaseobject (async_closure->callback);
funcs.releaseobject (async_closure->errback);
g_slice_free (AsyncClosure, async_closure);
}
static gboolean
plugin_install_extension (PluginObject *obj,
uint32_t argc,
const NPVariant *argv,
NPVariant *result)
{
gchar *uuid;
NPObject *callback, *errback;
AsyncClosure *async_closure;
if (!parse_args ("uoo", argc, argv, &uuid, &callback, &errback))
return FALSE;
async_closure = g_slice_new (AsyncClosure);
async_closure->obj = obj;
async_closure->callback = funcs.retainobject (callback);
async_closure->errback = funcs.retainobject (errback);
g_dbus_proxy_call (obj->proxy,
"InstallRemoteExtension",
g_variant_new ("(s)", uuid),
(enabled ? "EnableExtension" : "DisableExtension"),
g_variant_new ("(s)", uuid_str),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
install_extension_cb,
async_closure);
NULL, /* callback */
NULL /* user_data */);
g_free (uuid);
g_free (uuid_str);
return TRUE;
}
static gboolean
plugin_uninstall_extension (PluginObject *obj,
uint32_t argc,
const NPVariant *argv,
NPVariant *result)
plugin_install_extension (PluginObject *obj,
NPString uuid,
NPString version_tag)
{
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
gchar *version_tag_str;
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
version_tag_str = g_strndup (version_tag.UTF8Characters,
version_tag.UTF8Length);
g_dbus_proxy_call (obj->proxy,
"InstallRemoteExtension",
g_variant_new ("(ss)",
uuid_str,
version_tag_str),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
NULL, /* callback */
NULL /* user_data */);
g_free (uuid_str);
g_free (version_tag_str);
return TRUE;
}
static gboolean
plugin_uninstall_extension (PluginObject *obj,
NPString uuid,
NPVariant *result)
{
GError *error = NULL;
GVariant *res;
gchar *uuid;
gchar *uuid_str;
if (!parse_args ("u", argc, argv, &uuid))
return FALSE;
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
res = g_dbus_proxy_call_sync (obj->proxy,
"UninstallExtension",
g_variant_new ("(s)", uuid),
g_variant_new ("(s)",
uuid_str),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
&error);
g_free (uuid);
g_free (uuid_str);
if (!res)
{
@ -699,27 +585,30 @@ plugin_uninstall_extension (PluginObject *obj,
}
static gboolean
plugin_get_info (PluginObject *obj,
uint32_t argc,
const NPVariant *argv,
NPVariant *result)
plugin_get_info (PluginObject *obj,
NPString uuid,
NPVariant *result)
{
GError *error = NULL;
GVariant *res;
gchar *uuid;
gchar *uuid_str;
if (!parse_args ("u", argc, argv, &uuid))
return FALSE;
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
res = g_dbus_proxy_call_sync (obj->proxy,
"GetExtensionInfo",
g_variant_new ("(s)", uuid),
g_variant_new ("(s)", uuid_str),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
&error);
g_free (uuid);
g_free (uuid_str);
if (!res)
{
@ -732,26 +621,31 @@ plugin_get_info (PluginObject *obj,
}
static gboolean
plugin_get_errors (PluginObject *obj,
uint32_t argc,
const NPVariant *argv,
NPVariant *result)
plugin_get_errors (PluginObject *obj,
NPString uuid,
NPVariant *result)
{
GError *error = NULL;
GVariant *res;
gchar *uuid;
gchar *uuid_str;
if (!parse_args ("u", argc, argv, &uuid))
return FALSE;
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
res = g_dbus_proxy_call_sync (obj->proxy,
"GetExtensionErrors",
g_variant_new ("(s)", uuid),
g_variant_new ("(s)", uuid_str),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
&error);
g_free (uuid_str);
if (!res)
{
g_warning ("Failed to retrieve errors: %s", error->message);
@ -763,25 +657,29 @@ plugin_get_errors (PluginObject *obj,
}
static gboolean
plugin_launch_extension_prefs (PluginObject *obj,
uint32_t argc,
const NPVariant *argv,
NPVariant *result)
plugin_launch_extension_prefs (PluginObject *obj,
NPString uuid,
NPVariant *result)
{
gchar *uuid;
gchar *uuid_str;
if (!parse_args ("u", argc, argv, &uuid))
return FALSE;
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
g_dbus_proxy_call (obj->proxy,
"LaunchExtensionPrefs",
g_variant_new ("(s)", uuid),
g_variant_new ("(s)", uuid_str),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
NULL, /* callback */
NULL /* user_data */);
g_free (uuid_str);
return TRUE;
}
@ -835,40 +733,10 @@ plugin_get_shell_version (PluginObject *obj,
return ret;
}
#define METHODS \
METHOD (list_extensions) \
METHOD (get_info) \
METHOD (enable_extension) \
METHOD (install_extension) \
METHOD (uninstall_extension) \
METHOD (get_errors) \
METHOD (launch_extension_prefs) \
/* */
#define METHOD(x) \
static NPIdentifier x##_id;
METHODS
#undef METHOD
static NPIdentifier api_version_id;
static NPIdentifier shell_version_id;
static NPIdentifier onextension_changed_id;
static NPIdentifier onrestart_id;
static bool
plugin_object_has_method (NPObject *npobj,
NPIdentifier name)
{
#define METHOD(x) (name == (x##_id)) ||
/* expands to (name == list_extensions_id) || FALSE; */
return METHODS FALSE;
#undef METHOD
}
static bool
plugin_object_invoke (NPObject *npobj,
NPIdentifier name,
const NPVariant *argv,
const NPVariant *args,
uint32_t argc,
NPVariant *result)
{
@ -880,13 +748,61 @@ plugin_object_invoke (NPObject *npobj,
VOID_TO_NPVARIANT (*result);
#define METHOD(x) \
if (name == x##_id) \
return plugin_##x (obj, argc, argv, result);
METHODS
#undef METHOD
if (!plugin_object_has_method (npobj, name))
return FALSE;
return FALSE;
if (name == list_extensions_id)
return plugin_list_extensions (obj, result);
else if (name == get_info_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
return plugin_get_info (obj, NPVARIANT_TO_STRING(args[0]), result);
}
else if (name == enable_extension_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
if (!NPVARIANT_IS_BOOLEAN(args[1])) return FALSE;
return plugin_enable_extension (obj,
NPVARIANT_TO_STRING(args[0]),
NPVARIANT_TO_BOOLEAN(args[1]));
}
else if (name == install_extension_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
if (!NPVARIANT_IS_STRING(args[1])) return FALSE;
return plugin_install_extension (obj,
NPVARIANT_TO_STRING(args[0]),
NPVARIANT_TO_STRING(args[1]));
}
else if (name == uninstall_extension_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
return plugin_uninstall_extension (obj,
NPVARIANT_TO_STRING(args[0]),
result);
}
else if (name == get_errors_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
return plugin_get_errors (obj,
NPVARIANT_TO_STRING(args[0]),
result);
}
else if (name == launch_extension_prefs_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
return plugin_launch_extension_prefs (obj,
NPVARIANT_TO_STRING(args[0]),
result);
}
return TRUE;
}
static bool
@ -1030,12 +946,3 @@ NPP_GetValue(NPP instance,
return NPERR_NO_ERROR;
}
/* Opera tries to call NPP_SetWindow without checking the
* NULL pointer beforehand. */
NPError
NPP_SetWindow(NPP instance,
NPWindow *window)
{
return NPERR_NO_ERROR;
}

View File

@ -1,5 +1,5 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.6.3.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_INIT([gnome-shell],[3.4.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c])
@ -36,8 +36,6 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
PKG_PROG_PKG_CONFIG([0.22])
AC_PATH_PROG([XSLTPROC], [xsltproc])
GLIB_GSETTINGS
# Get a value to substitute into gnome-shell.in
@ -46,15 +44,15 @@ AC_SUBST(PYTHON)
# We need at least this, since gst_plugin_register_static() was added
# in 0.10.16, but nothing older than 0.10.21 has been tested.
GSTREAMER_MIN_VERSION=0.11.92
GSTREAMER_MIN_VERSION=0.10.16
recorder_modules=
build_recorder=false
AC_MSG_CHECKING([for GStreamer (needed for recording functionality)])
if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then
if $PKG_CONFIG --exists gstreamer-0.10 '>=' $GSTREAMER_MIN_VERSION ; then
AC_MSG_RESULT(yes)
build_recorder=true
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11"
recorder_modules="gstreamer-0.10 gstreamer-base-0.10 x11"
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
else
AC_MSG_RESULT(no)
@ -62,32 +60,30 @@ fi
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.11.11
CLUTTER_MIN_VERSION=1.9.16
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.33.2
MUTTER_MIN_VERSION=3.6.2
GJS_MIN_VERSION=1.29.18
MUTTER_MIN_VERSION=3.4.1
FOLKS_MIN_VERSION=0.5.2
GTK_MIN_VERSION=3.3.9
GIO_MIN_VERSION=2.31.6
LIBECAL_MIN_VERSION=3.5.3
LIBEDATASERVER_MIN_VERSION=3.5.3
LIBEDATASERVERUI_MIN_VERSION=3.5.3
LIBECAL_MIN_VERSION=2.32.0
LIBEDATASERVER_MIN_VERSION=1.2.0
LIBEDATASERVERUI_MIN_VERSION=2.91.6
TELEPATHY_GLIB_MIN_VERSION=0.17.5
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11
GCR_MIN_VERSION=3.3.90
GNOME_DESKTOP_REQUIRED_VERSION=3.5.1
GNOME_MENUS_REQUIRED_VERSION=3.5.3
# Collect more than 20 libraries for a prize!
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
libxml-2.0
gtk+-3.0 >= $GTK_MIN_VERSION
atk-bridge-2.0
folks >= $FOLKS_MIN_VERSION
libmutter >= $MUTTER_MIN_VERSION
gjs-internals-1.0 >= $GJS_MIN_VERSION
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
$recorder_modules
libgnome-menu-3.0 $recorder_modules
gdk-x11-3.0 libsoup-2.4
gl
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
@ -99,8 +95,7 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
libnm-glib libnm-util gnome-keyring-1
gcr-3 >= $GCR_MIN_VERSION
gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION)
gcr-3 >= $GCR_MIN_VERSION)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
@ -108,8 +103,9 @@ PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
GJS_VERSION=`$PKG_CONFIG --modversion gjs-internals-1.0`
AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to])
AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
@ -125,7 +121,7 @@ PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.2 x11)
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.5.4)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 0.1.7)
AC_MSG_CHECKING([for bluetooth support])
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
@ -141,7 +137,7 @@ PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
AC_SUBST([HAVE_BLUETOOTH],[0])
AC_MSG_RESULT([no])])
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION gio-2.0)
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION libedataserverui-3.0 >= $LIBEDATASERVERUI_MIN_VERSION gio-2.0)
AC_SUBST(CALENDAR_SERVER_CFLAGS)
AC_SUBST(CALENDAR_SERVER_LIBS)
@ -210,18 +206,6 @@ AC_SUBST(TYPELIBDIR)
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
AC_ARG_ENABLE(man,
[AS_HELP_STRING([--enable-man],
[generate man pages [default=yes]])],,
enable_man=yes)
if test "$enable_man" != no; then
AC_PATH_PROG([XSLTPROC], [xsltproc])
if test -z "$XSLTPROC"; then
AC_MSG_ERROR([xsltproc is required for --enable-man])
fi
fi
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
# Stay command-line compatible with the gnome-common configure option. Here
# minimum/yes/maximum are the same, however.
AC_ARG_ENABLE(compile_warnings,
@ -253,6 +237,31 @@ AC_ARG_ENABLE(jhbuild-wrapper-script,
AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
AM_CONDITIONAL(USE_JHBUILD_WRAPPER_SCRIPT, test "x$enable_jhbuild_wrapper_script" = xyes)
AC_MSG_CHECKING([location of system Certificate Authority list])
AC_ARG_WITH(ca-certificates,
[AC_HELP_STRING([--with-ca-certificates=@<:@path@:>@],
[path to system Certificate Authority list])])
if test "$with_ca_certificates" = "no"; then
AC_MSG_RESULT([disabled])
else
if test -z "$with_ca_certificates"; then
for f in /etc/pki/tls/certs/ca-bundle.crt \
/etc/ssl/certs/ca-certificates.crt; do
if test -f "$f"; then
with_ca_certificates="$f"
fi
done
if test -z "$with_ca_certificates"; then
AC_MSG_ERROR([could not find. Use --with-ca-certificates=path to set, or --without-ca-certificates to disable])
fi
fi
AC_MSG_RESULT($with_ca_certificates)
AC_DEFINE_UNQUOTED(SHELL_SYSTEM_CA_FILE, ["$with_ca_certificates"], [The system TLS CA list])
fi
AC_SUBST(SHELL_SYSTEM_CA_FILE,["$with_ca_certificates"])
BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}"
AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to])
@ -266,7 +275,6 @@ AC_CONFIG_FILES([
docs/reference/st/Makefile
docs/reference/st/st-docs.sgml
js/Makefile
src/calendar-server/evolution-calendar.desktop.in
src/Makefile
browser-plugin/Makefile
tests/Makefile

View File

@ -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>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<KeyListEntries schema="org.gnome.shell.keybindings"
group="system"
_name="System"
wm_name="GNOME Shell"
package="gnome-shell">
<KeyListEntry name="toggle-message-tray"
_description="Show the message tray"/>
</KeyListEntries>

View File

@ -8,7 +8,14 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
-e "s|@VERSION[@]|$(VERSION)|" \
$< > $@ || rm $@
@INTLTOOL_DESKTOP_RULE@
# Placeholder until we add intltool
%.desktop:%.desktop.in
$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@
searchprovidersdir = $(pkgdatadir)/open-search-providers
dist_searchproviders_DATA = \
open-search-providers/google.xml \
open-search-providers/wikipedia.xml
introspectiondir = $(datadir)/dbus-1/interfaces
introspection_DATA = org.gnome.ShellSearchProvider.xml
@ -29,39 +36,26 @@ dist_theme_DATA = \
theme/dash-placeholder.svg \
theme/filter-selected-ltr.svg \
theme/filter-selected-rtl.svg \
theme/gdm.css \
theme/gnome-shell.css \
theme/logged-in-indicator.svg \
theme/message-tray-background.png \
theme/noise-texture.png \
theme/panel-button-border.svg \
theme/panel-button-highlight-narrow.svg \
theme/panel-button-highlight-wide.svg \
theme/process-working.svg \
theme/running-indicator.svg \
theme/scroll-hhandle.svg \
theme/scroll-vhandle.svg \
theme/source-button-border.svg \
theme/summary-counter.svg \
theme/toggle-off-us.svg \
theme/toggle-off-intl.svg \
theme/toggle-on-us.svg \
theme/toggle-on-intl.svg \
theme/ws-switch-arrow-up.png \
theme/ws-switch-arrow-down.png
theme/ws-switch-arrow-up.svg \
theme/ws-switch-arrow-down.svg
keysdir = @GNOME_KEYBINDINGS_KEYSDIR@
keys_in_files = \
50-gnome-shell-screenshot.xml.in \
50-gnome-shell-system.xml.in \
$(NULL)
keys_DATA = $(keys_in_files:.xml.in=.xml)
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
gsettings_SCHEMAS = org.gnome.shell.gschema.xml org.gnome.shell.evolution.calendar.gschema.xml
@INTLTOOL_XML_NOMERGE_RULE@
%.gschema.xml.in: %.gschema.xml.in.in Makefile
$(AM_V_GEN) sed -e 's|@GETTEXT_PACKAGE[@]|$(GETTEXT_PACKAGE)|g' \
$< > $@ || rm $@
@GSETTINGS_RULES@
# We need to compile schemas at make time
@ -74,21 +68,25 @@ all-local: gschemas.compiled
convertdir = $(datadir)/GConf/gsettings
convert_DATA = gnome-shell-overrides.convert
shadersdir = $(pkgdatadir)/shaders
shaders_DATA = \
shaders/dim-window.glsl
EXTRA_DIST = \
gnome-shell.desktop.in.in \
gnome-shell-extension-prefs.desktop.in.in \
$(introspection_DATA) \
$(menu_DATA) \
$(shaders_DATA) \
$(convert_DATA) \
$(keys_in_files) \
org.gnome.shell.gschema.xml.in.in
org.gnome.shell.evolution.calendar.gschema.xml.in \
org.gnome.shell.gschema.xml.in
CLEANFILES = \
gnome-shell.desktop.in \
gnome-shell-extension-prefs.in \
$(desktop_DATA) \
$(keys_DATA) \
$(gsettings_SCHEMAS) \
gschemas.compiled \
org.gnome.shell.gschema.valid \
org.gnome.shell.gschema.xml.in
gschemas.compiled

View 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>

View 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>

View File

@ -108,7 +108,7 @@
</doc:summary>
</doc:doc>
</arg>
<arg type="aa{sv}" direction="out">
<arg type="a{sv}" direction="out">
<doc:doc>
<doc:summary>
<doc:para>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- NOTE: This schema is a GNOME 3.4 workaround - it uses the same path
as org.gnome.evolution.calendar, but avoids us requiring Evolution
be installed. In GNOME 3.6 the selected state will become a flag
on the calendar. Because the translations are in Evolution,
this is untranslated and in POTFILES.skip.
-->
<schemalist>
<schema path="/org/gnome/evolution/calendar/" id="org.gnome.shell.evolution.calendar" gettext-domain="evolution">
<key type="as" name="selected-calendars">
<default>[]</default>
<summary>List of selected calendars</summary>
<description>List of calendars to load</description>
</key>
<key type="as" name="selected-tasks">
<default>[]</default>
<summary>List of selected task lists</summary>
<description>List of task lists to load</description>
</key>
</schema>
</schemalist>

View File

@ -39,6 +39,10 @@
will be displayed in the favorites area.
</_description>
</key>
<key name="disabled-open-search-providers" type="as">
<default>[]</default>
<_summary>disabled OpenSearch providers</_summary>
</key>
<key name="command-history" type="as">
<default>[]</default>
<_summary>History for command (Alt-F2) dialog</_summary>
@ -57,14 +61,7 @@ value here is from the TpConnectionPresenceType enumeration.</_summary>
<_summary>Internally used to store the last session presence status for the user. The
value here is from the GsmPresenceStatus enumeration.</_summary>
</key>
<key name="always-show-log-out" type="b">
<default>false</default>
<_summary>Always show the 'Log out' menuitem in the user menu.</_summary>
<_description>
This key overrides the automatic hiding of the 'Log out'
menuitem in single-user, single-session situations.
</_description>
</key>
<child name="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"/>
@ -91,13 +88,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
Keybinding to open the application menu.
</_description>
</key>
<key name="toggle-message-tray" type="as">
<default>["&lt;Super&gt;m"]</default>
<_summary>Keybinding to toggle the visibility of the message tray</_summary>
<_description>
Keybinding to toggle the visibility of the message tray.
</_description>
</key>
<key name="toggle-recording" type="as">
<default><![CDATA[['<Control><Shift><Alt>r']]]></default>
<_summary>Keybinding to toggle the screen recorder</_summary>
@ -118,6 +108,24 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
</key>
</schema>
<schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="show-seconds" type="b">
<default>false</default>
<_summary>Show time with seconds</_summary>
<_description>
If true, display seconds in time.
</_description>
</key>
<key name="show-date" type="b">
<default>false</default>
<_summary>Show date in clock</_summary>
<_description>
If true, display date in the clock, in addition to time.
</_description>
</key>
</schema>
<schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="framerate" type="i">
@ -140,7 +148,7 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
take care of its own output - this might be used to send the output
to an icecast server via shout2send or similar. When unset or set
to an empty value, the default pipeline will be used. This is currently
'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux'
'vp8enc quality=8 speed=6 threads=%T ! queue ! webmmux'
and records to WEBM using the VP8 codec. %T is used as a placeholder
for a guess at the optimal thread count on the system.
</_description>
@ -198,13 +206,5 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
This key overrides the key in org.gnome.mutter when running GNOME Shell.
</description>
</key>
<key name="focus-change-on-pointer-rest" type="b">
<default>true</default>
<summary>Delay focus changes in mouse mode until the pointer stops moving</summary>
<description>
This key overrides the key in org.gnome.mutter when running GNOME Shell.
</description>
</key>
</schema>
</schemalist>

View 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;
}

View File

@ -10,11 +10,11 @@
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="29"
height="29"
width="28"
height="25"
id="svg10621"
version="1.1"
inkscape:version="0.48.2 r9819"
inkscape:version="0.48.1 r9760"
sodipodi:docname="calendar-today.svg">
<defs
id="defs10623">
@ -118,17 +118,6 @@
fx="51"
fy="30"
r="42" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient3113"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
</defs>
<sodipodi:namedview
id="base"
@ -138,29 +127,20 @@
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="15.839192"
inkscape:cx="20.652108"
inkscape:cy="11.839084"
inkscape:cx="8.3750933"
inkscape:cy="8.0837211"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="1280"
inkscape:window-height="741"
inkscape:window-width="1440"
inkscape:window-height="843"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
borderlayer="true">
<inkscape:grid
type="xygrid"
id="grid3109"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
inkscape:window-y="26"
inkscape:window-maximized="1" />
<metadata
id="metadata10626">
<rdf:RDF>
@ -177,28 +157,31 @@
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-469.08263,-532.99307)">
<path
sodipodi:type="arc"
style="opacity:0.4625;color:#000000;fill:url(#radialGradient3113);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="path34506-3"
sodipodi:cx="51"
sodipodi:cy="30"
sodipodi:rx="42"
sodipodi:ry="16"
d="M 9,29.999999 A 42,16 0 0 1 93,30 l -42,0 z"
sodipodi:start="3.1415927"
sodipodi:end="6.2831853"
transform="matrix(0.43692393,0,0,1.3783114,461.29951,517.6437)"
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<rect
style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
id="rect2996"
width="31"
height="3"
x="468.08264"
y="558.99304" />
transform="translate(-469.08263,-536.99307)">
<g
id="g3003">
<path
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
transform="matrix(0.43692393,0,0,1.3783114,460.60467,517.48289)"
sodipodi:end="6.2831853"
sodipodi:start="3.1415927"
d="M 9,29.999999 C 9.0000011,21.163443 27.804042,14 51.000002,14 74.195961,14 93,21.163444 93,30 l -42,0 z"
sodipodi:ry="16"
sodipodi:rx="42"
sodipodi:cy="30"
sodipodi:cx="51"
id="path34506-3"
style="opacity:0.4625;color:#000000;fill:url(#radialGradient2997);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
sodipodi:type="arc" />
<rect
y="558.85046"
x="468.96878"
height="3.1425927"
width="28.149134"
id="rect2996"
style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

180
data/theme/gdm.css Normal file
View 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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

View File

@ -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

View 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

View 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

View File

@ -1,120 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.0"
id="Foreground"
x="0px"
y="0px"
width="32"
height="32"
viewBox="0 0 23.272727 23.272727"
enable-background="new 0 0 16 16"
xml:space="preserve"
sodipodi:version="0.32"
inkscape:version="0.48.2 r9819"
sodipodi:docname="summary-counter.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
id="metadata2399"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs2397"><linearGradient
id="linearGradient3173"><stop
style="stop-color:#c4c4c4;stop-opacity:1;"
offset="0"
id="stop3175" /><stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop3177" /></linearGradient><inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 8 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="16 : 8 : 1"
inkscape:persp3d-origin="8 : 5.3333333 : 1"
id="perspective2401" /><filter
color-interpolation-filters="sRGB"
inkscape:collect="always"
id="filter16494-4"
x="-0.20989846"
width="1.4197969"
y="-0.20903821"
height="1.4180764"><feGaussianBlur
inkscape:collect="always"
stdDeviation="1.3282637"
id="feGaussianBlur16496-8" /></filter><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient16498-6"
id="radialGradient16504-1"
cx="7.6582627"
cy="5.8191104"
fx="7.6582627"
fy="5.8191104"
r="8.6928644"
gradientTransform="matrix(1.0474339,0,0,1.0517402,-0.3632615,-0.42032492)"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
id="linearGradient16498-6"><stop
style="stop-color:#9FD0FF;stop-opacity:1"
offset="0"
id="stop16500-8" /><stop
style="stop-color:#3465A4;stop-opacity:1"
offset="1"
id="stop16502-0" /></linearGradient></defs><sodipodi:namedview
inkscape:window-height="709"
inkscape:window-width="1366"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10.0"
gridtolerance="10.0"
objecttolerance="10.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#000000"
id="base"
showgrid="false"
inkscape:zoom="11.313708"
inkscape:cx="15.386407"
inkscape:cy="13.739577"
inkscape:window-x="0"
inkscape:window-y="1179"
inkscape:current-layer="g16402-8"
showguides="true"
inkscape:guide-bbox="true"
borderlayer="true"
inkscape:showpageshadow="false"
inkscape:window-maximized="1"><inkscape:grid
type="xygrid"
id="grid11246"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" /></sodipodi:namedview><g
style="display:inline"
id="g16402-8"
transform="translate(4.7533483,2.8238929)"><g
id="g3175-4"
transform="translate(-0.89995416,0.94028614)"><path
sodipodi:type="inkscape:offset"
inkscape:radius="0"
inkscape:original="M 7.65625 0.125 C 3.2589349 0.125 -0.3125 3.7070002 -0.3125 8.125 C -0.3125 12.543001 3.2589349 16.125 7.65625 16.125 C 12.053566 16.125 15.625 12.543001 15.625 8.125 C 15.625 3.7070002 12.053566 0.125 7.65625 0.125 z "
xlink:href="#path2394-32"
style="opacity:0.52994014;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.18181825;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter16494-4);enable-background:accumulate"
id="path16480-5"
inkscape:href="#path2394-32"
d="m 7.65625,0.125 c -4.3973151,0 -7.96875,3.5820002 -7.96875,8 0,4.418001 3.5714349,8 7.96875,8 4.397316,0 7.96875,-3.581999 7.96875,-8 0,-4.4179998 -3.571434,-8 -7.96875,-8 z"
transform="translate(0,1.028519)" /><path
clip-rule="evenodd"
d="m -0.30428257,8.1237596 c 0,-4.4179998 3.56522987,-7.9999996 7.96254497,-7.9999996 4.3973156,0 7.9625456,3.5819998 7.9625456,7.9999996 0,4.4180014 -3.56523,8.0000004 -7.9625456,8.0000004 -4.3973151,0 -7.96254497,-3.581999 -7.96254497,-8.0000004 z"
id="path2394-32"
style="color:#000000;fill:url(#radialGradient16504-1);fill-opacity:1;fill-rule:nonzero;stroke:#eeeeec;stroke-width:1.4545455;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
sodipodi:nodetypes="csssc"
inkscape:connector-curvature="0" /><g
id="g3172-6" /></g></g></svg>

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 850 B

View File

@ -0,0 +1,376 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="96"
height="96"
id="svg25070"
inkscape:version="0.48.0 r9654"
sodipodi:docname="ws-switch-arrow-down.svg">
<metadata
id="metadata3353">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="718"
inkscape:window-height="480"
id="namedview3351"
showgrid="false"
inkscape:zoom="2.6979167"
inkscape:cx="48"
inkscape:cy="48"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="0"
inkscape:current-layer="svg25070" />
<defs
id="defs25072">
<linearGradient
x1="-86.552246"
y1="185.439"
x2="-83.37072"
y2="197.31261"
id="linearGradient24957"
xlink:href="#linearGradient4034-0-4"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(6,0)" />
<linearGradient
id="linearGradient4034-0-4">
<stop
id="stop4036-5-7"
style="stop-color:#eeeeec;stop-opacity:1"
offset="0" />
<stop
id="stop4038-9-6"
style="stop-color:#babdb6;stop-opacity:1"
offset="1" />
</linearGradient>
<filter
x="0"
y="0"
width="1"
height="1"
color-interpolation-filters="sRGB"
id="filter24765">
<feColorMatrix
result="fbSourceGraphic"
values="1"
type="saturate"
id="feColorMatrix24767" />
<feColorMatrix
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
in="fbSourceGraphic"
id="feColorMatrix24769" />
</filter>
<linearGradient
x1="-74.520325"
y1="169.06032"
x2="-74.520325"
y2="205.94189"
id="linearGradient24955"
xlink:href="#linearGradient4632-1-3-9-3-2"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-5,0)" />
<linearGradient
id="linearGradient4632-1-3-9-3-2">
<stop
id="stop4634-1-8-3-9-0"
style="stop-color:#eeeeec;stop-opacity:1"
offset="0" />
<stop
id="stop4636-1-9-9-8-8"
style="stop-color:#ffffff;stop-opacity:1"
offset="0.0274937" />
<stop
id="stop4638-8-3-9-6-6"
style="stop-color:#f2f2f2;stop-opacity:1"
offset="0.274937" />
<stop
id="stop4640-8-5-7-8-9"
style="stop-color:#eeeeec;stop-opacity:1"
offset="0.38707438" />
<stop
id="stop4642-5-41-9-6-9"
style="stop-color:#d9dad8;stop-opacity:1"
offset="0.66528589" />
<stop
id="stop4644-5-2-7-9-2"
style="stop-color:#dfe0dd;stop-opacity:1"
offset="0.76745707" />
<stop
id="stop4646-3-2-3-7-3"
style="stop-color:#f0f0f0;stop-opacity:1"
offset="1" />
</linearGradient>
<radialGradient
cx="-33.412369"
cy="185.74171"
r="2.3554697"
fx="-33.412369"
fy="185.74171"
id="radialGradient24959"
xlink:href="#linearGradient4869-4-1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0075,0,0,1.0075,-5.4544,-1.25141)" />
<linearGradient
id="linearGradient4869-4-1">
<stop
id="stop4871-6-2"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop4879-7-4"
style="stop-color:#eeeeec;stop-opacity:1"
offset="0.31807542" />
<stop
id="stop4877-6-1"
style="stop-color:#c8c9c6;stop-opacity:1"
offset="0.74691135" />
<stop
id="stop4873-1-0"
style="stop-color:#d3d7cf;stop-opacity:1"
offset="1" />
</linearGradient>
<filter
x="0"
y="0"
width="1"
height="1"
color-interpolation-filters="sRGB"
id="filter25011">
<feColorMatrix
result="fbSourceGraphic"
values="1"
type="saturate"
id="feColorMatrix25013" />
<feColorMatrix
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
in="fbSourceGraphic"
id="feColorMatrix25015" />
</filter>
<radialGradient
cx="-33.412369"
cy="185.74171"
r="2.3554697"
fx="-33.412369"
fy="185.74171"
id="radialGradient24961"
xlink:href="#linearGradient4869-4-0"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0075,0,0,1.0075,-5.4544,-1.25141)" />
<linearGradient
id="linearGradient4869-4-0">
<stop
id="stop4871-6-8"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop4879-7-5"
style="stop-color:#eeeeec;stop-opacity:1"
offset="0.31807542" />
<stop
id="stop4877-6-5"
style="stop-color:#c8c9c6;stop-opacity:1"
offset="0.74691135" />
<stop
id="stop4873-1-4"
style="stop-color:#d3d7cf;stop-opacity:1"
offset="1" />
</linearGradient>
<filter
x="0"
y="0"
width="1"
height="1"
color-interpolation-filters="sRGB"
id="filter25023">
<feColorMatrix
result="fbSourceGraphic"
values="1"
type="saturate"
id="feColorMatrix25025" />
<feColorMatrix
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
in="fbSourceGraphic"
id="feColorMatrix25027" />
</filter>
<linearGradient
x1="-39.858727"
y1="184.61784"
x2="-38.244785"
y2="188.84898"
id="linearGradient24963"
xlink:href="#linearGradient4941"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient4941">
<stop
id="stop4943"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop4945"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<filter
x="0"
y="0"
width="1"
height="1"
color-interpolation-filters="sRGB"
id="filter25033">
<feColorMatrix
result="fbSourceGraphic"
values="1"
type="saturate"
id="feColorMatrix25035" />
<feColorMatrix
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
in="fbSourceGraphic"
id="feColorMatrix25037" />
</filter>
<linearGradient
x1="-39.858727"
y1="184.61784"
x2="-38.244785"
y2="188.84898"
id="linearGradient24965"
xlink:href="#linearGradient4941-7"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient4941-7">
<stop
id="stop4943-2"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop4945-5"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<filter
x="0"
y="0"
width="1"
height="1"
color-interpolation-filters="sRGB"
id="filter25043">
<feColorMatrix
result="fbSourceGraphic"
values="1"
type="saturate"
id="feColorMatrix25045" />
<feColorMatrix
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
in="fbSourceGraphic"
id="feColorMatrix25047" />
</filter>
<filter
x="0"
y="0"
width="1"
height="1"
color-interpolation-filters="sRGB"
id="filter25049">
<feColorMatrix
result="fbSourceGraphic"
values="1"
type="saturate"
id="feColorMatrix25051" />
<feColorMatrix
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
in="fbSourceGraphic"
id="feColorMatrix25053" />
</filter>
<filter
x="0"
y="0"
width="1"
height="1"
color-interpolation-filters="sRGB"
id="filter25055">
<feColorMatrix
result="fbSourceGraphic"
values="1"
type="saturate"
id="feColorMatrix25057" />
<feColorMatrix
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"
in="fbSourceGraphic"
id="feColorMatrix25059" />
</filter>
</defs>
<g
transform="matrix(0,1,-1,0,48.0003,4.1307112e-7)"
id="layer1">
<g
transform="matrix(-2,0,0,2,-97.2497,-374.967)"
id="g4030-1-8"
style="stroke:#000000;stroke-opacity:1;display:inline">
<path
d="m -72.5,173.5 -14,14 14,14"
id="path3165-7-3"
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" />
</g>
<path
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
transform="matrix(-3.34328,0,0,3.34328,-89.2797,-623.176)"
id="path4050-2-7-9-4"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" />
<path
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
transform="matrix(-3.34328,0,0,3.34328,-111.2797,-623.176)"
id="path4050-2-7-9-4-8"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" />
<path
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
transform="matrix(-2.86565,0,0,2.86565,-70.8457,-534.143)"
id="path4050-2-7-9-4-0"
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" />
<path
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
transform="matrix(-2.86565,0,0,2.86565,-92.8457,-534.143)"
id="path4050-2-7-9-4-0-9"
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
inkscape:connector-curvature="0" />
<path
d="m 47.87528,-34.0295 c 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25 -32.25,32.25 c -2.2253,2.2253 -6.2747,2.2253 -8.5,0 -2.2253,-2.22528 -2.2253,-6.2747 0,-8.5 l 23.75,-23.75 -23.75,-23.75 c -1.73168,-1.6731 -2.295,-4.44228 -1.3546,-6.65894 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 z"
id="path3165-7-3-1"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
inkscape:connector-curvature="0" />
<path
d="m 41.8316,28.09418 c -0.014,-1.58898 0.54158,-3.18406 1.66868,-4.31118 l 23.75,-23.75 m -25.1046,-30.40894 c 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25"
id="path3165-7-3-1-9"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 841 B

View 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

View File

@ -68,10 +68,6 @@ IGNORE_HFILES= \
gactionobserver.h \
shell-recorder-src.h
if !BUILD_RECORDER
IGNORE_HFILES += shell-recorder.h
endif
# Images to copy into HTML directory.
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
HTML_IMAGES=

View File

@ -29,6 +29,8 @@
<chapter>
<title>Search</title>
<xi:include href="xml/shell-app-system.xml"/>
<xi:include href="xml/shell-contact-system.xml"/>
<xi:include href="xml/shell-doc-system.xml"/>
</chapter>
<chapter>
<title>Tray Icons</title>
@ -40,6 +42,7 @@
<chapter>
<title>Recorder</title>
<xi:include href="xml/shell-recorder.xml"/>
<xi:include href="xml/shell-recorder-src.xml"/>
</chapter>
<chapter>
<title>Integration helpers and utilities</title>

View File

@ -1,4 +1,3 @@
NULL =
EXTRA_DIST = misc/config.js.in
CLEANFILES = misc/config.js
@ -7,7 +6,9 @@ misc/config.js: misc/config.js.in Makefile
[ -d $(@D) ] || $(mkdir_p) $(@D) ; \
sed -e "s|[@]PACKAGE_NAME@|$(PACKAGE_NAME)|g" \
-e "s|[@]PACKAGE_VERSION@|$(PACKAGE_VERSION)|g" \
-e "s|[@]GJS_VERSION@|$(GJS_VERSION)|g" \
-e "s|[@]HAVE_BLUETOOTH@|$(HAVE_BLUETOOTH)|g" \
-e "s|[@]SHELL_SYSTEM_CA_FILE@|$(SHELL_SYSTEM_CA_FILE)|g" \
-e "s|[@]GETTEXT_PACKAGE@|$(GETTEXT_PACKAGE)|g" \
-e "s|[@]datadir@|$(datadir)|g" \
-e "s|[@]libexecdir@|$(libexecdir)|g" \
@ -18,75 +19,79 @@ jsdir = $(pkgdatadir)/js
nobase_dist_js_DATA = \
gdm/batch.js \
gdm/consoleKit.js \
gdm/fingerprint.js \
gdm/loginDialog.js \
gdm/powerMenu.js \
gdm/realmd.js \
gdm/util.js \
gdm/systemd.js \
extensionPrefs/main.js \
misc/config.js \
misc/extensionUtils.js \
misc/fileUtils.js \
misc/format.js \
misc/gnomeSession.js \
misc/history.js \
misc/jsParse.js \
misc/loginManager.js \
misc/modemManager.js \
misc/params.js \
misc/screenSaver.js \
misc/util.js \
perf/core.js \
ui/altTab.js \
ui/appDisplay.js \
ui/appFavorites.js \
ui/automountManager.js \
ui/autorunManager.js \
ui/boxpointer.js \
ui/calendar.js \
ui/checkBox.js \
ui/contactDisplay.js \
ui/ctrlAltTab.js \
ui/dash.js \
ui/dateMenu.js \
ui/dnd.js \
ui/endSessionDialog.js \
ui/extensionSystem.js \
ui/extensionDownloader.js \
ui/environment.js \
ui/extensionSystem.js \
ui/flashspot.js \
ui/ibusCandidatePopup.js\
ui/grabHelper.js \
ui/iconGrid.js \
ui/keyboard.js \
ui/keyringPrompt.js \
ui/layout.js \
ui/lightbox.js \
ui/link.js \
ui/lookingGlass.js \
ui/magnifier.js \
ui/magnifierDBus.js \
ui/main.js \
ui/messageTray.js \
ui/modalDialog.js \
ui/sessionMode.js \
ui/networkAgent.js \
ui/shellEntry.js \
ui/shellMountOperation.js \
ui/notificationDaemon.js \
ui/overview.js \
ui/panel.js \
ui/panelMenu.js \
ui/pointerWatcher.js \
ui/placeDisplay.js \
ui/polkitAuthenticationAgent.js \
ui/popupMenu.js \
ui/remoteSearch.js \
ui/runDialog.js \
ui/screenShield.js \
ui/scripting.js \
ui/search.js \
ui/searchDisplay.js \
ui/shellDBus.js \
ui/statusIconDispatcher.js \
ui/status/accessibility.js \
ui/status/candidatePanel.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 \
@ -96,13 +101,4 @@ nobase_dist_js_DATA = \
ui/workspaceThumbnail.js \
ui/workspacesView.js \
ui/workspaceSwitcherPopup.js \
ui/xdndHandler.js \
ui/components/__init__.js \
ui/components/autorunManager.js \
ui/components/automountManager.js \
ui/components/networkAgent.js \
ui/components/polkitAgent.js \
ui/components/recorder.js \
ui/components/telepathyClient.js \
ui/components/keyring.js \
$(NULL)
ui/xdndHandler.js

View File

@ -6,14 +6,15 @@ const GObject = imports.gi.GObject;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
const Format = imports.format;
const _ = Gettext.gettext;
const Config = imports.misc.config;
const Format = imports.misc.format;
const ExtensionUtils = imports.misc.extensionUtils;
const GnomeShellIface = <interface name="org.gnome.Shell.Extensions">
const GnomeShellIface = <interface name="org.gnome.Shell">
<signal name="ExtensionStatusChanged">
<arg type="s" name="uuid"/>
<arg type="i" name="state"/>
@ -161,7 +162,7 @@ const Application = new Lang.Class({
vbox.add(toolbar);
let toolitem;
let label = new Gtk.Label({ label: '<b>' + _("Extension") + '</b>',
let label = new Gtk.Label({ label: _("<b>Extension</b>"),
use_markup: true });
toolitem = new Gtk.ToolItem({ child: label });
toolbar.add(toolitem);
@ -174,7 +175,7 @@ const Application = new Lang.Class({
let renderer = new Gtk.CellRendererText();
this._extensionSelector.pack_start(renderer, true);
this._extensionSelector.add_attribute(renderer, 'text', 1);
this._extensionSelector.set_cell_data_func(renderer, Lang.bind(this, this._setExtensionInsensitive));
this._extensionSelector.set_cell_data_func(renderer, Lang.bind(this, this._setExtensionInsensitive), null);
this._extensionSelector.connect('changed', Lang.bind(this, this._extensionSelected));
toolitem = new Gtk.ToolItem({ child: this._extensionSelector });
@ -201,17 +202,23 @@ const Application = new Lang.Class({
},
_scanExtensions: function() {
let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', Lang.bind(this, this._extensionFound));
finder.scanExtensions();
},
ExtensionUtils.scanExtensions(Lang.bind(this, function(uuid, dir, type) {
if (ExtensionUtils.extensions[uuid] !== undefined)
return;
_extensionFound: function(signals, extension) {
let iter = this._model.append();
this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
this._extensionIters[extension.uuid] = iter;
},
let extension;
try {
extension = ExtensionUtils.createExtensionObject(uuid, dir, type);
} catch(e) {
global.logError('' + e);
return;
}
let iter = this._model.append();
this._model.set(iter, [0, 1], [uuid, extension.metadata.name]);
this._extensionIters[uuid] = iter;
}));
},
_onActivate: function() {
this._window.present();
@ -250,7 +257,7 @@ function initEnvironment() {
},
logError: function(s) {
log('ERROR: ' + s);
global.log('ERROR: ' + s);
},
userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell'])
@ -261,6 +268,7 @@ function initEnvironment() {
function main(argv) {
initEnvironment();
ExtensionUtils.init();
Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
Gettext.textdomain(Config.GETTEXT_PACKAGE);

22
js/gdm/consoleKit.js Normal file
View File

@ -0,0 +1,22 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'>
<method name='CanRestart'>
<arg type='b' direction='out'/>
</method>
<method name='CanStop'>
<arg type='b' direction='out'/>
</method>
<method name='Restart' />
<method name='Stop' />
</interface>;
const ConsoleKitProxy = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
function ConsoleKitManager() {
return new ConsoleKitProxy(Gio.DBus.system,
'org.freedesktop.ConsoleKit',
'/org/freedesktop/ConsoleKit/Manager');
};

View File

@ -19,7 +19,8 @@ function FprintManager() {
g_interface_info: FprintManagerInfo,
g_name: 'net.reactivated.Fprint',
g_object_path: '/net/reactivated/Fprint/Manager',
g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
self.init(null);
return self;

File diff suppressed because it is too large Load Diff

View File

@ -21,7 +21,8 @@
const Lang = imports.lang;
const UPowerGlib = imports.gi.UPowerGlib;
const LoginManager = imports.misc.loginManager;
const ConsoleKit = imports.gdm.consoleKit;
const Systemd = imports.gdm.systemd;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
@ -31,11 +32,13 @@ const PowerMenuButton = new Lang.Class({
Extends: PanelMenu.SystemStatusButton,
_init: function() {
/* Translators: accessible name of the power menu in the login screen */
this.parent('system-shutdown-symbolic', _("Power"));
this.parent('system-shutdown', null);
this._upClient = new UPowerGlib.Client();
this._loginManager = LoginManager.getLoginManager();
if (Systemd.haveSystemd())
this._systemdLoginManager = new Systemd.SystemdLoginManager();
else
this._consoleKitManager = new ConsoleKit.ConsoleKitManager();
this._createSubMenu();
@ -62,19 +65,57 @@ const PowerMenuButton = new Lang.Class({
},
_updateHaveShutdown: function() {
this._loginManager.canPowerOff(Lang.bind(this, function(result) {
this._haveShutdown = result;
this._powerOffItem.actor.visible = this._haveShutdown;
this._updateVisibility();
}));
if (Systemd.haveSystemd()) {
this._systemdLoginManager.CanPowerOffRemote(Lang.bind(this,
function(result, error) {
if (!error)
this._haveShutdown = result != 'no';
else
this._haveShutdown = false;
this._powerOffItem.actor.visible = this._haveShutdown;
this._updateVisibility();
}));
} else {
this._consoleKitManager.CanStopRemote(Lang.bind(this,
function(result, error) {
if (!error)
this._haveShutdown = result;
else
this._haveShutdown = false;
this._powerOffItem.actor.visible = this._haveShutdown;
this._updateVisibility();
}));
}
},
_updateHaveRestart: function() {
this._loginManager.canReboot(Lang.bind(this, function(result) {
this._haveRestart = result;
this._restartItem.actor.visible = this._haveRestart;
this._updateVisibility();
}));
if (Systemd.haveSystemd()) {
this._systemdLoginManager.CanRebootRemote(Lang.bind(this,
function(result, error) {
if (!error)
this._haveRestart = result != 'no';
else
this._haveRestart = false;
this._restartItem.actor.visible = this._haveRestart;
this._updateVisibility();
}));
} else {
this._consoleKitManager.CanRestartRemote(Lang.bind(this,
function(result, error) {
if (!error)
this._haveRestart = result;
else
this._haveRestart = false;
this._restartItem.actor.visible = this._haveRestart;
this._updateVisibility();
}));
}
},
_updateHaveSuspend: function() {
@ -111,13 +152,19 @@ const PowerMenuButton = new Lang.Class({
if (!this._haveRestart)
return;
this._loginManager.reboot();
if (Systemd.haveSystemd())
this._systemdLoginManager.RebootRemote(true);
else
this._consoleKitManager.RestartRemote();
},
_onActivatePowerOff: function() {
if (!this._haveShutdown)
return;
this._loginManager.powerOff();
if (Systemd.haveSystemd())
this._systemdLoginManager.PowerOffRemote(true);
else
this._consoleKitManager.StopRemote();
}
});

View File

@ -1,139 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const ProviderIface = <interface name='org.freedesktop.realmd.Provider'>
<property name="Name" type="s" access="read"/>
<property name="Version" type="s" access="read"/>
<property name="Realms" type="ao" access="read"/>
<method name="Discover">
<arg name="string" type="s" direction="in"/>
<arg name="options" type="a{sv}" direction="in"/>
<arg name="relevance" type="i" direction="out"/>
<arg name="realm" type="ao" direction="out"/>
</method>
</interface>;
const Provider = Gio.DBusProxy.makeProxyWrapper(ProviderIface);
const ServiceIface = <interface name="org.freedesktop.realmd.Service">
<method name="Cancel">
<arg name="operation" type="s" direction="in"/>
</method>
<method name="Release" />
<method name="SetLocale">
<arg name="locale" type="s" direction="in"/>
</method>
<signal name="Diagnostics">
<arg name="data" type="s"/>
<arg name="operation" type="s"/>
</signal>
</interface>;
const Service = Gio.DBusProxy.makeProxyWrapper(ServiceIface);
const RealmIface = <interface name="org.freedesktop.realmd.Realm">
<property name="Name" type="s" access="read"/>
<property name="Configured" type="s" access="read"/>
<property name="Details" type="a(ss)" access="read"/>
<property name="LoginFormats" type="as" access="read"/>
<property name="LoginPolicy" type="s" access="read"/>
<property name="PermittedLogins" type="as" access="read"/>
<property name="SupportedInterfaces" type="as" access="read"/>
<method name="ChangeLoginPolicy">
<arg name="login_policy" type="s" direction="in"/>
<arg name="permitted_add" type="as" direction="in"/>
<arg name="permitted_remove" type="as" direction="in"/>
<arg name="options" type="a{sv}" direction="in"/>
</method>
<method name="Deconfigure">
<arg name="options" type="a{sv}" direction="in"/>
</method>
</interface>;
const Realm = Gio.DBusProxy.makeProxyWrapper(RealmIface);
const Manager = new Lang.Class({
Name: 'Manager',
_init: function(parentActor) {
this._aggregateProvider = Provider(Gio.DBus.system,
'org.freedesktop.realmd',
'/org/freedesktop/realmd',
Lang.bind(this, this._reloadRealms))
this._realms = {};
this._aggregateProvider.connect('g-properties-changed',
Lang.bind(this, function(proxy, properties) {
if ('Realms' in properties.deep_unpack())
this._reloadRealms();
}));
},
_reloadRealms: function() {
let realmPaths = this._aggregateProvider.Realms;
if (!realmPaths)
return;
for (let i = 0; i < realmPaths.length; i++) {
let realm = Realm(Gio.DBus.system,
'org.freedesktop.realmd',
realmPaths[i],
Lang.bind(this, this._onRealmLoaded));
}
},
_reloadRealm: function(realm) {
if (!realm.Configured) {
if (this._realms[realm.get_object_path()])
delete this._realms[realm.get_object_path()];
return;
}
this._realms[realm.get_object_path()] = realm;
this._updateLoginFormat();
},
_onRealmLoaded: function(realm, error) {
if (error)
return;
this._reloadRealm(realm);
realm.connect('g-properties-changed',
Lang.bind(this, function(proxy, properties) {
if ('Configured' in properties.deep_unpack())
this._reloadRealm();
}));
},
_updateLoginFormat: function() {
let newLoginFormat;
for (let realmPath in this._realms) {
let realm = this._realms[realmPath];
if (realm.LoginFormats && realm.LoginFormats.length > 0) {
newLoginFormat = realm.LoginFormats[0];
break;
}
}
if (this._loginFormat != newLoginFormat) {
this._loginFormat = newLoginFormat;
this.emit('login-format-changed', newLoginFormat);
}
},
get loginFormat() {
if (this._loginFormat !== undefined)
return this._loginFormat;
this._updateLoginFormat();
return this._loginFormat;
}
});
Signals.addSignalMethods(Manager.prototype)

31
js/gdm/systemd.js Normal file
View File

@ -0,0 +1,31 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
<method name='PowerOff'>
<arg type='b' direction='in'/>
</method>
<method name='Reboot'>
<arg type='b' direction='in'/>
</method>
<method name='CanPowerOff'>
<arg type='s' direction='out'/>
</method>
<method name='CanReboot'>
<arg type='s' direction='out'/>
</method>
</interface>;
const SystemdLoginManagerProxy = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
function SystemdLoginManager() {
return new SystemdLoginManagerProxy(Gio.DBus.system,
'org.freedesktop.login1',
'/org/freedesktop/login1');
};
function haveSystemd() {
return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0;
}

View File

@ -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);

View File

@ -4,8 +4,12 @@
const PACKAGE_NAME = '@PACKAGE_NAME@';
/* The version of this package */
const PACKAGE_VERSION = '@PACKAGE_VERSION@';
/* The version of GJS we're linking to */
const GJS_VERSION = '@GJS_VERSION@';
/* 1 if gnome-bluetooth is available, 0 otherwise */
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
/* The system TLS CA list */
const SHELL_SYSTEM_CA_FILE = '@SHELL_SYSTEM_CA_FILE@';
/* gettext package */
const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
/* locale dir */

View File

@ -3,9 +3,6 @@
// Common utils for the extension system and the extension
// preferences tool
const Lang = imports.lang;
const Signals = imports.signals;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const ShellJS = imports.gi.ShellJS;
@ -17,6 +14,9 @@ const ExtensionType = {
PER_USER: 2
};
// GFile for user extensions
var userExtensionsDir = null;
// Maps uuid -> metadata object
const extensions = {};
@ -40,18 +40,13 @@ function getCurrentExtension() {
throw new Error('Could not find current extension');
let path = match[1];
let file = Gio.File.new_for_path(path);
let uuid = GLib.path_get_basename(GLib.path_get_dirname(path));
// Walk up the directory tree, looking for an extesion with
// the same UUID as a directory name.
while (file != null) {
let extension = extensions[file.get_basename()];
if (extension !== undefined)
return extension;
file = file.get_parent();
}
let extension = extensions[uuid];
if (extension === undefined)
throw new Error('Could not find current extension');
throw new Error('Could not find current extension');
return extension;
}
/**
@ -88,6 +83,9 @@ function isOutOfDate(extension) {
if (!versionCheck(extension.metadata['shell-version'], Config.PACKAGE_VERSION))
return true;
if (extension.metadata['js-version'] && !versionCheck(extension.metadata['js-version'], Config.GJS_VERSION))
return true;
return false;
}
@ -120,6 +118,11 @@ function createExtensionObject(uuid, dir, type) {
}
}
// Encourage people to add this
if (!meta.url) {
global.log('Warning: Missing "url" property in metadata.json');
}
if (uuid != meta.uuid) {
throw new Error('uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
}
@ -147,56 +150,45 @@ function installImporter(extension) {
_extension = null;
}
const ExtensionFinder = new Lang.Class({
Name: 'ExtensionFinder',
_scanExtensionsInDirectory: function(dir, type) {
let fileEnum;
let file, info;
try {
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
} catch(e) {
if (e.domain != Gio.io_error_quark() || e.code != Gio.IOErrorEnum.NOT_FOUND)
logError(e, 'Could not enumerate extensions directory');
return;
}
while ((info = fileEnum.next_file(null)) != null) {
let fileType = info.get_file_type();
if (fileType != Gio.FileType.DIRECTORY)
continue;
let uuid = info.get_name();
let extensionDir = dir.get_child(uuid);
let existing = extensions[uuid];
if (existing) {
log('Extension %s already installed in %s. %s will not be loaded'.format(uuid, existing.path, extensionDir.get_path()));
continue;
}
let extension;
try {
extension = createExtensionObject(uuid, extensionDir, type);
} catch(e) {
logError(e, 'Could not load extension %s'.format(uuid));
continue;
}
this.emit('extension-found', extension);
}
fileEnum.close(null);
},
scanExtensions: function() {
let userExtensionsDir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions']));
this._scanExtensionsInDirectory(userExtensionsDir, ExtensionType.PER_USER);
let systemDataDirs = GLib.get_system_data_dirs();
for (let i = 0; i < systemDataDirs.length; i++) {
let dirPath = GLib.build_filenamev([systemDataDirs[i], 'gnome-shell', 'extensions']);
let dir = Gio.file_new_for_path(dirPath);
if (dir.query_exists(null))
this._scanExtensionsInDirectory(dir, ExtensionType.SYSTEM);
}
function init() {
let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']);
userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
try {
if (!userExtensionsDir.query_exists(null))
userExtensionsDir.make_directory_with_parents(null);
} catch (e) {
global.logError('' + e);
}
});
Signals.addSignalMethods(ExtensionFinder.prototype);
}
function scanExtensionsInDirectory(callback, dir, type) {
let fileEnum;
let file, info;
try {
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
} catch(e) {
global.logError('' + e);
return;
}
while ((info = fileEnum.next_file(null)) != null) {
let fileType = info.get_file_type();
if (fileType != Gio.FileType.DIRECTORY)
continue;
let uuid = info.get_name();
let extensionDir = dir.get_child(uuid);
callback(uuid, extensionDir, type);
}
fileEnum.close(null);
}
function scanExtensions(callback) {
let systemDataDirs = GLib.get_system_data_dirs();
scanExtensionsInDirectory(callback, userExtensionsDir, ExtensionType.PER_USER);
for (let i = 0; i < systemDataDirs.length; i++) {
let dirPath = GLib.build_filenamev([systemDataDirs[i], 'gnome-shell', 'extensions']);
let dir = Gio.file_new_for_path(dirPath);
if (dir.query_exists(null))
scanExtensionsInDirectory(callback, dir, ExtensionType.SYSTEM);
}
}

View File

@ -28,7 +28,7 @@ function deleteGFile(file) {
return file['delete'](null);
}
function recursivelyDeleteDir(dir, deleteParent) {
function recursivelyDeleteDir(dir) {
let children = dir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null);
@ -39,29 +39,8 @@ function recursivelyDeleteDir(dir, deleteParent) {
if (type == Gio.FileType.REGULAR)
deleteGFile(child);
else if (type == Gio.FileType.DIRECTORY)
recursivelyDeleteDir(child, true);
recursivelyDeleteDir(child);
}
if (deleteParent)
deleteGFile(dir);
}
function recursivelyMoveDir(srcDir, destDir) {
let children = srcDir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null);
if (!destDir.query_exists(null))
destDir.make_directory_with_parents(null);
let info, child;
while ((info = children.next_file(null)) != null) {
let type = info.get_file_type();
let srcChild = srcDir.get_child(info.get_name());
let destChild = destDir.get_child(info.get_name());
log([srcChild.get_path(), destChild.get_path()]);
if (type == Gio.FileType.REGULAR)
srcChild.move(destChild, Gio.FileCopyFlags.NONE, null, null);
else if (type == Gio.FileType.DIRECTORY)
recursivelyMoveDir(srcChild, destChild);
}
deleteGFile(dir);
}

71
js/misc/format.js Normal file
View File

@ -0,0 +1,71 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const ShellJS = imports.gi.ShellJS;
/*
* This function is intended to extend the String object and provide
* an String.format API for string formatting.
* It has to be set up using String.prototype.format = Format.format;
* Usage:
* "somestring %s %d".format('hello', 5);
* It supports %s, %d, %x and %f, for %f it also support precisions like
* "%.2f".format(1.526). All specifiers can be prefixed with a minimum
* field width, e.g. "%5s".format("foo"). Unless the width is prefixed
* with '0', the formatted string will be padded with spaces.
*/
function format() {
let str = this;
let i = 0;
let args = arguments;
return str.replace(/%(I+)?([0-9]+)?(?:\.([0-9]+))?(.)/g, function (str, flagsGroup, widthGroup, precisionGroup, genericGroup) {
if (precisionGroup != '' && genericGroup != 'f')
throw new Error("Precision can only be specified for 'f'");
let hasAlternativeIntFlag = (flagsGroup.indexOf('I') != -1);
if (hasAlternativeIntFlag && genericGroup != 'd')
throw new Error("Alternative output digits can only be specfied for 'd'");
let fillChar = (widthGroup[0] == '0') ? '0' : ' ';
let width = parseInt(widthGroup, 10) || 0;
function fillWidth(s, c, w) {
let fill = '';
for (let i = 0; i < w; i++)
fill += c;
return fill.substr(s.length) + s;
}
let s = '';
switch (genericGroup) {
case '%':
return '%';
break;
case 's':
s = args[i++].toString();
break;
case 'd':
let intV = parseInt(args[i++]);
if (hasAlternativeIntFlag)
s = ShellJS.format_int_alternative_output(intV);
else
s = intV.toString();
break;
case 'x':
s = parseInt(args[i++]).toString(16);
break;
case 'f':
if (precisionGroup == '')
s = parseFloat(args[i++]).toString();
else
s = parseFloat(args[i++]).toFixed(parseInt(precisionGroup));
break;
default:
throw new Error('Unsupported conversion character %' + genericGroup);
}
return fillWidth(s, fillChar, width);
});
}

View File

@ -50,20 +50,9 @@ const SessionManagerIface = <interface name="org.gnome.SessionManager">
<arg type="u" direction="in" />
</method>
<method name="Shutdown" />
<method name="Reboot" />
<method name="CanShutdown">
<arg type="b" direction="out" />
</method>
<method name="IsInhibited">
<arg type="u" direction="in" />
<arg type="b" direction="out" />
</method>
<signal name="InhibitorAdded">
<arg type="o" direction="out"/>
</signal>
<signal name="InhibitorRemoved">
<arg type="o" direction="out"/>
</signal>
</interface>;
var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface);

View File

@ -41,26 +41,24 @@ const HistoryManager = new Lang.Class({
this._historyIndex = this._history.length;
},
_setPrevItem: function(text) {
prevItem: function(text) {
if (this._historyIndex <= 0)
return false;
return text;
if (text)
this._history[this._historyIndex] = text;
this._historyIndex--;
this._indexChanged();
return true;
return this._indexChanged();
},
_setNextItem: function(text) {
nextItem: function(text) {
if (this._historyIndex >= this._history.length)
return false;
return text;
if (text)
this._history[this._historyIndex] = text;
this._historyIndex++;
this._indexChanged();
return true;
return this._indexChanged();
},
lastItem: function() {
@ -85,9 +83,11 @@ const HistoryManager = new Lang.Class({
_onEntryKeyPress: function(entry, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Up) {
return this._setPrevItem(entry.get_text());
this.prevItem(entry.get_text());
return true;
} else if (symbol == Clutter.KEY_Down) {
return this._setNextItem(entry.get_text());
this.nextItem(entry.get_text());
return true;
}
return false;
},
@ -98,6 +98,8 @@ const HistoryManager = new Lang.Class({
if (this._entry)
this._entry.set_text(current);
return current;
},
_save: function() {

View File

@ -1,197 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
<method name='PowerOff'>
<arg type='b' direction='in'/>
</method>
<method name='Reboot'>
<arg type='b' direction='in'/>
</method>
<method name='CanPowerOff'>
<arg type='s' direction='out'/>
</method>
<method name='CanReboot'>
<arg type='s' direction='out'/>
</method>
</interface>;
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
<signal name='Lock' />
<signal name='Unlock' />
</interface>;
const SystemdLoginManager = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'>
<method name='CanRestart'>
<arg type='b' direction='out'/>
</method>
<method name='CanStop'>
<arg type='b' direction='out'/>
</method>
<method name='Restart' />
<method name='Stop' />
<method name='GetCurrentSession'>
<arg type='o' direction='out' />
</method>
</interface>;
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'>
<method name='IsActive'>
<arg type='b' direction='out' />
</method>
<signal name='ActiveChanged'>
<arg type='b' direction='out' />
</signal>
<signal name='Lock' />
<signal name='Unlock' />
</interface>;
const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
function haveSystemd() {
return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0;
}
let _loginManager = null;
/**
* LoginManager:
* An abstraction over systemd/logind and ConsoleKit.
*
*/
function getLoginManager() {
if (_loginManager == null) {
if (haveSystemd())
_loginManager = new LoginManagerSystemd();
else
_loginManager = new LoginManagerConsoleKit();
}
return _loginManager;
}
const LoginManagerSystemd = new Lang.Class({
Name: 'LoginManagerSystemd',
_init: function() {
this._proxy = new SystemdLoginManager(Gio.DBus.system,
'org.freedesktop.login1',
'/org/freedesktop/login1');
},
// Having this function is a bit of a hack since the Systemd and ConsoleKit
// session objects have different interfaces - but in both cases there are
// Lock/Unlock signals, and that's all we count upon at the moment.
getCurrentSessionProxy: function() {
if (!this._currentSession) {
this._currentSession = new SystemdLoginSession(Gio.DBus.system,
'org.freedesktop.login1',
'/org/freedesktop/login1/session/' +
GLib.getenv('XDG_SESSION_ID'));
}
return this._currentSession;
},
get sessionActive() {
return Shell.session_is_active_for_systemd();
},
canPowerOff: function(asyncCallback) {
this._proxy.CanPowerOffRemote(function(result, error) {
if (error)
asyncCallback(false);
else
asyncCallback(result[0] != 'no');
});
},
canReboot: function(asyncCallback) {
this._proxy.CanRebootRemote(function(result, error) {
if (error)
asyncCallback(false);
else
asyncCallback(result[0] != 'no');
});
},
powerOff: function() {
this._proxy.PowerOffRemote(true);
},
reboot: function() {
this._proxy.RebootRemote(true);
}
});
const LoginManagerConsoleKit = new Lang.Class({
Name: 'LoginManagerConsoleKit',
_init: function() {
this._proxy = new ConsoleKitManager(Gio.DBus.system,
'org.freedesktop.ConsoleKit',
'/org/freedesktop/ConsoleKit/Manager');
},
// Having this function is a bit of a hack since the Systemd and ConsoleKit
// session objects have different interfaces - but in both cases there are
// Lock/Unlock signals, and that's all we count upon at the moment.
getCurrentSessionProxy: function() {
if (!this._currentSession) {
let [currentSessionId] = this._proxy.GetCurrentSessionSync();
this._currentSession = new ConsoleKitSession(Gio.DBus.system,
'org.freedesktop.ConsoleKit',
currentSessionId);
}
return this._currentSession;
},
get sessionActive() {
if (this._sessionActive !== undefined)
return this._sessionActive;
let session = this.getCurrentSessionProxy();
session.connectSignal('ActiveChanged', Lang.bind(this, function(object, senderName, [isActive]) {
this._sessionActive = isActive;
}));
[this._sessionActive] = session.IsActiveSync();
return this._sessionActive;
},
canPowerOff: function(asyncCallback) {
this._proxy.CanStopRemote(function(result, error) {
if (error)
asyncCallback(false);
else
asyncCallback(result[0]);
});
},
canReboot: function(asyncCallback) {
this._proxy.CanRestartRemote(function(result, error) {
if (error)
asyncCallback(false);
else
asyncCallback(result[0]);
});
},
powerOff: function() {
this._proxy.StopRemote();
},
reboot: function() {
this._proxy.RestartRemote();
}
});

48
js/misc/screenSaver.js Normal file
View File

@ -0,0 +1,48 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const ScreenSaverIface = <interface name="org.gnome.ScreenSaver">
<method name="GetActive">
<arg type="b" direction="out" />
</method>
<method name="Lock" />
<method name="SetActive">
<arg type="b" direction="in" />
</method>
<signal name="ActiveChanged">
<arg type="b" direction="out" />
</signal>
</interface>;
const ScreenSaverInfo = Gio.DBusInterfaceInfo.new_for_xml(ScreenSaverIface);
function ScreenSaverProxy() {
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
g_interface_name: ScreenSaverInfo.name,
g_interface_info: ScreenSaverInfo,
g_name: 'org.gnome.ScreenSaver',
g_object_path: '/org/gnome/ScreenSaver',
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
self.init(null);
self.screenSaverActive = false;
self.connectSignal('ActiveChanged', function(proxy, senderName, [isActive]) {
self.screenSaverActive = isActive;
});
self.connect('notify::g-name-owner', function() {
if (self.g_name_owner) {
self.GetActiveRemote(function(result, excp) {
if (result) {
let [isActive] = result;
self.screenSaverActive = isActive;
}
});
} else
self.screenSaverActive = false;
});
return self;
}

View File

@ -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.

View File

@ -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();
}
}

View File

@ -22,7 +22,6 @@ const Search = imports.ui.search;
const Tweener = imports.ui.tweener;
const Workspace = imports.ui.workspace;
const Params = imports.misc.params;
const Util = imports.misc.util;
const MAX_APPLICATION_WORK_MILLIS = 75;
const MENU_POPUP_TIMEOUT = 600;
@ -37,7 +36,6 @@ const AlphabeticalView = new Lang.Class({
this._pendingAppLaterId = 0;
this._appIcons = {}; // desktop file id
this._allApps = [];
let box = new St.BoxLayout({ vertical: true });
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
@ -62,22 +60,16 @@ const AlphabeticalView = new Lang.Class({
}));
},
removeAll: function() {
_removeAll: function() {
this._grid.removeAll();
this._appIcons = {};
this._allApps = [];
},
addApp: function(app) {
_addApp: function(app) {
var id = app.get_id();
if (this._appIcons[id] !== undefined)
return;
let appIcon = new AppWellIcon(app);
let pos = Util.insertSorted(this._allApps, app, function(a, b) {
return a.compare_by_name(b);
});
this._grid.addItem(appIcon.actor, pos);
this._grid.addItem(appIcon.actor);
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
this._appIcons[id] = appIcon;
@ -128,6 +120,14 @@ const AlphabeticalView = new Lang.Class({
icon.actor.visible = true;
}
}
},
setAppList: function(apps) {
this._removeAll();
for (var i = 0; i < apps.length; i++) {
var app = apps[i];
this._addApp(app);
}
}
});
@ -147,6 +147,7 @@ const ViewByCategories = new Lang.Class({
// (used only before the actor is mapped the first time)
this._currentCategory = -2;
this._categories = [];
this._apps = null;
this._categoryBox = new St.BoxLayout({ vertical: true,
reactive: true,
@ -203,19 +204,16 @@ const ViewByCategories = new Lang.Class({
if (nextType == GMenu.TreeItemType.ENTRY) {
var entry = iter.get_entry();
var app = this._appSystem.lookup_app_by_tree_entry(entry);
if (!entry.get_app_info().get_nodisplay()) {
this._view.addApp(app);
if (!entry.get_app_info().get_nodisplay())
appList.push(app);
}
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
var itemDir = iter.get_directory();
if (!itemDir.get_is_nodisplay())
this._loadCategory(itemDir, appList);
if (!dir.get_is_nodisplay())
this._loadCategory(iter.get_directory(), appList);
}
}
},
_addCategory: function(name, index, dir) {
_addCategory: function(name, index, dir, allApps) {
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
style_class: 'app-filter',
x_align: St.Align.START,
@ -227,6 +225,7 @@ const ViewByCategories = new Lang.Class({
var apps;
if (dir == null) {
apps = allApps;
this._allCategoryButton = button;
} else {
apps = [];
@ -240,7 +239,6 @@ const ViewByCategories = new Lang.Class({
},
_removeAll: function() {
this._view.removeAll();
this._categories = [];
this._categoryBox.destroy_all_children();
},
@ -248,8 +246,13 @@ const ViewByCategories = new Lang.Class({
refresh: function() {
this._removeAll();
var allApps = Shell.AppSystem.get_default().get_all();
allApps.sort(function(a, b) {
return a.compare_by_name(b);
});
/* Translators: Filter to display all applications */
this._addCategory(_("All"), -1, null);
this._addCategory(_("All"), -1, null, allApps);
var tree = this._appSystem.get_tree();
var root = tree.get_root_directory();
@ -267,6 +270,7 @@ const ViewByCategories = new Lang.Class({
}
}
this._view.setAppList(allApps);
this._selectCategory(-1);
if (this._focusDummy) {
@ -308,10 +312,11 @@ const AppSearchProvider = new Lang.Class({
_init: function() {
this.parent(_("APPLICATIONS"));
this._appSys = Shell.AppSystem.get_default();
},
getResultMetas: function(apps, callback) {
getResultMetas: function(apps) {
let metas = [];
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
@ -322,15 +327,15 @@ const AppSearchProvider = new Lang.Class({
}
});
}
callback(metas);
return metas;
},
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) {
@ -373,7 +378,7 @@ const SettingsSearchProvider = new Lang.Class({
this._gnomecc = this._appSys.lookup_app('gnome-control-center.desktop');
},
getResultMetas: function(prefs, callback) {
getResultMetas: function(prefs) {
let metas = [];
for (let i = 0; i < prefs.length; i++) {
let pref = prefs[i];
@ -384,15 +389,15 @@ const SettingsSearchProvider = new Lang.Class({
}
});
}
callback(metas);
return metas;
},
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) {

290
js/ui/automountManager.js Normal file
View File

@ -0,0 +1,290 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Params = imports.misc.params;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
const ShellMountOperation = imports.ui.shellMountOperation;
const ScreenSaver = imports.misc.screenSaver;
// GSettings keys
const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
const SETTING_ENABLE_AUTOMOUNT = 'automount';
const AUTORUN_EXPIRE_TIMEOUT_SECS = 10;
const ConsoleKitSessionIface = <interface name="org.freedesktop.ConsoleKit.Session">
<method name="IsActive">
<arg type="b" direction="out" />
</method>
<signal name="ActiveChanged">
<arg type="b" direction="out" />
</signal>
</interface>;
const ConsoleKitSessionProxy = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
const ConsoleKitManagerIface = <interface name="org.freedesktop.ConsoleKit.Manager">
<method name="GetCurrentSession">
<arg type="o" direction="out" />
</method>
</interface>;
const ConsoleKitManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ConsoleKitManagerIface);
function ConsoleKitManager() {
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
g_interface_name: ConsoleKitManagerInfo.name,
g_interface_info: ConsoleKitManagerInfo,
g_name: 'org.freedesktop.ConsoleKit',
g_object_path: '/org/freedesktop/ConsoleKit/Manager',
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
self._updateSessionActive = function() {
if (self.g_name_owner) {
self.GetCurrentSessionRemote(function([session]) {
self._ckSession = new ConsoleKitSessionProxy(Gio.DBus.system, 'org.freedesktop.ConsoleKit', session);
self._ckSession.connectSignal('ActiveChanged', function(object, senderName, [isActive]) {
self.sessionActive = isActive;
});
self._ckSession.IsActiveRemote(function([isActive]) {
self.sessionActive = isActive;
});
});
} else {
self.sessionActive = true;
}
};
self.connect('notify::g-name-owner',
Lang.bind(self, self._updateSessionActive));
self._updateSessionActive();
self.init(null);
return self;
}
function haveSystemd() {
return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0;
}
const AutomountManager = new Lang.Class({
Name: 'AutomountManager',
_init: function() {
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
this._volumeQueue = [];
if (!haveSystemd())
this.ckListener = new ConsoleKitManager();
this._ssProxy = new ScreenSaver.ScreenSaverProxy();
this._ssProxy.connectSignal('ActiveChanged',
Lang.bind(this, this._screenSaverActiveChanged));
this._volumeMonitor = Gio.VolumeMonitor.get();
this._volumeMonitor.connect('volume-added',
Lang.bind(this,
this._onVolumeAdded));
this._volumeMonitor.connect('volume-removed',
Lang.bind(this,
this._onVolumeRemoved));
this._volumeMonitor.connect('drive-connected',
Lang.bind(this,
this._onDriveConnected));
this._volumeMonitor.connect('drive-disconnected',
Lang.bind(this,
this._onDriveDisconnected));
this._volumeMonitor.connect('drive-eject-button',
Lang.bind(this,
this._onDriveEjectButton));
Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
},
_screenSaverActiveChanged: function(object, senderName, [isActive]) {
if (!isActive) {
this._volumeQueue.forEach(Lang.bind(this, function(volume) {
this._checkAndMountVolume(volume);
}));
}
// clear the queue anyway
this._volumeQueue = [];
},
_startupMountAll: function() {
let volumes = this._volumeMonitor.get_volumes();
volumes.forEach(Lang.bind(this, function(volume) {
this._checkAndMountVolume(volume, { checkSession: false,
useMountOp: false });
}));
return false;
},
isSessionActive: function() {
// Return whether the current session is active, using the
// right mechanism: either systemd if available or ConsoleKit
// as fallback.
if (haveSystemd())
return Shell.session_is_active_for_systemd();
return this.ckListener.sessionActive;
},
_onDriveConnected: function() {
// if we're not in the current ConsoleKit session,
// or screensaver is active, don't play sounds
if (!this.isSessionActive())
return;
if (this._ssProxy.screenSaverActive)
return;
global.play_theme_sound(0, 'device-added-media');
},
_onDriveDisconnected: function() {
// if we're not in the current ConsoleKit session,
// or screensaver is active, don't play sounds
if (!this.isSessionActive())
return;
if (this._ssProxy.screenSaverActive)
return;
global.play_theme_sound(0, 'device-removed-media');
},
_onDriveEjectButton: function(monitor, drive) {
// TODO: this code path is not tested, as the GVfs volume monitor
// doesn't emit this signal just yet.
if (!this.isSessionActive())
return;
// we force stop/eject in this case, so we don't have to pass a
// mount operation object
if (drive.can_stop()) {
drive.stop
(Gio.MountUnmountFlags.FORCE, null, null,
Lang.bind(this, function(drive, res) {
try {
drive.stop_finish(res);
} catch (e) {
log("Unable to stop the drive after drive-eject-button " + e.toString());
}
}));
} else if (drive.can_eject()) {
drive.eject_with_operation
(Gio.MountUnmountFlags.FORCE, null, null,
Lang.bind(this, function(drive, res) {
try {
drive.eject_with_operation_finish(res);
} catch (e) {
log("Unable to eject the drive after drive-eject-button " + e.toString());
}
}));
}
},
_onVolumeAdded: function(monitor, volume) {
this._checkAndMountVolume(volume);
},
_checkAndMountVolume: function(volume, params) {
params = Params.parse(params, { checkSession: true,
useMountOp: true });
if (params.checkSession) {
// if we're not in the current ConsoleKit session,
// don't attempt automount
if (!this.isSessionActive())
return;
if (this._ssProxy.screenSaverActive) {
if (this._volumeQueue.indexOf(volume) == -1)
this._volumeQueue.push(volume);
return;
}
}
// Volume is already mounted, don't bother.
if (volume.get_mount())
return;
if (!this._settings.get_boolean(SETTING_ENABLE_AUTOMOUNT) ||
!volume.should_automount() ||
!volume.can_mount()) {
// allow the autorun to run anyway; this can happen if the
// mount gets added programmatically later, even if
// should_automount() or can_mount() are false, like for
// blank optical media.
this._allowAutorun(volume);
this._allowAutorunExpire(volume);
return;
}
if (params.useMountOp) {
let operation = new ShellMountOperation.ShellMountOperation(volume);
this._mountVolume(volume, operation.mountOp);
} else {
this._mountVolume(volume, null);
}
},
_mountVolume: function(volume, operation) {
this._allowAutorun(volume);
volume.mount(0, operation, null,
Lang.bind(this, this._onVolumeMounted));
},
_onVolumeMounted: function(volume, res) {
this._allowAutorunExpire(volume);
try {
volume.mount_finish(res);
} catch (e) {
let string = e.toString();
// FIXME: needs proper error code handling instead of this
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
if (string.indexOf('No key available with this passphrase') != -1)
this._reaskPassword(volume);
else
log('Unable to mount volume ' + volume.get_name() + ': ' + string);
}
},
_onVolumeRemoved: function(monitor, volume) {
this._volumeQueue =
this._volumeQueue.filter(function(element) {
return (element != volume);
});
},
_reaskPassword: function(volume) {
let operation = new ShellMountOperation.ShellMountOperation(volume, { reaskPassword: true });
this._mountVolume(volume, operation.mountOp);
},
_allowAutorun: function(volume) {
volume.allowAutorun = true;
},
_allowAutorunExpire: function(volume) {
Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
volume.allowAutorun = false;
return false;
});
}
});

View File

@ -4,7 +4,6 @@ const Lang = imports.lang;
const Gio = imports.gi.Gio;
const St = imports.gi.St;
const LoginManager = imports.misc.loginManager;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const ShellMountOperation = imports.ui.shellMountOperation;
@ -24,14 +23,12 @@ const AutorunSetting = {
};
// misc utils
function shouldAutorunMount(mount, forTransient) {
function ignoreAutorunForMount(mount) {
let root = mount.get_root();
let volume = mount.get_volume();
if (!volume || (!volume.allowAutorun && forTransient))
return false;
if (!root.is_native() || isMountRootHidden(root))
if ((root.is_native() && !isMountRootHidden(root)) ||
(volume && volume.allowAutorun && volume.should_automount()))
return false;
return true;
@ -44,19 +41,6 @@ function isMountRootHidden(root) {
return (path.indexOf('/.') != -1);
}
function isMountNonLocal(mount) {
// If the mount doesn't have an associated volume, that means it could
// be a remote filesystem. For certain kinds of local filesystems,
// like digital cameras and music players, there's no associated
// gvfs volume, so err on the side of caution and assume it's a local
// filesystem to allow the prompt.
let volume = mount.get_volume();
if (volume == null)
return false;
return (volume.get_identifier("class") == "network");
}
function startAppForMount(app, mount) {
let files = [];
let root = mount.get_root();
@ -96,21 +80,13 @@ const ContentTypeDiscoverer = new Lang.Class({
_init: function(callback) {
this._callback = callback;
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
},
guessContentTypes: function(mount) {
let autorunEnabled = !this._settings.get_boolean(SETTING_DISABLE_AUTORUN);
let shouldScan = autorunEnabled && !isMountNonLocal(mount);
if (shouldScan) {
// guess mount's content types using GIO
mount.guess_content_type(false, null,
Lang.bind(this,
this._onContentTypeGuessed));
} else {
this._emitCallback(mount, []);
}
// guess mount's content types using GIO
mount.guess_content_type(false, null,
Lang.bind(this,
this._onContentTypeGuessed));
},
_onContentTypeGuessed: function(mount, res) {
@ -164,69 +140,55 @@ const AutorunManager = new Lang.Class({
Name: 'AutorunManager',
_init: function() {
this._loginManager = LoginManager.getLoginManager();
this._volumeMonitor = Gio.VolumeMonitor.get();
this._transDispatcher = new AutorunTransientDispatcher(this);
},
this._volumeMonitor.connect('mount-added',
Lang.bind(this,
this._onMountAdded));
this._volumeMonitor.connect('mount-removed',
Lang.bind(this,
this._onMountRemoved));
_ensureResidentSource: function() {
if (this._residentSource)
return;
this._transDispatcher = new AutorunTransientDispatcher();
this._createResidentSource();
this._residentSource = new AutorunResidentSource(this);
let destroyId = this._residentSource.connect('destroy', Lang.bind(this, function() {
this._residentSource.disconnect(destroyId);
this._residentSource = null;
}));
},
enable: function() {
this._scanMounts();
this._mountAddedId = this._volumeMonitor.connect('mount-added', Lang.bind(this, this._onMountAdded));
this._mountRemovedId = this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._onMountRemoved));
},
disable: function() {
if (this._residentSource)
this._residentSource.destroy();
this._volumeMonitor.disconnect(this._mountAddedId);
this._volumeMonitor.disconnect(this._mountRemovedId);
},
_processMount: function(mount, hotplug) {
let discoverer = new ContentTypeDiscoverer(Lang.bind(this, function(mount, apps, contentTypes) {
this._ensureResidentSource();
this._residentSource.addMount(mount, apps);
if (hotplug)
this._transDispatcher.addMount(mount, apps, contentTypes);
}));
discoverer.guessContentTypes(mount);
},
_scanMounts: function() {
let mounts = this._volumeMonitor.get_mounts();
mounts.forEach(Lang.bind(this, function(mount) {
this._processMount(mount, false);
mounts.forEach(Lang.bind(this, function (mount) {
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
function (mount, apps) {
this._residentSource.addMount(mount, apps);
}));
discoverer.guessContentTypes(mount);
}));
},
_createResidentSource: function() {
this._residentSource = new AutorunResidentSource();
this._residentSource.connect('destroy',
Lang.bind(this,
this._createResidentSource));
},
_onMountAdded: function(monitor, mount) {
// don't do anything if our session is not the currently
// active one
if (!this._loginManager.sessionActive)
if (!Main.automountManager.isSessionActive())
return;
this._processMount(mount, true);
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
function (mount, apps, contentTypes) {
this._transDispatcher.addMount(mount, apps, contentTypes);
this._residentSource.addMount(mount, apps);
}));
discoverer.guessContentTypes(mount);
},
_onMountRemoved: function(monitor, mount) {
this._transDispatcher.removeMount(mount);
if (this._residentSource)
this._residentSource.removeMount(mount);
this._residentSource.removeMount(mount);
},
ejectMount: function(mount) {
@ -262,9 +224,11 @@ const AutorunManager = new Lang.Class({
try {
mount.unmount_with_operation_finish(res);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
log('Unable to eject the mount ' + mount.get_name()
+ ': ' + e.toString());
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
// but we can't access the error code from JS.
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to eject the mount ' + mount.get_name()
+ ': ' + e.toString());
}
},
@ -272,9 +236,11 @@ const AutorunManager = new Lang.Class({
try {
source.eject_with_operation_finish(res);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
log('Unable to eject the drive ' + source.get_name()
+ ': ' + e.toString());
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
// but we can't access the error code from JS.
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to eject the drive ' + source.get_name()
+ ': ' + e.toString());
}
},
@ -282,9 +248,11 @@ const AutorunManager = new Lang.Class({
try {
drive.stop_finish(res);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
log('Unable to stop the drive ' + drive.get_name()
+ ': ' + e.toString());
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
// but we can't access the error code from JS.
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to stop the drive ' + drive.get_name()
+ ': ' + e.toString());
}
},
});
@ -293,22 +261,17 @@ const AutorunResidentSource = new Lang.Class({
Name: 'AutorunResidentSource',
Extends: MessageTray.Source,
_init: function(manager) {
this.parent(_("Removable Devices"), 'media-removable');
this.showInLockScreen = false;
_init: function() {
this.parent(_("Removable Devices"));
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) {
@ -347,6 +310,12 @@ 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 });
}
});
@ -354,7 +323,7 @@ const AutorunResidentNotification = new Lang.Class({
Name: 'AutorunResidentNotification',
Extends: MessageTray.Notification,
_init: function(manager, source) {
_init: function(source) {
this.parent(source, source.title, null, { customContent: true });
// set the notification as resident
@ -362,7 +331,6 @@ const AutorunResidentNotification = new Lang.Class({
this._layout = new St.BoxLayout ({ style_class: 'hotplug-resident-box',
vertical: true });
this._manager = manager;
this.addActor(this._layout,
{ x_expand: true,
@ -410,7 +378,7 @@ const AutorunResidentNotification = new Lang.Class({
expand: true });
let ejectIcon =
new St.Icon({ icon_name: 'media-eject-symbolic',
new St.Icon({ icon_name: 'media-eject',
style_class: 'hotplug-resident-eject-icon' });
let ejectButton =
@ -425,7 +393,7 @@ const AutorunResidentNotification = new Lang.Class({
}));
ejectButton.connect('clicked', Lang.bind(this, function() {
this._manager.ejectMount(mount);
Main.autorunManager.ejectMount(mount);
}));
return item;
@ -435,8 +403,7 @@ const AutorunResidentNotification = new Lang.Class({
const AutorunTransientDispatcher = new Lang.Class({
Name: 'AutorunTransientDispatcher',
_init: function(manager) {
this._manager = manager;
_init: function() {
this._sources = [];
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
},
@ -479,7 +446,7 @@ const AutorunTransientDispatcher = new Lang.Class({
return;
// add a new source
this._sources.push(new AutorunTransientSource(this._manager, mount, apps));
this._sources.push(new AutorunTransientSource(mount, apps));
},
addMount: function(mount, apps, contentTypes) {
@ -488,7 +455,7 @@ const AutorunTransientDispatcher = new Lang.Class({
return;
// if the mount doesn't want to be autorun, return
if (!shouldAutorunMount(mount, true))
if (ignoreAutorunForMount(mount))
return;
let setting = this._getAutorunSettingForType(contentTypes[0]);
@ -532,23 +499,23 @@ const AutorunTransientSource = new Lang.Class({
Name: 'AutorunTransientSource',
Extends: MessageTray.Source,
_init: function(manager, mount, apps) {
this._manager = manager;
_init: function(mount, apps) {
this.parent(mount.get_name());
this.mount = mount;
this.apps = apps;
this.parent(mount.get_name());
this._notification = new AutorunTransientNotification(this._manager, this);
this._notification = new AutorunTransientNotification(this);
this._setSummaryIcon(this.createNotificationIcon());
// add ourselves as a source, and popup the notification
Main.messageTray.add(this);
this.notify(this._notification);
},
createIcon: function(size) {
createNotificationIcon: function() {
return new St.Icon({ gicon: this.mount.get_icon(),
icon_size: size });
icon_size: this.ICON_SIZE });
}
});
@ -556,10 +523,9 @@ const AutorunTransientNotification = new Lang.Class({
Name: 'AutorunTransientNotification',
Extends: MessageTray.Notification,
_init: function(manager, source) {
_init: function(source) {
this.parent(source, source.title, null, { customContent: true });
this._manager = manager;
this._box = new St.BoxLayout({ style_class: 'hotplug-transient-box',
vertical: true });
this.addActor(this._box);
@ -611,7 +577,7 @@ const AutorunTransientNotification = new Lang.Class({
_buttonForEject: function() {
let box = new St.BoxLayout();
let icon = new St.Icon({ icon_name: 'media-eject-symbolic',
let icon = new St.Icon({ icon_name: 'media-eject',
style_class: 'hotplug-notification-item-icon' });
box.add(icon);
@ -628,11 +594,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;

View File

@ -9,13 +9,6 @@ const Shell = imports.gi.Shell;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const PopupAnimation = {
NONE: 0,
SLIDE: 1 << 0,
FADE: 1 << 1,
FULL: ~0,
};
const POPUP_ANIMATION_TIME = 0.15;
/**
@ -25,10 +18,7 @@ const POPUP_ANIMATION_TIME = 0.15;
*
* An actor which displays a triangle "arrow" pointing to a given
* side. The .bin property is a container in which content can be
* placed. The arrow position may be controlled via
* setArrowOrigin(). The arrow side might be temporarily flipped
* depending on the box size and source position to keep the box
* totally inside the monitor if possible.
* placed. The arrow position may be controlled via setArrowOrigin().
*
*/
const BoxPointer = new Lang.Class({
@ -36,7 +26,6 @@ const BoxPointer = new Lang.Class({
_init: function(arrowSide, binProperties) {
this._arrowSide = arrowSide;
this._userArrowSide = arrowSide;
this._arrowOrigin = 0;
this.actor = new St.Bin({ x_fill: true,
y_fill: true });
@ -76,16 +65,11 @@ const BoxPointer = new Lang.Class({
show: function(animate, onComplete) {
let themeNode = this.actor.get_theme_node();
let rise = themeNode.get_length('-arrow-rise');
let animationTime = (animate & PopupAnimation.FULL) ? POPUP_ANIMATION_TIME : 0;
if (animate & PopupAnimation.FADE)
this.opacity = 0;
else
this.opacity = 255;
this.opacity = 0;
this.actor.show();
if (animate & PopupAnimation.SLIDE) {
if (animate) {
switch (this._arrowSide) {
case St.Side.TOP:
this.yOffset = -rise;
@ -111,7 +95,7 @@ const BoxPointer = new Lang.Class({
if (onComplete)
onComplete();
}),
time: animationTime });
time: POPUP_ANIMATION_TIME });
},
hide: function(animate, onComplete) {
@ -119,10 +103,8 @@ const BoxPointer = new Lang.Class({
let yOffset = 0;
let themeNode = this.actor.get_theme_node();
let rise = themeNode.get_length('-arrow-rise');
let fade = (animate & PopupAnimation.FADE);
let animationTime = (animate & PopupAnimation.FULL) ? POPUP_ANIMATION_TIME : 0;
if (animate & PopupAnimation.SLIDE) {
if (animate) {
switch (this._arrowSide) {
case St.Side.TOP:
yOffset = rise;
@ -141,15 +123,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 +199,8 @@ const BoxPointer = new Lang.Class({
}
this.bin.allocate(childBox, flags);
if (this._sourceActor && this._sourceActor.mapped) {
if (this._sourceActor && this._sourceActor.mapped)
this._reposition(this._sourceActor, this._arrowAlignment);
if (this._shouldFlip()) {
switch (this._arrowSide) {
case St.Side.TOP:
this._arrowSide = St.Side.BOTTOM;
break;
case St.Side.BOTTOM:
this._arrowSide = St.Side.TOP;
break;
case St.Side.LEFT:
this._arrowSide = St.Side.RIGHT;
break;
case St.Side.RIGHT:
this._arrowSide = St.Side.LEFT;
break;
}
this._reposition(this._sourceActor, this._arrowAlignment);
}
}
},
_drawBorder: function(area) {
@ -253,6 +214,7 @@ const BoxPointer = new Lang.Class({
let halfBorder = borderWidth / 2;
let halfBase = Math.floor(base/2);
let borderColor = themeNode.get_color('-arrow-border-color');
let backgroundColor = themeNode.get_color('-arrow-background-color');
let [width, height] = area.get_surface_size();
@ -263,6 +225,7 @@ const BoxPointer = new Lang.Class({
boxWidth -= rise;
}
let cr = area.get_context();
Clutter.cairo_set_source_color(cr, borderColor);
// Translate so that box goes from 0,0 to boxWidth,boxHeight,
// with the arrow poking out of that
@ -275,51 +238,14 @@ const BoxPointer = new Lang.Class({
let [x1, y1] = [halfBorder, halfBorder];
let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
let skipTopLeft = false;
let skipTopRight = false;
let skipBottomLeft = false;
let skipBottomRight = false;
switch (this._arrowSide) {
case St.Side.TOP:
if (this._arrowOrigin == x1)
skipTopLeft = true;
else if (this._arrowOrigin == x2)
skipTopRight = true;
break;
case St.Side.RIGHT:
if (this._arrowOrigin == y1)
skipTopRight = true;
else if (this._arrowOrigin == y2)
skipBottomRight = true;
break;
case St.Side.BOTTOM:
if (this._arrowOrigin == x1)
skipBottomLeft = true;
else if (this._arrowOrigin == x2)
skipBottomRight = true;
break;
case St.Side.LEFT:
if (this._arrowOrigin == y1)
skipTopLeft = true;
else if (this._arrowOrigin == y2)
skipBottomLeft = true;
break;
}
cr.moveTo(x1 + borderRadius, y1);
if (this._arrowSide == St.Side.TOP) {
if (skipTopLeft) {
cr.moveTo(x1, y2 - borderRadius);
cr.lineTo(x1, y1 - rise);
cr.lineTo(x1 + halfBase, y1);
} else if (skipTopRight) {
cr.lineTo(x2 - halfBase, y1);
cr.lineTo(x2, y1 - rise);
cr.lineTo(x2, y1 + borderRadius);
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
cr.lineTo(this._arrowOrigin, y1 - rise);
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y1);
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y1);
cr.lineTo(this._arrowOrigin, y1 - rise);
} else {
cr.lineTo(this._arrowOrigin - halfBase, y1);
cr.lineTo(this._arrowOrigin, y1 - rise);
@ -327,20 +253,19 @@ const BoxPointer = new Lang.Class({
}
}
if (!skipTopRight) {
cr.lineTo(x2 - borderRadius, y1);
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
3*Math.PI/2, Math.PI*2);
}
cr.lineTo(x2 - borderRadius, y1);
// top-right corner
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
3*Math.PI/2, Math.PI*2);
if (this._arrowSide == St.Side.RIGHT) {
if (skipTopRight) {
cr.lineTo(x2 + rise, y1);
cr.lineTo(x2 + rise, y1 + halfBase);
} else if (skipBottomRight) {
cr.lineTo(x2, y2 - halfBase);
cr.lineTo(x2 + rise, y2);
cr.lineTo(x2 - borderRadius, y2);
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
cr.lineTo(x2 + rise, this._arrowOrigin);
cr.lineTo(x2, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
cr.lineTo(x2, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
cr.lineTo(x2 + rise, this._arrowOrigin);
} else {
cr.lineTo(x2, this._arrowOrigin - halfBase);
cr.lineTo(x2 + rise, this._arrowOrigin);
@ -348,20 +273,19 @@ const BoxPointer = new Lang.Class({
}
}
if (!skipBottomRight) {
cr.lineTo(x2, y2 - borderRadius);
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
0, Math.PI/2);
}
cr.lineTo(x2, y2 - borderRadius);
// bottom-right corner
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
0, Math.PI/2);
if (this._arrowSide == St.Side.BOTTOM) {
if (skipBottomLeft) {
cr.lineTo(x1 + halfBase, y2);
cr.lineTo(x1, y2 + rise);
cr.lineTo(x1, y2 - borderRadius);
} else if (skipBottomRight) {
cr.lineTo(x2, y2 + rise);
cr.lineTo(x2 - halfBase, y2);
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y2);
cr.lineTo(this._arrowOrigin, y2 + rise);
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
cr.lineTo(this._arrowOrigin, y2 + rise);
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y2);
} else {
cr.lineTo(this._arrowOrigin + halfBase, y2);
cr.lineTo(this._arrowOrigin, y2 + rise);
@ -369,20 +293,19 @@ const BoxPointer = new Lang.Class({
}
}
if (!skipBottomLeft) {
cr.lineTo(x1 + borderRadius, y2);
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
Math.PI/2, Math.PI);
}
cr.lineTo(x1 + borderRadius, y2);
// bottom-left corner
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
Math.PI/2, Math.PI);
if (this._arrowSide == St.Side.LEFT) {
if (skipTopLeft) {
cr.lineTo(x1, y1 + halfBase);
cr.lineTo(x1 - rise, y1);
cr.lineTo(x1 + borderRadius, y1);
} else if (skipBottomLeft) {
cr.lineTo(x1 - rise, y2)
cr.lineTo(x1 - rise, y2 - halfBase);
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
cr.lineTo(x1, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
cr.lineTo(x1 - rise, this._arrowOrigin);
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
cr.lineTo(x1 - rise, this._arrowOrigin);
cr.lineTo(x1, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
} else {
cr.lineTo(x1, this._arrowOrigin + halfBase);
cr.lineTo(x1 - rise, this._arrowOrigin);
@ -390,26 +313,20 @@ const BoxPointer = new Lang.Class({
}
}
if (!skipTopLeft) {
cr.lineTo(x1, y1 + borderRadius);
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
Math.PI, 3*Math.PI/2);
}
cr.lineTo(x1, y1 + borderRadius);
// top-left corner
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
Math.PI, 3*Math.PI/2);
Clutter.cairo_set_source_color(cr, backgroundColor);
cr.fillPreserve();
if (borderWidth > 0) {
let borderColor = themeNode.get_color('-arrow-border-color');
Clutter.cairo_set_source_color(cr, borderColor);
cr.setLineWidth(borderWidth);
cr.stroke();
}
Clutter.cairo_set_source_color(cr, borderColor);
cr.setLineWidth(borderWidth);
cr.stroke();
},
setPosition: function(sourceActor, alignment) {
this._arrowSide = this._userArrowSide;
// We need to show it now to force an allocation,
// so that we can query the correct size.
this.actor.show();
@ -426,7 +343,11 @@ const BoxPointer = new Lang.Class({
if (!this._sourceActor)
return;
this.setPosition(this._sourceActor, this._arrowAlignment);
// We need to show it now to force an allocation,
// so that we can query the correct size.
this.actor.show();
this._reposition(this._sourceActor, this._arrowAlignment);
},
_reposition: function(sourceActor, alignment) {
@ -447,9 +368,10 @@ const BoxPointer = new Lang.Class({
let arrowBase = themeNode.get_length('-arrow-base');
let borderRadius = themeNode.get_length('-arrow-border-radius');
let margin = (4 * borderRadius + borderWidth + arrowBase);
let halfMargin = margin / 2;
let themeNode = this.actor.get_theme_node();
let gap = themeNode.get_length('-boxpointer-gap');
let padding = themeNode.get_length('-arrow-rise');
let resX, resY;
@ -468,66 +390,29 @@ const BoxPointer = new Lang.Class({
break;
}
// Now align and position the pointing axis, making sure it fits on
// screen. If the arrowOrigin is so close to the edge that the arrow
// will not be isosceles, we try to compensate as follows:
// - We skip the rounded corner and settle for a right angled arrow
// as shown below. See _drawBorder for further details.
// |\_____
// |
// |
// - If the arrow was going to be acute angled, we move the position
// of the box to maintain the arrow's accuracy.
let arrowOrigin;
let halfBase = Math.floor(arrowBase/2);
let halfBorder = borderWidth / 2;
let halfMargin = margin / 2;
let [x1, y1] = [halfBorder, halfBorder];
let [x2, y2] = [natWidth - halfBorder, natHeight - halfBorder];
// Now align and position the pointing axis, making sure
// it fits on screen
switch (this._arrowSide) {
case St.Side.TOP:
case St.Side.BOTTOM:
resX = sourceCenterX - (halfMargin + (natWidth - margin) * alignment);
resX = Math.max(resX, monitor.x + padding);
resX = Math.min(resX, monitor.x + monitor.width - (padding + natWidth));
arrowOrigin = sourceCenterX - resX;
if (arrowOrigin <= (x1 + (borderRadius + halfBase))) {
if (arrowOrigin > x1)
resX += (arrowOrigin - x1);
arrowOrigin = x1;
} else if (arrowOrigin >= (x2 - (borderRadius + halfBase))) {
if (arrowOrigin < x2)
resX -= (x2 - arrowOrigin);
arrowOrigin = x2;
}
resX = Math.max(resX, monitor.x + 10);
resX = Math.min(resX, monitor.x + monitor.width - (10 + natWidth));
this.setArrowOrigin(sourceCenterX - resX);
break;
case St.Side.LEFT:
case St.Side.RIGHT:
resY = sourceCenterY - (halfMargin + (natHeight - margin) * alignment);
resY = Math.max(resY, monitor.y + padding);
resY = Math.min(resY, monitor.y + monitor.height - (padding + natHeight));
resY = Math.max(resY, monitor.y + 10);
resY = Math.min(resY, monitor.y + monitor.height - (10 + natHeight));
arrowOrigin = sourceCenterY - resY;
if (arrowOrigin <= (y1 + (borderRadius + halfBase))) {
if (arrowOrigin > y1)
resY += (arrowOrigin - y1);
arrowOrigin = y1;
} else if (arrowOrigin >= (y2 - (borderRadius + halfBase))) {
if (arrowOrigin < y2)
resX -= (y2 - arrowOrigin);
arrowOrigin = y2;
}
this.setArrowOrigin(sourceCenterY - resY);
break;
}
this.setArrowOrigin(arrowOrigin);
let parent = this.actor.get_parent();
let success, x, y;
while (!success) {
@ -561,39 +446,6 @@ const BoxPointer = new Lang.Class({
-(this._yPosition + this._yOffset));
},
_shouldFlip: function() {
let sourceAllocation = Shell.util_get_transformed_allocation(this._sourceActor);
let boxAllocation = Shell.util_get_transformed_allocation(this.actor);
let boxWidth = boxAllocation.x2 - boxAllocation.x1;
let boxHeight = boxAllocation.y2 - boxAllocation.y1;
let monitor = Main.layoutManager.findMonitorForActor(this.actor);
switch (this._arrowSide) {
case St.Side.TOP:
if (boxAllocation.y2 > monitor.y + monitor.height &&
boxHeight < sourceAllocation.y1 - monitor.y)
return true;
break;
case St.Side.BOTTOM:
if (boxAllocation.y1 < monitor.y &&
boxHeight < monitor.y + monitor.height - sourceAllocation.y2)
return true;
break;
case St.Side.LEFT:
if (boxAllocation.x2 > monitor.x + monitor.width &&
boxWidth < sourceAllocation.x1 - monitor.x)
return true;
break;
case St.Side.RIGHT:
if (boxAllocation.x1 < monitor.x &&
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
return true;
break;
}
return false;
},
set xOffset(offset) {
this._xOffset = offset;
this._shiftActor();

View File

@ -204,11 +204,12 @@ const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIfac
function CalendarServer() {
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
g_interface_name: CalendarServerInfo.name,
g_interface_info: CalendarServerInfo,
g_name: 'org.gnome.Shell.CalendarServer',
g_object_path: '/org/gnome/Shell/CalendarServer',
g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES });
g_interface_name: CalendarServerInfo.name,
g_interface_info: CalendarServerInfo,
g_name: 'org.gnome.Shell.CalendarServer',
g_object_path: '/org/gnome/Shell/CalendarServer',
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
self.init(null);
return self;
@ -339,10 +340,22 @@ const DBusEventSource = new Lang.Class({
});
Signals.addSignalMethods(DBusEventSource.prototype);
// Calendar:
// @eventSource: is an object implementing the EventSource API, e.g. the
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
const Calendar = new Lang.Class({
Name: 'Calendar',
_init: function() {
_init: function(eventSource) {
if (eventSource) {
this._eventSource = eventSource;
this._eventSource.connect('changed', Lang.bind(this,
function() {
this._update(false);
}));
}
this._weekStart = Shell.util_get_week_start();
this._weekdate = NaN;
this._digitWidth = NaN;
@ -379,24 +392,6 @@ const Calendar = new Lang.Class({
this._buildHeader ();
},
// @eventSource: is an object implementing the EventSource API, e.g. the
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
setEventSource: function(eventSource) {
if (this._eventSource) {
this._eventSource.disconnect(this._eventSourceChangedId);
this._eventSource = null;
}
this._eventSource = eventSource;
if (this._eventSource) {
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, function() {
this._update(false);
}));
this._update(true);
}
},
// Sets the calendar to show a specific date
setDate: function(date, forceReload) {
if (!_sameDay(date, this._selectedDate)) {
@ -453,7 +448,7 @@ const Calendar = new Lang.Class({
}
// All the children after this are days, and get removed when we update the calendar
this._firstDayIndex = this.actor.get_n_children();
this._firstDayIndex = this.actor.get_children().length;
},
_onStyleChange: function(actor, event) {
@ -556,7 +551,6 @@ const Calendar = new Lang.Class({
let row = 2;
while (true) {
let button = new St.Button({ label: iter.getDate().toString() });
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
if (!this._eventSource)
button.reactive = false;
@ -577,10 +571,7 @@ const Calendar = new Lang.Class({
// Hack used in lieu of border-collapse - see gnome-shell.css
if (row == 2)
styleClass = 'calendar-day-top ' + styleClass;
let leftMost = rtl ? iter.getDay() == (this._weekStart + 6) % 7
: iter.getDay() == this._weekStart;
if (leftMost)
if (iter.getDay() == this._weekStart)
styleClass = 'calendar-day-left ' + styleClass;
if (_sameDay(now, iter))
@ -627,25 +618,16 @@ Signals.addSignalMethods(Calendar.prototype);
const EventsList = new Lang.Class({
Name: 'EventsList',
_init: function() {
_init: function(eventSource) {
this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
this._date = new Date();
this._eventSource = eventSource;
this._eventSource.connect('changed', Lang.bind(this, this._update));
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
this._weekStart = Shell.util_get_week_start();
},
setEventSource: function(eventSource) {
if (this._eventSource) {
this._eventSource.disconnect(this._eventSourceChangedId);
this._eventSource = null;
}
this._eventSource = eventSource;
if (this._eventSource) {
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, this._update));
}
this._update();
},
_addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
@ -731,15 +713,13 @@ const EventsList = new Lang.Class({
let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
this._addPeriod(_("Tomorrow"), tomorrowBegin, tomorrowEnd, false, true);
let dayInWeek = (dayEnd.getDay() - this._weekStart + 7) % 7;
if (dayInWeek < 5) {
if (dayEnd.getDay() <= 4 + this._weekStart) {
/* If now is within the first 5 days we show "This week" and
* include events up until and including Saturday/Sunday
* (depending on whether a week starts on Sunday/Monday).
*/
let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
let thisWeekEnd = new Date(dayEnd.getTime() + (6 - dayInWeek) * 86400 * 1000);
let thisWeekEnd = new Date(dayEnd.getTime() + (6 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
this._addPeriod(_("This week"), thisWeekBegin, thisWeekEnd, true, false);
} else {
/* otherwise it's one of the two last days of the week ... show
@ -747,7 +727,7 @@ const EventsList = new Lang.Class({
* Saturday/Sunday
*/
let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
let nextWeekEnd = new Date(dayEnd.getTime() + (13 - dayInWeek) * 86400 * 1000);
let nextWeekEnd = new Date(dayEnd.getTime() + (13 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
this._addPeriod(_("Next week"), nextWeekBegin, nextWeekEnd, true, false);
}
},

View File

@ -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();
}
});

View File

@ -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;

View File

@ -1,58 +0,0 @@
const Lang = imports.lang;
const Gio = imports.gi.Gio;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Recorder = new Lang.Class({
Name: 'Recorder',
_init: function() {
this._recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
this._desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' });
this._bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
this._recorder = null;
},
enable: function() {
global.display.add_keybinding('toggle-recording',
this._bindingSettings,
Meta.KeyBindingFlags.NONE, Lang.bind(this, this._toggleRecorder));
},
disable: function() {
global.display.remove_keybinding('toggle-recording');
},
_ensureRecorder: function() {
if (this._recorder == null)
this._recorder = new Shell.Recorder({ stage: global.stage });
return this._recorder;
},
_toggleRecorder: function() {
let recorder = this._ensureRecorder();
if (recorder.is_recording()) {
recorder.close();
Meta.enable_unredirect_for_screen(global.screen);
} else if (!this._desktopLockdownSettings.get_boolean('disable-save-to-disk')) {
// read the parameters from GSettings always in case they have changed
recorder.set_framerate(this._recorderSettings.get_int('framerate'));
/* Translators: this is a filename used for screencast recording */
// xgettext:no-c-format
recorder.set_filename(_("Screencast from %d %t") + '.' + this._recorderSettings.get_string('file-extension'));
let pipeline = this._recorderSettings.get_string('pipeline');
if (!pipeline.match(/^\s*$/))
recorder.set_pipeline(pipeline);
else
recorder.set_pipeline(null);
Meta.disable_unredirect_for_screen(global.screen);
recorder.record();
}
}
});
const Component = Recorder;

196
js/ui/contactDisplay.js Normal file
View File

@ -0,0 +1,196 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Folks = imports.gi.Folks
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Atk = imports.gi.Atk;
const Util = imports.misc.util;
const IconGrid = imports.ui.iconGrid;
const Search = imports.ui.search;
const SearchDisplay = imports.ui.searchDisplay;
const MAX_SEARCH_RESULTS_ROWS = 1;
const ICON_SIZE = 81;
function launchContact(id) {
Util.spawn(['gnome-contacts', '-i', id]);
}
/* This class represents a shown contact search result in the overview */
const Contact = new Lang.Class({
Name: 'Contact',
_init: function(id) {
this._contactSys = Shell.ContactSystem.get_default();
this.individual = this._contactSys.get_individual(id);
this.actor = new St.Bin({ style_class: 'contact',
reactive: true,
can_focus: true,
track_hover: true,
accessible_role: Atk.Role.PUSH_BUTTON });
let content = new St.BoxLayout( { style_class: 'contact-content',
vertical: false });
this.actor.set_child(content);
let icon = new St.Icon({ icon_type: St.IconType.FULLCOLOR,
icon_size: ICON_SIZE,
style_class: 'contact-icon' });
if (this.individual.avatar != null)
icon.gicon = this.individual.avatar;
else
icon.icon_name = 'avatar-default';
content.add(icon, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
let details = new St.BoxLayout({ style_class: 'contact-details',
vertical: true });
content.add(details, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
let email = this._contactSys.get_email_for_display(this.individual);
let aliasText = this.individual.alias ||
this.individual.full_name ||
this.individual.nickname ||
email ||
_("Unknown");
let aliasLabel = new St.Label({ text: aliasText,
style_class: 'contact-details-alias' });
details.add(aliasLabel, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
this.actor.label_actor = aliasLabel;
let presence = this._createPresence(this.individual.presence_type);
details.add(presence, { x_fill: false,
y_fill: true,
x_align: St.Align.START,
y_align: St.Align.END });
},
_createPresence: function(presence) {
let text;
let iconName;
switch(presence) {
case Folks.PresenceType.AVAILABLE:
text = _("Available");
iconName = 'user-available';
break;
case Folks.PresenceType.AWAY:
case Folks.PresenceType.EXTENDED_AWAY:
text = _("Away");
iconName = 'user-away';
break;
case Folks.PresenceType.BUSY:
text = _("Busy");
iconName = 'user-busy';
break;
case Folks.PresenceType.OFFLINE:
text = _("Offline");
iconName = 'user-offline';
break;
default:
text = '';
iconName = null;
}
let box = new St.BoxLayout({ vertical: false,
style_class: 'contact-details-status' });
if (iconName) {
let icon = new St.Icon({ icon_name: iconName,
icon_type: St.IconType.FULLCOLOR,
icon_size: 16,
style_class: 'contact-details-status-icon' });
box.add(icon, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
}
let label = new St.Label({ text: text });
box.add(label, { x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.START });
return box;
},
createIcon: function(size) {
let tc = St.TextureCache.get_default();
let icon = this.individual.avatar;
if (icon != null) {
return tc.load_gicon(null, icon, size);
} else {
return tc.load_icon_name(null, 'avatar-default', St.IconType.FULLCOLOR, size);
}
},
});
/* Searches for and returns contacts */
const ContactSearchProvider = new Lang.Class({
Name: 'ContactSearchProvider',
Extends: Search.SearchProvider,
_init: function() {
this.parent(_("CONTACTS"));
this._contactSys = Shell.ContactSystem.get_default();
},
getResultMetas: function(ids) {
let metas = [];
for (let i = 0; i < ids.length; i++) {
let contact = new Contact(ids[i]);
metas.push({ 'id': ids[i],
'name': contact.alias,
'createIcon': function(size) {
return contact.createIcon(size);
}
});
}
return metas;
},
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);
}
});

View File

@ -27,6 +27,7 @@ const CtrlAltTabManager = new Lang.Class({
_init: function() {
this._items = [];
this._focusManager = St.FocusManager.get_for_stage(global.stage);
},
addGroup: function(root, name, icon, params) {
@ -40,11 +41,11 @@ const CtrlAltTabManager = new Lang.Class({
this._items.push(item);
root.connect('destroy', Lang.bind(this, function() { this.removeGroup(root); }));
global.focus_manager.add_group(root);
this._focusManager.add_group(root);
},
removeGroup: function(root) {
global.focus_manager.remove_group(root);
this._focusManager.remove_group(root);
for (let i = 0; i < this._items.length; i++) {
if (this._items[i].root == root) {
this._items.splice(i, 1);
@ -54,17 +55,16 @@ const CtrlAltTabManager = new Lang.Class({
},
focusGroup: function(item) {
if (item.window) {
Main.activateWindow(item.window);
} else if (item.focusCallback) {
item.focusCallback();
} else {
if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
global.stage_input_mode == Shell.StageInputMode.NORMAL)
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
global.stage_input_mode == Shell.StageInputMode.NORMAL)
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
if (item.window)
Main.activateWindow(item.window);
else if (item.focusCallback)
item.focusCallback();
else
item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
}
},
// Sort the items into a consistent order; panel first, tray last,
@ -319,6 +319,7 @@ const CtrlAltTabSwitcher = new Lang.Class({
let icon = item.iconActor;
if (!icon) {
icon = new St.Icon({ icon_name: item.iconName,
icon_type: St.IconType.SYMBOLIC,
icon_size: POPUP_APPICON_SIZE });
}
box.add(icon, { x_fill: false, y_fill: false } );

View File

@ -21,18 +21,6 @@ const DASH_ITEM_LABEL_SHOW_TIME = 0.15;
const DASH_ITEM_LABEL_HIDE_TIME = 0.1;
const DASH_ITEM_HOVER_TIMEOUT = 300;
function getAppFromSource(source) {
if (source instanceof AppDisplay.AppWellIcon) {
let appSystem = Shell.AppSystem.get_default();
return appSystem.lookup_app(source.getId());
} else if (source.metaWindow) {
let tracker = Shell.WindowTracker.get_default();
return tracker.get_window_app(source.metaWindow);
} else {
return null;
}
}
// A container like StBin, but taking the child's scale into account
// when requesting a size
const DashItemContainer = new Lang.Class({
@ -48,11 +36,7 @@ const DashItemContainer = new Lang.Class({
Lang.bind(this, this._allocate));
this.actor._delegate = this;
this._labelText = "";
this.label = new St.Label({ style_class: 'dash-label'});
this.label.hide();
Main.layoutManager.addChrome(this.label);
this.actor.label_actor = this.label;
this.label = null;
this.child = null;
this._childScale = 1;
@ -107,10 +91,9 @@ const DashItemContainer = new Lang.Class({
},
showLabel: function() {
if (!this._labelText)
if (this.label == null)
return;
this.label.set_text(this._labelText);
this.label.opacity = 0;
this.label.show();
@ -141,11 +124,16 @@ const DashItemContainer = new Lang.Class({
},
setLabelText: function(text) {
this._labelText = text;
this.child.accessible_name = text;
if (this.label == null)
this.label = new St.Label({ style_class: 'dash-label'});
this.label.set_text(text);
Main.layoutManager.addChrome(this.label);
this.label.hide();
},
hideLabel: function () {
this.label.opacity = 255;
Tweener.addTween(this.label,
{ opacity: 0,
time: DASH_ITEM_LABEL_HIDE_TIME,
@ -239,76 +227,52 @@ const DashItemContainer = new Lang.Class({
}
});
const ShowAppsIcon = new Lang.Class({
Name: 'ShowAppsIcon',
const RemoveFavoriteIcon = new Lang.Class({
Name: 'RemoveFavoriteIcon',
Extends: DashItemContainer,
_init: function() {
this.parent();
this.toggleButton = new St.Button({ style_class: 'show-apps',
track_hover: true,
can_focus: true,
toggle_mode: true });
this._iconBin = new St.Bin({ style_class: 'remove-favorite' });
this._iconActor = null;
this.icon = new IconGrid.BaseIcon(_("Show Applications"),
this.icon = new IconGrid.BaseIcon(_("Remove"),
{ setSizeManually: true,
showLabel: false,
createIcon: Lang.bind(this, this._createIcon) });
this.toggleButton.add_actor(this.icon.actor);
this.toggleButton._delegate = this;
this._iconBin.set_child(this.icon.actor);
this._iconBin._delegate = this;
this.setChild(this.toggleButton);
this.setDragApp(null);
this.setChild(this._iconBin);
},
_createIcon: function(size) {
this._iconActor = new St.Icon({ icon_name: 'view-grid-symbolic',
icon_size: size,
style_class: 'show-apps-icon',
track_hover: true });
this._iconActor = new St.Icon({ icon_name: 'user-trash',
style_class: 'remove-favorite-icon',
icon_size: size });
return this._iconActor;
},
_canRemoveApp: function(app) {
if (app == null)
return false;
let id = app.get_id();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(id);
return isFavorite;
},
setDragApp: function(app) {
let canRemove = this._canRemoveApp(app);
this.toggleButton.set_hover(canRemove);
setHover: function(hovered) {
this._iconBin.set_hover(hovered);
if (this._iconActor)
this._iconActor.set_hover(canRemove);
if (canRemove)
this.setLabelText(_("Remove from Favorites"));
else
this.setLabelText(_("Show Applications"));
this._iconActor.set_hover(hovered);
},
// Rely on the dragged item being a favorite
handleDragOver: function(source, actor, x, y, time) {
let app = getAppFromSource(source);
if (app == null)
return DND.DragMotionResult.NO_DROP;
let id = app.get_id();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(id);
if (!isFavorite)
return DND.DragMotionResult.NO_DROP;
return DND.DragMotionResult.MOVE_DROP;
},
acceptDrop: function(source, actor, x, y, time) {
let app = getAppFromSource(source);
if (app == null)
return false;
let app = null;
if (source instanceof AppDisplay.AppWellIcon) {
let appSystem = Shell.AppSystem.get_default();
app = appSystem.lookup_app(source.getId());
} else if (source.metaWindow) {
let tracker = Shell.WindowTracker.get_default();
app = tracker.get_window_app(source.metaWindow);
}
let id = app.get_id();
@ -332,39 +296,6 @@ const DragPlaceholderItem = new Lang.Class({
}
});
const DashActor = new Lang.Class({
Name: 'DashActor',
Extends: St.Widget,
_init: function() {
let layout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.VERTICAL });
this.parent({ name: 'dash',
layout_manager: layout,
clip_to_allocation: true });
},
vfunc_allocate: function(box, flags) {
let contentBox = this.get_theme_node().get_content_box(box);
let availWidth = contentBox.x2 - contentBox.x1;
this.set_allocation(box, flags);
let [appIcons, showAppsButton] = this.get_children();
let [showAppsMinHeight, showAppsNatHeight] = showAppsButton.get_preferred_height(availWidth);
let childBox = new Clutter.ActorBox();
childBox.x1 = contentBox.x1;
childBox.y1 = contentBox.y1;
childBox.x2 = contentBox.x2;
childBox.y2 = contentBox.y2 - showAppsNatHeight;
appIcons.allocate(childBox, flags);
childBox.y1 = contentBox.y2 - showAppsNatHeight;
childBox.y2 = contentBox.y2;
showAppsButton.allocate(childBox, flags);
}
});
const Dash = new Lang.Class({
Name: 'Dash',
@ -376,25 +307,17 @@ const Dash = new Lang.Class({
this._dragPlaceholder = null;
this._dragPlaceholderPos = -1;
this._animatingPlaceholdersCount = 0;
this._favRemoveTarget = null;
this._showLabelTimeoutId = 0;
this._resetHoverTimeoutId = 0;
this._labelShowing = false;
this._container = new DashActor();
this._box = new St.BoxLayout({ vertical: true,
this._box = new St.BoxLayout({ name: 'dash',
vertical: true,
clip_to_allocation: true });
this._box._delegate = this;
this._container.add_actor(this._box);
this._showAppsIcon = new ShowAppsIcon();
this._showAppsIcon.icon.setIconSize(this.iconSize);
this._hookUpLabel(this._showAppsIcon);
this.showAppsButton = this._showAppsIcon.toggleButton;
this._container.add_actor(this._showAppsIcon.actor);
this.actor = new St.Bin({ child: this._container });
this.actor = new St.Bin({ y_align: St.Align.START, child: this._box });
this.actor.connect('notify::height', Lang.bind(this,
function() {
if (this._maxHeight != this.actor.height)
@ -404,6 +327,7 @@ const Dash = new Lang.Class({
this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay));
this._tracker = Shell.WindowTracker.get_default();
this._appSystem = Shell.AppSystem.get_default();
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
@ -446,25 +370,54 @@ const Dash = new Lang.Class({
_endDrag: function() {
this._clearDragPlaceholder();
this._showAppsIcon.setDragApp(null);
if (this._favRemoveTarget) {
this._favRemoveTarget.animateOutAndDestroy();
this._favRemoveTarget.actor.connect('destroy', Lang.bind(this,
function() {
this._favRemoveTarget = null;
}));
this._adjustIconSize();
}
DND.removeDragMonitor(this._dragMonitor);
},
_onDragMotion: function(dragEvent) {
let app = getAppFromSource(dragEvent.source);
if (app == null)
let app = null;
if (dragEvent.source instanceof AppDisplay.AppWellIcon)
app = this._appSystem.lookup_app(dragEvent.source.getId());
else if (dragEvent.source.metaWindow)
app = this._tracker.get_window_app(dragEvent.source.metaWindow);
else
return DND.DragMotionResult.CONTINUE;
let showAppsHovered =
this._showAppsIcon.actor.contains(dragEvent.targetActor);
let id = app.get_id();
if (!this._box.contains(dragEvent.targetActor) || showAppsHovered)
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
let srcIsFavorite = (id in favorites);
if (srcIsFavorite &&
app.get_state() != Shell.AppState.RUNNING &&
dragEvent.source.actor &&
this.actor.contains (dragEvent.source.actor) &&
this._favRemoveTarget == null) {
this._favRemoveTarget = new RemoveFavoriteIcon();
this._favRemoveTarget.icon.setIconSize(this.iconSize);
this._box.add(this._favRemoveTarget.actor);
this._adjustIconSize();
this._favRemoveTarget.animateIn();
}
let favRemoveHovered = false;
if (this._favRemoveTarget)
favRemoveHovered =
this._favRemoveTarget.actor.contains(dragEvent.targetActor);
if (!this._box.contains(dragEvent.targetActor) || favRemoveHovered)
this._clearDragPlaceholder();
if (showAppsHovered)
this._showAppsIcon.setDragApp(app);
else
this._showAppsIcon.setDragApp(null);
if (this._favRemoveTarget)
this._favRemoveTarget.setHover(favRemoveHovered);
return DND.DragMotionResult.CONTINUE;
},
@ -480,17 +433,6 @@ const Dash = new Lang.Class({
Main.queueDeferredWork(this._workId);
},
_hookUpLabel: function(item) {
item.child.connect('notify::hover', Lang.bind(this, function() {
this._onHover(item);
}));
Main.overview.connect('hiding', Lang.bind(this, function() {
this._labelShowing = false;
item.hideLabel();
}));
},
_createAppItem: function(app) {
let display = new AppDisplay.AppWellIcon(app,
{ setSizeManually: true,
@ -507,19 +449,21 @@ const Dash = new Lang.Class({
let item = new DashItemContainer();
item.setChild(display.actor);
// Override default AppWellIcon label_actor, now the
// accessible_name is set at DashItemContainer.setLabelText
display.actor.label_actor = null;
item.setLabelText(app.get_name());
// Override default AppWellIcon label_actor
display.actor.label_actor = item.label;
display.icon.setIconSize(this.iconSize);
this._hookUpLabel(item);
display.actor.connect('notify::hover',
Lang.bind(this, function() {
this._onHover(item, display)
}));
return item;
},
_onHover: function (item) {
if (item.child.get_hover() && !item.child._delegate.isMenuUp) {
_onHover: function (item, display) {
if (display.actor.get_hover() && !display.isMenuUp) {
if (this._showLabelTimeoutId == 0) {
let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
@ -530,7 +474,7 @@ const Dash = new Lang.Class({
}));
if (this._resetHoverTimeoutId > 0) {
Mainloop.source_remove(this._resetHoverTimeoutId);
this._resetHoverTimeoutId = 0;
this._resetHoverTimeoutId = 0;
}
}
} else {
@ -560,12 +504,18 @@ const Dash = new Lang.Class({
!actor._delegate.animatingOut;
});
iconChildren.push(this._showAppsIcon.actor);
if (iconChildren.length == 0) {
this._box.add_style_pseudo_class('empty');
return;
}
this._box.remove_style_pseudo_class('empty');
if (this._maxHeight == -1)
return;
let themeNode = this._container.get_theme_node();
let themeNode = this._box.get_theme_node();
let maxAllocation = new Clutter.ActorBox({ x1: 0, y1: 0,
x2: 42 /* whatever */,
y2: this._maxHeight });
@ -591,6 +541,7 @@ const Dash = new Lang.Class({
[minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
}
// Subtract icon padding and box spacing from the available height
availHeight -= iconChildren.length * (natHeight - this.iconSize) +
(iconChildren.length - 1) * spacing;
@ -621,10 +572,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();
@ -748,9 +697,8 @@ const Dash = new Lang.Class({
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();
@ -765,9 +713,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++)
@ -783,7 +730,11 @@ const Dash = new Lang.Class({
},
handleDragOver : function(source, actor, x, y, time) {
let app = getAppFromSource(source);
let app = null;
if (source instanceof AppDisplay.AppWellIcon)
app = this._appSystem.lookup_app(source.getId());
else if (source.metaWindow)
app = this._tracker.get_window_app(source.metaWindow);
// Don't allow favoriting of transient apps
if (app == null || app.is_window_backed())
@ -864,7 +815,12 @@ const Dash = new Lang.Class({
// Draggable target interface
acceptDrop : function(source, actor, x, y, time) {
let app = getAppFromSource(source);
let app = null;
if (source instanceof AppDisplay.AppWellIcon) {
app = this._appSystem.lookup_app(source.getId());
} else if (source.metaWindow) {
app = this._tracker.get_window_app(source.metaWindow);
}
// Don't allow favoriting of transient apps
if (app == null || app.is_window_backed()) {

View File

@ -2,7 +2,6 @@
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Cairo = imports.cairo;
@ -17,6 +16,14 @@ const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Calendar = imports.ui.calendar;
const UPowerGlib = imports.gi.UPowerGlib;
// in org.gnome.desktop.interface
const CLOCK_FORMAT_KEY = 'clock-format';
// in org.gnome.shell.clock
const CLOCK_SHOW_DATE_KEY = 'show-date';
const CLOCK_SHOW_SECONDS_KEY = 'show-seconds';
function _onVertSepRepaint (area)
{
@ -38,7 +45,9 @@ const DateMenuButton = new Lang.Class({
Name: 'DateMenuButton',
Extends: PanelMenu.Button,
_init: function() {
_init: function(params) {
params = Params.parse(params, { showEvents: true });
let item;
let hbox;
let vbox;
@ -53,8 +62,8 @@ const DateMenuButton = new Lang.Class({
// role ATK_ROLE_MENU like other elements of the panel.
this.actor.accessible_role = Atk.Role.LABEL;
this._clockDisplay = new St.Label();
this.actor.add_actor(this._clockDisplay);
this._clock = new St.Label();
this.actor.add_actor(this._clock);
hbox = new St.BoxLayout({name: 'calendarArea' });
this.menu.addActor(hbox);
@ -66,12 +75,20 @@ const DateMenuButton = new Lang.Class({
// Date
this._date = new St.Label();
this.actor.label_actor = this._clockDisplay;
this.actor.label_actor = this._date;
this._date.style_class = 'datemenu-date-label';
vbox.add(this._date);
this._eventList = new Calendar.EventsList();
this._calendar = new Calendar.Calendar();
if (params.showEvents) {
this._eventSource = new Calendar.DBusEventSource();
this._eventList = new Calendar.EventsList(this._eventSource);
} else {
this._eventSource = null;
this._eventList = null;
}
// Calendar
this._calendar = new Calendar.Calendar(this._eventSource);
this._calendar.connect('selected-date-changed',
Lang.bind(this, function(calendar, date) {
@ -89,34 +106,31 @@ const DateMenuButton = new Lang.Class({
separator.setColumnWidths(1);
vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false});
item.actor.show_on_set_parent = false;
item.actor.can_focus = false;
item.actor.reparent(vbox);
this._dateAndTimeSeparator = separator;
}
this._separator = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
pseudo_class: 'highlighted' });
this._separator.connect('repaint', Lang.bind(this, _onVertSepRepaint));
hbox.add(this._separator);
if (params.showEvents) {
// Add vertical separator
// Fill up the second column
vbox = new St.BoxLayout({ name: 'calendarEventsArea',
vertical: true });
hbox.add(vbox, { expand: true });
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
pseudo_class: 'highlighted' });
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
hbox.add(item);
// Event list
vbox.add(this._eventList.actor, { expand: true });
// Fill up the second column
vbox = new St.BoxLayout({name: 'calendarEventsArea',
vertical: true});
hbox.add(vbox, { expand: true });
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
this._openCalendarItem.actor.can_focus = false;
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
// Event list
vbox.add(this._eventList.actor, { expand: true });
this._calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
this._calendarSettings.connect('changed::exec',
Lang.bind(this, this._calendarSettingsChanged));
this._calendarSettingsChanged();
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
item.actor.can_focus = false;
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
}
// Whenever the menu is opened, select today
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
@ -143,75 +157,79 @@ const DateMenuButton = new Lang.Class({
// Done with hbox for calendar and event list
this._clock = new GnomeDesktop.WallClock();
this._clock.connect('notify::clock', Lang.bind(this, this._updateClockAndDate));
// Track changes to clock settings
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this._clockSettings = new Gio.Settings({ schema: 'org.gnome.shell.clock' });
this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
this._clockSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
// https://bugzilla.gnome.org/show_bug.cgi?id=655129
this._upClient = new UPowerGlib.Client();
this._upClient.connect('notify-resume', Lang.bind(this, this._updateClockAndDate));
// Start the clock
this._updateClockAndDate();
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
},
_calendarSettingsChanged: function() {
let exec = this._calendarSettings.get_string('exec');
let fullExec = GLib.find_program_in_path(exec);
this._openCalendarItem.actor.visible = fullExec != null;
},
_setEventsVisibility: function(visible) {
this._openCalendarItem.actor.visible = visible;
this._separator.visible = visible;
if (visible) {
let alignment = 0.25;
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
alignment = 1.0 - alignment;
this.menu._arrowAlignment = alignment;
this._eventList.actor.get_parent().show();
} else {
this.menu._arrowAlignment = 0.5;
this._eventList.actor.get_parent().hide();
}
},
_setEventSource: function(eventSource) {
this._calendar.setEventSource(eventSource);
this._eventList.setEventSource(eventSource);
},
_sessionUpdated: function() {
let eventSource;
let showEvents = Main.sessionMode.showCalendarEvents;
if (showEvents) {
eventSource = new Calendar.DBusEventSource();
} else {
eventSource = null;
}
this._setEventSource(eventSource);
this._setEventsVisibility(showEvents);
// This needs to be handled manually, as the code to
// autohide separators doesn't work across the vbox
this._dateAndTimeSeparator.actor.visible = Main.sessionMode.allowSettings;
},
_updateClockAndDate: function() {
this._clockDisplay.set_text(this._clock.clock);
let format = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
let showDate = this._clockSettings.get_boolean(CLOCK_SHOW_DATE_KEY);
let showSeconds = this._clockSettings.get_boolean(CLOCK_SHOW_SECONDS_KEY);
let clockFormat;
let dateFormat;
switch (format) {
case '24h':
if (showDate)
/* Translators: This is the time format with date used
in 24-hour mode. */
clockFormat = showSeconds ? _("%a %b %e, %R:%S")
: _("%a %b %e, %R");
else
/* Translators: This is the time format without date used
in 24-hour mode. */
clockFormat = showSeconds ? _("%a %R:%S")
: _("%a %R");
break;
case '12h':
default:
if (showDate)
/* Translators: This is a time format with date used
for AM/PM. */
clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
: _("%a %b %e, %l:%M %p");
else
/* Translators: This is a time format without date used
for AM/PM. */
clockFormat = showSeconds ? _("%a %l:%M:%S %p")
: _("%a %l:%M %p");
break;
}
let displayDate = new Date();
this._clock.set_text(displayDate.toLocaleFormat(clockFormat));
/* Translators: This is the date format to use when the calendar popup is
* shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
*/
let dateFormat = _("%A %B %e, %Y");
let displayDate = new Date();
dateFormat = _("%A %B %e, %Y");
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
Mainloop.timeout_add_seconds(1, Lang.bind(this, this._updateClockAndDate));
return false;
},
_onOpenCalendarActivate: function() {
this.menu.close();
let tool = this._calendarSettings.get_string('exec');
if (tool.length == 0 || tool.substr(0, 9) == 'evolution') {
let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
let tool = calendarSettings.get_string('exec');
if (tool.length == 0 || tool == 'evolution') {
// TODO: pass the selected day
let app = Shell.AppSystem.get_default().lookup_app('evolution-calendar.desktop');
app.activate();
Util.spawn(['evolution', '-c', 'calendar']);
} else {
let needTerm = this._calendarSettings.get_boolean('needs-term');
let needTerm = calendarSettings.get_boolean('needs-term');
if (needTerm) {
let terminalSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.terminal' });
let term = terminalSettings.get_string('exec');

View File

@ -235,10 +235,6 @@ const _Draggable = new Lang.Class({
if (this.actor._delegate && this.actor._delegate.getDragActor) {
this._dragActor = this.actor._delegate.getDragActor(this._dragStartX, this._dragStartY);
this._dragActor.reparent(Main.uiGroup);
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
// Drag actor does not always have to be the same as actor. For example drag actor
// can be an image that's part of the actor. So to perform "snap back" correctly we need
// to know what was the drag actor source.
@ -267,17 +263,12 @@ const _Draggable = new Lang.Class({
this._dragOffsetY = this._dragActor.y - this._dragStartY;
} else {
this._dragActor = this.actor;
this._dragActorSource = undefined;
this._dragOrigParent = this.actor.get_parent();
this._dragOrigX = this._dragActor.x;
this._dragOrigY = this._dragActor.y;
this._dragOrigScale = this._dragActor.scale_x;
this._dragActor.reparent(Main.uiGroup);
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
let [actorStageX, actorStageY] = this.actor.get_transformed_position();
this._dragOffsetX = actorStageX - this._dragStartX;
this._dragOffsetY = actorStageY - this._dragStartY;
@ -289,6 +280,10 @@ const _Draggable = new Lang.Class({
scaledHeight / this.actor.height);
}
this._dragActor.reparent(Main.uiGroup);
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
this._dragOrigOpacity = this._dragActor.opacity;
if (this._dragActorOpacity != undefined)
this._dragActor.opacity = this._dragActorOpacity;

View File

@ -31,10 +31,10 @@ const St = imports.gi.St;
const Shell = imports.gi.Shell;
const GnomeSession = imports.misc.gnomeSession;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const Tweener = imports.ui.tweener;
const UserMenu = imports.ui.userMenu;
let _endSessionDialog = null;
@ -90,7 +90,7 @@ const shutdownDialogContent = {
label: C_("button", "Restart") },
{ signal: 'ConfirmedShutdown',
label: C_("button", "Power Off") }],
iconName: 'system-shutdown-symbolic',
iconName: 'system-shutdown',
iconStyleClass: 'end-session-dialog-shutdown-icon'
};
@ -105,7 +105,7 @@ const restartDialogContent = {
endDescription: _("Restarting the system."),
confirmButtons: [{ signal: 'ConfirmedReboot',
label: C_("button", "Restart") }],
iconName: 'system-shutdown-symbolic',
iconName: 'system-shutdown',
iconStyleClass: 'end-session-dialog-shutdown-icon'
};
@ -161,7 +161,6 @@ const ListItem = new Lang.Class({
this._descriptionLabel = new St.Label({ text: this._reason,
style_class: 'end-session-dialog-app-list-item-description' });
this.actor.label_actor = this._nameLabel;
textLayout.add(this._descriptionLabel,
{ expand: true,
x_fill: true });
@ -281,17 +280,21 @@ const EndSessionDialog = new Lang.Class({
scrollView.hide();
this._applicationList = new St.BoxLayout({ vertical: true });
scrollView.add_actor(this._applicationList);
scrollView.add_actor(this._applicationList,
{ x_fill: true,
y_fill: true,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
this._applicationList.connect('actor-added',
Lang.bind(this, function() {
if (this._applicationList.get_n_children() == 1)
if (this._applicationList.get_children().length == 1)
scrollView.show();
}));
this._applicationList.connect('actor-removed',
Lang.bind(this, function() {
if (this._applicationList.get_n_children() == 0)
if (this._applicationList.get_children().length == 0)
scrollView.hide();
}));
@ -304,7 +307,42 @@ const EndSessionDialog = new Lang.Class({
this._user.disconnect(this._userChangedId);
},
_updateDescription: function() {
_setIconFromFile: function(iconFile, styleClass) {
if (styleClass)
this._iconBin.set_style_class_name(styleClass);
this._iconBin.set_style(null);
this._iconBin.child = null;
if (iconFile) {
this._iconBin.show();
this._iconBin.set_style('background-image: url("' + iconFile + '");' +
'background-size: contain;');
} else {
this._iconBin.hide();
}
},
_setIconFromName: function(iconName, styleClass) {
if (styleClass)
this._iconBin.set_style_class_name(styleClass);
this._iconBin.set_style(null);
if (iconName != null) {
let textureCache = St.TextureCache.get_default();
let icon = textureCache.load_icon_name(this._iconBin.get_theme_node(),
iconName,
St.IconType.SYMBOLIC,
_DIALOG_ICON_SIZE);
this._iconBin.child = icon;
this._iconBin.show();
} else {
this._iconBin.child = null;
this._iconBin.hide();
}
},
_updateContent: function() {
if (this.state != ModalDialog.State.OPENING &&
this.state != ModalDialog.State.OPENED)
return;
@ -314,6 +352,17 @@ const EndSessionDialog = new Lang.Class({
let subject = dialogContent.subject;
let description;
if (this._user.is_loaded && !dialogContent.iconName) {
let iconFile = this._user.get_icon_file();
if (GLib.file_test(iconFile, GLib.FileTest.EXISTS))
this._setIconFromFile(iconFile, dialogContent.iconStyleClass);
else
this._setIconFromName('avatar-default', dialogContent.iconStyleClass);
} else if (dialogContent.iconName) {
this._setIconFromName(dialogContent.iconName,
dialogContent.iconStyleClass);
}
if (this._inhibitors.length > 0) {
this._stopTimer();
description = dialogContent.inhibitedDescription;
@ -346,27 +395,6 @@ const EndSessionDialog = new Lang.Class({
_setLabelText(this._descriptionLabel, description);
},
_updateContent: function() {
if (this.state != ModalDialog.State.OPENING &&
this.state != ModalDialog.State.OPENED)
return;
let dialogContent = DialogContent[this._type];
if (dialogContent.iconName) {
this._iconBin.child = new St.Icon({ icon_name: dialogContent.iconName,
icon_size: _DIALOG_ICON_SIZE,
style_class: dialogContent.iconStyleClass });
} else {
let avatarWidget = new UserMenu.UserAvatarWidget(this._user,
{ iconSize: _DIALOG_ICON_SIZE,
styleClass: dialogContent.iconStyleClass });
this._iconBin.child = avatarWidget.actor;
avatarWidget.update();
}
this._updateDescription();
},
_updateButtons: function() {
let dialogContent = DialogContent[this._type];
let buttons = [{ action: Lang.bind(this, this.cancel),
@ -413,7 +441,7 @@ const EndSessionDialog = new Lang.Class({
{ _secondsLeft: 0,
time: this._secondsLeft,
transition: 'linear',
onUpdate: Lang.bind(this, this._updateDescription),
onUpdate: Lang.bind(this, this._updateContent),
onComplete: Lang.bind(this, function() {
let dialogContent = DialogContent[this._type];
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];

View File

@ -90,7 +90,7 @@ function init() {
}
// OK, now things are initialized enough that we can import shell JS
const Format = imports.format;
const Format = imports.misc.format;
const Tweener = imports.ui.tweener;
Tweener.init();

View File

@ -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());
}

View File

@ -3,12 +3,19 @@
const Lang = imports.lang;
const Signals = imports.signals;
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Soup = imports.gi.Soup;
const Config = imports.misc.config;
const ExtensionUtils = imports.misc.extensionUtils;
const Main = imports.ui.main;
const FileUtils = imports.misc.fileUtils;
const ModalDialog = imports.ui.modalDialog;
const API_VERSION = 1;
const ExtensionState = {
ENABLED: 1,
@ -23,6 +30,29 @@ const ExtensionState = {
UNINSTALLED: 99
};
const REPOSITORY_URL_BASE = 'https://extensions.gnome.org';
const REPOSITORY_URL_DOWNLOAD = REPOSITORY_URL_BASE + '/download-extension/%s.shell-extension.zip';
const REPOSITORY_URL_INFO = REPOSITORY_URL_BASE + '/extension-info/';
const _httpSession = new Soup.SessionAsync();
// The unfortunate state of gjs, gobject-introspection and libsoup
// means that I have to do a hack to add a feature.
// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
if (Soup.Session.prototype.add_feature != null)
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
function _getCertFile() {
let localCert = GLib.build_filenamev([global.userdatadir, 'extensions.gnome.org.crt']);
if (GLib.file_test(localCert, GLib.FileTest.EXISTS))
return localCert;
else
return Config.SHELL_SYSTEM_CA_FILE;
}
_httpSession.ssl_ca_file = _getCertFile();
// Arrays of uuids
var enabledExtensions;
// Contains the order that extensions were enabled in.
@ -39,8 +69,89 @@ const disconnect = Lang.bind(_signals, _signals.disconnect);
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
var initted = false;
var enabled;
function installExtensionFromUUID(uuid, version_tag) {
let params = { uuid: uuid,
version_tag: version_tag,
shell_version: Config.PACKAGE_VERSION,
api_version: API_VERSION.toString() };
let message = Soup.form_request_new_from_hash('GET', REPOSITORY_URL_INFO, params);
_httpSession.queue_message(message,
function(session, message) {
let info = JSON.parse(message.response_body.data);
let dialog = new InstallExtensionDialog(uuid, version_tag, info.name);
dialog.open(global.get_current_time());
});
}
function uninstallExtensionFromUUID(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return false;
// Try to disable it -- if it's ERROR'd, we can't guarantee that,
// but it will be removed on next reboot, and hopefully nothing
// broke too much.
disableExtension(uuid);
// Don't try to uninstall system extensions
if (extension.type != ExtensionUtils.ExtensionType.PER_USER)
return false;
extension.state = ExtensionState.UNINSTALLED;
_signals.emit('extension-state-changed', extension);
delete ExtensionUtils.extensions[uuid];
FileUtils.recursivelyDeleteDir(Gio.file_new_for_path(extension.path));
return true;
}
function gotExtensionZipFile(session, message, uuid) {
if (message.status_code != Soup.KnownStatusCode.OK) {
logExtensionError(uuid, 'downloading extension: ' + message.status_code);
return;
}
// FIXME: use a GFile mkstemp-type method once one exists
let fd, tmpzip;
try {
[fd, tmpzip] = GLib.file_open_tmp('XXXXXX.shell-extension.zip');
} catch (e) {
logExtensionError(uuid, 'tempfile: ' + e.toString());
return;
}
let stream = new Gio.UnixOutputStream({ fd: fd });
let dir = ExtensionUtils.userExtensionsDir.get_child(uuid);
Shell.write_soup_message_to_stream(stream, message);
stream.close(null);
let [success, pid] = GLib.spawn_async(null,
['unzip', '-uod', dir.get_path(), '--', tmpzip],
null,
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
null);
if (!success) {
logExtensionError(uuid, 'extract: could not extract');
return;
}
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
GLib.spawn_close_pid(pid);
// Add extension to 'enabled-extensions' for the user, always...
let enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
if (enabledExtensions.indexOf(uuid) == -1) {
enabledExtensions.push(uuid);
global.settings.set_strv(ENABLED_EXTENSIONS_KEY, enabledExtensions);
}
loadExtension(dir, ExtensionUtils.ExtensionType.PER_USER, true);
});
}
function disableExtension(uuid) {
let extension = ExtensionUtils.extensions[uuid];
@ -67,23 +178,23 @@ function disableExtension(uuid) {
try {
ExtensionUtils.extensions[uuid].stateObj.disable();
} catch(e) {
logExtensionError(uuid, e);
logExtensionError(uuid, e.toString());
}
}
if (extension.stylesheet) {
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
theme.unload_stylesheet(extension.stylesheet.get_path());
try {
extension.stateObj.disable();
} catch(e) {
logExtensionError(uuid, e.toString());
return;
}
extension.stateObj.disable();
for (let i = 0; i < order.length; i++) {
let uuid = order[i];
try {
ExtensionUtils.extensions[uuid].stateObj.enable();
} catch(e) {
logExtensionError(uuid, e);
logExtensionError(uuid, e.toString());
}
}
@ -106,86 +217,68 @@ function enableExtension(uuid) {
extensionOrder.push(uuid);
let stylesheetFile = extension.dir.get_child('stylesheet.css');
if (stylesheetFile.query_exists(null)) {
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
theme.load_stylesheet(stylesheetFile.get_path());
extension.stylesheet = stylesheetFile;
try {
extension.stateObj.enable();
} catch(e) {
logExtensionError(uuid, e.toString());
return;
}
extension.stateObj.enable();
extension.state = ExtensionState.ENABLED;
_signals.emit('extension-state-changed', extension);
}
function logExtensionError(uuid, error) {
function logExtensionError(uuid, message, state) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return;
let message = '' + error;
if (error.state)
extension.state = error.state;
else
extension.state = ExtensionState.ERROR;
if (!extension.errors)
extension.errors = [];
log('Extension "%s" had error: %s'.format(uuid, message));
extension.errors.push(message);
global.logError('Extension "%s" had error: %s'.format(uuid, message));
state = state || ExtensionState.ERROR;
_signals.emit('extension-state-changed', { uuid: uuid,
error: message,
state: extension.state });
state: state });
}
function loadExtension(extension) {
function loadExtension(dir, type, enabled) {
let uuid = dir.get_basename();
let extension;
if (ExtensionUtils.extensions[uuid] != undefined) {
global.logError('Extension "%s" is already loaded'.format(uuid));
return;
}
try {
extension = ExtensionUtils.createExtensionObject(uuid, dir, type);
} catch(e) {
logExtensionError(uuid, e.message);
return;
}
// Default to error, we set success as the last step
extension.state = ExtensionState.ERROR;
if (ExtensionUtils.isOutOfDate(extension)) {
let error = new Error('extension is not compatible with current GNOME Shell and/or GJS version');
error.state = ExtensionState.OUT_OF_DATE;
throw error;
logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version', ExtensionState.OUT_OF_DATE);
extension.state = ExtensionState.OUT_OF_DATE;
return;
}
let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
if (enabled) {
initExtension(extension.uuid);
initExtension(uuid);
if (extension.state == ExtensionState.DISABLED)
enableExtension(extension.uuid);
enableExtension(uuid);
} else {
extension.state = ExtensionState.INITIALIZED;
}
_signals.emit('extension-state-changed', extension);
}
function unloadExtension(extension) {
// Try to disable it -- if it's ERROR'd, we can't guarantee that,
// but it will be removed on next reboot, and hopefully nothing
// broke too much.
disableExtension(extension.uuid);
extension.state = ExtensionState.UNINSTALLED;
_signals.emit('extension-state-changed', extension);
delete ExtensionUtils.extensions[extension.uuid];
return true;
}
function reloadExtension(oldExtension) {
// Grab the things we'll need to pass to createExtensionObject
// to reload it.
let { uuid: uuid, dir: dir, type: type } = oldExtension;
// Then unload the old extension.
unloadExtension(oldExtension);
// Now, recreate the extension and load it.
let newExtension = ExtensionUtils.createExtensionObject(uuid, dir, type);
loadExtension(newExtension);
global.log('Loaded extension ' + uuid);
}
function initExtension(uuid) {
@ -196,43 +289,76 @@ function initExtension(uuid) {
throw new Error("Extension was not properly created. Call loadExtension first");
let extensionJs = dir.get_child('extension.js');
if (!extensionJs.query_exists(null))
throw new Error('Missing extension.js');
if (!extensionJs.query_exists(null)) {
logExtensionError(uuid, 'Missing extension.js');
return;
}
let stylesheetPath = null;
let themeContext = St.ThemeContext.get_for_stage(global.stage);
let theme = themeContext.get_theme();
let stylesheetFile = dir.get_child('stylesheet.css');
if (stylesheetFile.query_exists(null)) {
try {
theme.load_stylesheet(stylesheetFile.get_path());
} catch (e) {
logExtensionError(uuid, 'Stylesheet parse error: ' + e);
return;
}
}
let extensionModule;
let extensionState = null;
try {
ExtensionUtils.installImporter(extension);
extensionModule = extension.imports.extension;
} catch (e) {
if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath);
logExtensionError(uuid, '' + e);
return;
}
ExtensionUtils.installImporter(extension);
extensionModule = extension.imports.extension;
if (!extensionModule.init) {
logExtensionError(uuid, 'missing \'init\' function');
return;
}
if (extensionModule.init) {
try {
extensionState = extensionModule.init(extension);
} catch (e) {
if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath);
logExtensionError(uuid, 'Failed to evaluate init function:' + e);
return;
}
if (!extensionState)
extensionState = extensionModule;
extension.stateObj = extensionState;
if (!extensionState.enable) {
logExtensionError(uuid, 'missing \'enable\' function');
return;
}
if (!extensionState.disable) {
logExtensionError(uuid, 'missing \'disable\' function');
return;
}
extension.state = ExtensionState.DISABLED;
_signals.emit('extension-loaded', uuid);
}
function onEnabledExtensionsChanged() {
let newEnabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
if (!enabled)
return;
// Find and enable all the newly enabled extensions: UUIDs found in the
// new setting, but not in the old one.
newEnabledExtensions.filter(function(uuid) {
return enabledExtensions.indexOf(uuid) == -1;
}).forEach(function(uuid) {
try {
enableExtension(uuid);
} catch(e) {
logExtensionError(uuid, e);
}
enableExtension(uuid);
});
// Find and disable all the newly disabled extensions: UUIDs found in the
@ -240,67 +366,87 @@ function onEnabledExtensionsChanged() {
enabledExtensions.filter(function(item) {
return newEnabledExtensions.indexOf(item) == -1;
}).forEach(function(uuid) {
try {
disableExtension(uuid);
} catch(e) {
logExtensionError(uuid, e);
}
disableExtension(uuid);
});
enabledExtensions = newEnabledExtensions;
}
function _loadExtensions() {
function init() {
ExtensionUtils.init();
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
}
let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', function(signals, extension) {
try {
loadExtension(extension);
} catch(e) {
logExtensionError(extension.uuid, e);
}
function loadExtensions() {
ExtensionUtils.scanExtensions(function(uuid, dir, type) {
let enabled = enabledExtensions.indexOf(uuid) != -1;
loadExtension(dir, type, enabled);
});
finder.scanExtensions();
}
function enableAllExtensions() {
if (enabled)
return;
const InstallExtensionDialog = new Lang.Class({
Name: 'InstallExtensionDialog',
Extends: ModalDialog.ModalDialog,
if (!initted) {
_loadExtensions();
initted = true;
} else {
enabledExtensions.forEach(function(uuid) {
enableExtension(uuid);
});
_init: function(uuid, version_tag, name) {
this.parent({ styleClass: 'extension-dialog' });
this._uuid = uuid;
this._version_tag = version_tag;
this._name = name;
this.setButtons([{ label: _("Cancel"),
action: Lang.bind(this, this._onCancelButtonPressed),
key: Clutter.Escape
},
{ label: _("Install"),
action: Lang.bind(this, this._onInstallButtonPressed)
}]);
let message = _("Download and install '%s' from extensions.gnome.org?").format(name);
this._descriptionLabel = new St.Label({ text: message });
this.contentLayout.add(this._descriptionLabel,
{ y_fill: true,
y_align: St.Align.START });
},
_onCancelButtonPressed: function(button, event) {
this.close(global.get_current_time());
// Even though the extension is already "uninstalled", send through
// a state-changed signal for any users who want to know if the install
// went through correctly -- using proper async DBus would block more
// traditional clients like the plugin
let meta = { uuid: this._uuid,
state: ExtensionState.UNINSTALLED,
error: '' };
_signals.emit('extension-state-changed', meta);
},
_onInstallButtonPressed: function(button, event) {
let state = { uuid: this._uuid,
state: ExtensionState.DOWNLOADING,
error: '' };
_signals.emit('extension-state-changed', state);
let params = { version_tag: this._version_tag,
shell_version: Config.PACKAGE_VERSION,
api_version: API_VERSION.toString() };
let url = REPOSITORY_URL_DOWNLOAD.format(this._uuid);
let message = Soup.form_request_new_from_hash('GET', url, params);
_httpSession.queue_message(message,
Lang.bind(this, function(session, message) {
gotExtensionZipFile(session, message, this._uuid);
}));
this.close(global.get_current_time());
}
enabled = true;
}
function disableAllExtensions() {
if (!enabled)
return;
if (initted) {
enabledExtensions.forEach(function(uuid) {
disableExtension(uuid);
});
}
enabled = false;
}
function _sessionUpdated() {
if (Main.sessionMode.allowExtensions)
enableAllExtensions();
else
disableAllExtensions();
}
function init() {
Main.sessionMode.connect('updated', _sessionUpdated);
_sessionUpdated();
}
});

View File

@ -1,358 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Main = imports.ui.main;
const Params = imports.misc.params;
function _navigateActor(actor) {
if (!actor)
return;
let needsGrab = true;
if (actor instanceof St.Widget)
needsGrab = !actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
if (needsGrab)
actor.grab_key_focus();
}
// GrabHelper:
// @owner: the actor that owns the GrabHelper
//
// Creates a new GrabHelper object, for dealing with keyboard and pointer grabs
// associated with a set of actors.
//
// Note that the grab can be automatically dropped at any time by the user, and
// your code just needs to deal with it; you shouldn't adjust behavior directly
// after you call ungrab(), but instead pass an 'onUngrab' callback when you
// call grab().
const GrabHelper = new Lang.Class({
Name: 'GrabHelper',
_init: function(owner) {
this._owner = owner;
this._grabStack = [];
this._actors = [];
this._capturedEventId = 0;
this._eventId = 0;
this._keyFocusNotifyId = 0;
this._focusWindowChangedId = 0;
this._ignoreRelease = false;
this._modalCount = 0;
this._grabFocusCount = 0;
},
// addActor:
// @actor: an actor
//
// Adds @actor to the set of actors that are allowed to process events
// during a grab.
addActor: function(actor) {
actor.__grabHelperDestroyId = actor.connect('destroy', Lang.bind(this, function() { this.removeActor(actor); }));
this._actors.push(actor);
},
// removeActor:
// @actor: an actor
//
// Removes @actor from the set of actors that are allowed to
// process events during a grab.
removeActor: function(actor) {
let index = this._actors.indexOf(actor);
if (index != -1)
this._actors.splice(index, 1);
if (actor.__grabHelperDestroyId) {
actor.disconnect(actor.__grabHelperDestroyId);
delete actor.__grabHelperDestroyId;
}
},
_isWithinGrabbedActor: function(actor) {
while (actor) {
if (this._actors.indexOf(actor) != -1)
return true;
actor = actor.get_parent();
}
return false;
},
get currentGrab() {
return this._grabStack[this._grabStack.length - 1] || {};
},
_findStackIndex: function(actor) {
if (!actor)
return -1;
for (let i = 0; i < this._grabStack.length; i++) {
if (this._grabStack[i].actor === actor)
return i;
}
return -1;
},
isActorGrabbed: function(actor) {
return this._findStackIndex(actor) >= 0;
},
// grab:
// @params: A bunch of parameters, see below
//
// Grabs the mouse and keyboard, according to the GrabHelper's
// parameters. If @newFocus is not %null, then the keyboard focus
// is moved to the first #StWidget:can-focus widget inside it.
//
// The grab will automatically be dropped if:
// - The user clicks outside the grabbed actors
// - The user types Escape
// - The keyboard focus is moved outside the grabbed actors
// - A window is focused
//
// If @params.actor is not null, then it will be focused as the
// new actor. If you attempt to grab an already focused actor, the
// request to be focused will be ignored. The actor will not be
// added to the grab stack, so do not call a paired ungrab().
//
// If @params contains { modal: true }, then grab() will push a modal
// on the owner of the GrabHelper. As long as there is at least one
// { modal: true } actor on the grab stack, the grab will be kept.
// When the last { modal: true } actor is ungrabbed, then the modal
// will be dropped. A modal grab can fail if there is already a grab
// in effect from aother application; in this case the function returns
// false and nothing happens. Non-modal grabs can never fail.
//
// If @params contains { grabFocus: true }, then if you call grab()
// while the shell is outside the overview, it will set the stage
// input mode to %Shell.StageInputMode.FOCUSED, and ungrab() will
// revert it back, and re-focus the previously-focused window (if
// another window hasn't been explicitly focused before then).
grab: function(params) {
params = Params.parse(params, { actor: null,
modal: false,
grabFocus: false,
onUngrab: null });
let focus = global.stage.key_focus;
let hadFocus = focus && this._isWithinGrabbedActor(focus);
let newFocus = params.actor;
if (this.isActorGrabbed(params.actor))
return true;
params.savedFocus = focus;
if (params.modal && !this._takeModalGrab())
return false;
if (params.grabFocus && !this._takeFocusGrab(hadFocus))
return false;
if (hadFocus || params.grabFocus)
_navigateActor(newFocus);
this._grabStack.push(params);
return true;
},
_takeModalGrab: function() {
let firstGrab = (this._modalCount == 0);
if (firstGrab) {
if (!Main.pushModal(this._owner))
return false;
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
this._eventId = global.stage.connect('event', Lang.bind(this, this._onEvent));
}
this._modalCount++;
return true;
},
_releaseModalGrab: function() {
this._modalCount--;
if (this._modalCount > 0)
return;
if (this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
if (this._eventId > 0) {
global.stage.disconnect(this._eventId);
this._eventId = 0;
}
Main.popModal(this._owner);
global.sync_pointer();
},
_takeFocusGrab: function(hadFocus) {
let firstGrab = (this._grabFocusCount == 0);
if (firstGrab) {
let metaDisplay = global.screen.get_display();
this._grabbedFromKeynav = hadFocus;
this._preGrabInputMode = global.stage_input_mode;
this._prevFocusedWindow = metaDisplay.focus_window;
if (this._preGrabInputMode == Shell.StageInputMode.NONREACTIVE ||
this._preGrabInputMode == Shell.StageInputMode.NORMAL) {
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
}
this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
this._focusWindowChangedId = metaDisplay.connect('notify::focus-window', Lang.bind(this, this._focusWindowChanged));
}
this._grabFocusCount++;
return true;
},
_releaseFocusGrab: function() {
this._grabFocusCount--;
if (this._grabFocusCount > 0)
return;
if (this._keyFocusNotifyId > 0) {
global.stage.disconnect(this._keyFocusNotifyId);
this._keyFocusNotifyId = 0;
}
if (!this._focusWindowChanged > 0) {
let metaDisplay = global.screen.get_display();
metaDisplay.disconnect(this._focusWindowChangedId);
this._focusWindowChangedId = 0;
}
let prePopInputMode = global.stage_input_mode;
if (this._grabbedFromKeynav) {
if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED &&
prePopInputMode != Shell.StageInputMode.FULLSCREEN)
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
}
if (this._prevFocusedWindow) {
let metaDisplay = global.screen.get_display();
if (!metaDisplay.focus_window) {
metaDisplay.set_input_focus_window(this._prevFocusedWindow,
false, global.get_current_time());
}
}
},
// ignoreRelease:
//
// Make sure that the next button release event evaluated by the
// capture event handler returns false. This is designed for things
// like the ComboBoxMenu that go away on press, but need to eat
// the next release event.
ignoreRelease: function() {
this._ignoreRelease = true;
},
// ungrab:
// @params: The parameters for the grab; see below.
//
// Pops an actor from the grab stack, potentially dropping the grab.
//
// If the actor that was popped from the grab stack was not the actor
// That was passed in, this call is ignored.
ungrab: function(params) {
params = Params.parse(params, { actor: this.currentGrab.actor });
let grabStackIndex = this._findStackIndex(params.actor);
if (grabStackIndex < 0)
return;
let focus = global.stage.key_focus;
let hadFocus = focus && this._isWithinGrabbedActor(focus);
let poppedGrabs = this._grabStack.slice(grabStackIndex);
// "Pop" all newly ungrabbed actors off the grab stack
// by truncating the array.
this._grabStack.length = grabStackIndex;
for (let i = poppedGrabs.length - 1; i >= 0; i--) {
let poppedGrab = poppedGrabs[i];
if (poppedGrab.onUngrab)
poppedGrab.onUngrab();
if (poppedGrab.modal)
this._releaseModalGrab();
if (poppedGrab.grabFocus)
this._releaseFocusGrab();
}
if (hadFocus) {
let poppedGrab = poppedGrabs[0];
_navigateActor(poppedGrab.savedFocus);
}
},
_onCapturedEvent: function(actor, event) {
let type = event.type();
let press = type == Clutter.EventType.BUTTON_PRESS;
let release = type == Clutter.EventType.BUTTON_RELEASE;
let button = press || release;
if (release && this._ignoreRelease) {
this._ignoreRelease = false;
return false;
}
if (!button && this._modalCount == 0)
return false;
if (this._isWithinGrabbedActor(event.get_source()))
return false;
if (Main.keyboard.shouldTakeEvent(event))
return false;
if (button) {
// If we have a press event, ignore the next event,
// which should be a release event.
if (press)
this._ignoreRelease = true;
this.ungrab({ actor: this._grabStack[0].actor });
}
return this._modalCount > 0;
},
// We catch 'event' rather than 'key-press-event' so that we get
// a chance to run before the overview's own Escape check
_onEvent: function(actor, event) {
if (event.type() == Clutter.EventType.KEY_PRESS &&
event.get_key_symbol() == Clutter.KEY_Escape) {
this.ungrab();
return true;
}
return false;
},
_onKeyFocusChanged: function() {
let focus = global.stage.key_focus;
if (!focus || !this._isWithinGrabbedActor(focus))
this.ungrab();
},
_focusWindowChanged: function() {
let metaDisplay = global.screen.get_display();
if (metaDisplay.focus_window != null)
this.ungrab();
}
});

View File

@ -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());
}
});

View File

@ -282,10 +282,6 @@ const IconGrid = new Lang.Class({
return this._computeLayout(rowWidth)[0];
},
getRowLimit: function() {
return this._rowLimit;
},
_computeLayout: function (forWidth) {
let nColumns = 0;
let usedWidth = 0;
@ -309,22 +305,21 @@ const IconGrid = new Lang.Class({
this._grid.queue_relayout();
},
removeAll: function() {
this._grid.destroy_all_children();
removeAll: function () {
this._grid.get_children().forEach(Lang.bind(this, function (child) {
child.destroy();
}));
},
addItem: function(actor, index) {
if (index !== undefined)
this._grid.insert_child_at_index(actor, index);
else
this._grid.add_actor(actor);
addItem: function(actor) {
this._grid.add_actor(actor);
},
getItemAtIndex: function(index) {
return this._grid.get_child_at_index(index);
return this._grid.get_children()[index];
},
visibleItemsCount: function() {
return this._grid.get_n_children() - this._grid.get_n_skip_paint();
return this._grid.get_children().length - this._grid.get_n_skip_paint();
}
});

View File

@ -93,7 +93,7 @@ const Key = new Lang.Class({
this._getExtendedKeys();
this.actor._extended_keys = this._extended_keyboard;
this._boxPointer.actor.hide();
Main.layoutManager.addChrome(this._boxPointer.actor);
Main.layoutManager.addChrome(this._boxPointer.actor, { visibleInFullscreen: true });
}
},
@ -175,7 +175,7 @@ const Key = new Lang.Class({
this.actor.fake_release();
this._boxPointer.actor.raise_top();
this._boxPointer.setPosition(this.actor, 0.5);
this._boxPointer.show(BoxPointer.PopupAnimation.FULL);
this._boxPointer.show(true);
this.actor.set_hover(false);
if (!this._grabbed) {
Main.pushModal(this.actor);
@ -186,7 +186,7 @@ const Key = new Lang.Class({
} else {
if (this._grabbed)
this._ungrab();
this._boxPointer.hide(BoxPointer.PopupAnimation.FULL);
this._boxPointer.hide(true);
}
}
});
@ -200,10 +200,8 @@ const Keyboard = new Lang.Class({
this._impl.export(Gio.DBus.session, '/org/gnome/Caribou/Keyboard');
this.actor = null;
this._focusInTray = false;
this._focusInExtendedKeys = false;
this._timestamp = global.display.get_current_time_roundtrip();
this._timestamp = global.get_current_time();
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
@ -290,15 +288,7 @@ const Keyboard = new Lang.Class({
// Showing an extended key popup and clicking a key from the extended keys
// will grab focus, but ignore that
let extendedKeysWereFocused = this._focusInExtendedKeys;
this._focusInExtendedKeys = focus && (focus._extended_keys || focus.extended_key);
if (this._focusInExtendedKeys || extendedKeysWereFocused)
return;
// Ignore focus changes caused by message tray showing/hiding
let trayWasFocused = this._focusInTray;
this._focusInTray = (focus && Main.messageTray.actor.contains(focus));
if (this._focusInTray || trayWasFocused)
if (focus && (focus._extended_keys || (focus._key && focus._key.extended_key)))
return;
let time = global.get_current_time();
@ -349,13 +339,6 @@ const Keyboard = new Lang.Class({
trayButton.reactive = true;
trayButton.remove_style_pseudo_class('grayed');
}));
Main.sessionMode.connect('updated', Lang.bind(this, function() {
trayButton.reactive = !Main.sessionMode.isLocked;
if (Main.sessionMode.isLocked)
trayButton.add_style_pseudo_class('grayed');
else
trayButton.remove_style_pseudo_class('grayed');
}));
return trayButton;
},
@ -485,12 +468,6 @@ const Keyboard = new Lang.Class({
}
},
shouldTakeEvent: function(event) {
let actor = event.get_source();
return Main.layoutManager.keyboardBox.contains(actor) ||
actor._extended_keys || actor.extended_key;
},
show: function () {
this._redraw();
@ -517,30 +494,15 @@ const Keyboard = new Lang.Class({
this._moveTemporarily();
},
// _compareTimestamp:
//
// Compare two timestamps taking into account
// CURRENT_TIME (0)
_compareTimestamp: function(one, two) {
if (one == two)
return 0;
if (one == Clutter.CURRENT_TIME)
return 1;
if (two == Clutter.CURRENT_TIME)
return -1;
return one - two;
},
// D-Bus methods
Show: function(timestamp) {
if (!this._enableKeyboard)
return;
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
if (timestamp - this._timestamp < 0)
return;
if (timestamp != Clutter.CURRENT_TIME)
this._timestamp = timestamp;
this._timestamp = timestamp;
this.show();
},
@ -548,11 +510,10 @@ const Keyboard = new Lang.Class({
if (!this._enableKeyboard)
return;
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
if (timestamp - this._timestamp < 0)
return;
if (timestamp != Clutter.CURRENT_TIME)
this._timestamp = timestamp;
this._timestamp = timestamp;
this.hide();
},
@ -580,9 +541,16 @@ const KeyboardSource = new Lang.Class({
Extends: MessageTray.Source,
_init: function(keyboard) {
this.parent(_("Keyboard"));
this._keyboard = keyboard;
this.parent(_("Keyboard"), 'input-keyboard-symbolic');
this.keepTrayOnSummaryClick = true;
this._setSummaryIcon(this.createNotificationIcon());
},
createNotificationIcon: function() {
return new St.Icon({ icon_name: 'input-keyboard',
icon_type: St.IconType.SYMBOLIC,
icon_size: this.ICON_SIZE });
},
handleSummaryClick: function() {

View File

@ -65,8 +65,7 @@ const KeyringDialog = new Lang.Class({
key: Clutter.Escape
},
{ label: '',
action: Lang.bind(this, this._onContinueButton),
default: true
action: Lang.bind(this, this._onContinueButton)
}]
this.setButtons(buttons);
@ -84,10 +83,7 @@ const KeyringDialog = new Lang.Class({
if (this.prompt.password_visible) {
let label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
label.set_text(_("Password:"));
table.add(label, { row: row, col: 0,
x_expand: false, x_fill: true,
x_align: St.Align.START,
y_fill: false, y_align: St.Align.MIDDLE });
table.add(label, { row: row, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START });
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
text: '',
can_focus: true});
@ -103,10 +99,7 @@ const KeyringDialog = new Lang.Class({
if (this.prompt.confirm_visible) {
var label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
label.set_text(_("Type again:"));
table.add(label, { row: row, col: 0,
x_expand: false, x_fill: true,
x_align: St.Align.START,
y_fill: false, y_align: St.Align.MIDDLE });
table.add(label, { row: row, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START });
this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
text: '',
can_focus: true});
@ -192,36 +185,23 @@ const KeyringDialog = new Lang.Class({
},
_onContinueButton: function() {
this.prompt.complete();
this.prompt.complete()
},
_onCancelButton: function() {
this.prompt.cancel();
this.prompt.cancel()
},
});
const KeyringPrompter = new Lang.Class({
Name: 'KeyringPrompter',
function init() {
prompter = new Gcr.SystemPrompter();
prompter.connect('new-prompt', function(prompter) {
let dialog = new KeyringDialog();
return dialog.prompt;
});
_init: function() {
this._prompter = new Gcr.SystemPrompter();
this._prompter.connect('new-prompt', function(prompter) {
let dialog = new KeyringDialog();
return dialog.prompt;
});
this._dbusId = null;
},
enable: function() {
this._prompter.register(Gio.DBus.session);
this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
Gio.BusNameOwnerFlags.REPLACE, null, null);
},
disable: function() {
this._prompter.unregister(false);
Gio.DBus.session.unown_name(this._dbusId);
}
});
const Component = KeyringPrompter;
let connection = Gio.DBus.session;
prompter.register(connection);
Gio.bus_own_name_on_connection (connection, 'org.gnome.keyring.SystemPrompter',
Gio.BusNameOwnerFlags.REPLACE, null, null);
}

View File

@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GObject = imports.gi.GObject;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -12,88 +11,12 @@ const St = imports.gi.St;
const DND = imports.ui.dnd;
const Main = imports.ui.main;
const Params = imports.misc.params;
const ScreenSaver = imports.misc.screenSaver;
const Tweener = imports.ui.tweener;
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
const STARTUP_ANIMATION_TIME = 0.2;
const KEYBOARD_ANIMATION_TIME = 0.5;
const PLYMOUTH_TRANSITION_TIME = 1;
const MonitorConstraint = new Lang.Class({
Name: 'MonitorConstraint',
Extends: Clutter.Constraint,
Properties: {'primary': GObject.ParamSpec.boolean('primary',
'Primary', 'Track primary monitor',
GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
false),
'index': GObject.ParamSpec.int('index',
'Monitor index', 'Track specific monitor',
GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
-1, 64, -1)},
_init: function(props) {
this._primary = false;
this._index = -1;
this.parent(props);
},
get primary() {
return this._primary;
},
set primary(v) {
if (v)
this._index = -1;
this._primary = v;
if (this.actor)
this.actor.queue_relayout();
this.notify('primary');
},
get index() {
return this._index;
},
set index(v) {
this._primary = false;
this._index = v;
if (this.actor)
this.actor.queue_relayout();
this.notify('index');
},
vfunc_set_actor: function(actor) {
if (actor) {
if (!this._monitorsChangedId) {
this._monitorsChangedId = Main.layoutManager.connect('monitors-changed', Lang.bind(this, function() {
this.actor.queue_relayout();
}));
}
} else {
if (this._monitorsChangedId)
Main.layoutManager.disconnect(this._monitorsChangedId);
this._monitorsChangedId = 0;
}
this.parent(actor);
},
vfunc_update_allocation: function(actor, actorBox) {
if (!this._primary && this._index < 0)
return;
let monitor;
if (this._primary) {
monitor = Main.layoutManager.primaryMonitor;
} else {
let index = Math.min(this._index, Main.layoutManager.monitors.length - 1);
monitor = Main.layoutManager.monitors[index];
}
actorBox.init_rect(monitor.x, monitor.y, monitor.width, monitor.height);
}
});
const LayoutManager = new Lang.Class({
Name: 'LayoutManager',
@ -104,46 +27,32 @@ const LayoutManager = new Lang.Class({
this.primaryMonitor = null;
this.primaryIndex = -1;
this._hotCorners = [];
this._rootPixmap = null;
this._leftPanelBarrier = 0;
this._rightPanelBarrier = 0;
this._trayBarrier = 0;
this._chrome = new Chrome(this);
this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup',
visible: false,
clip_to_allocation: true,
layout_manager: new Clutter.BinLayout(),
});
this.addChrome(this.screenShieldGroup);
this.panelBox = new St.BoxLayout({ name: 'panelBox',
vertical: true });
this.addChrome(this.panelBox, { affectsStruts: true,
trackFullscreen: true });
this.addChrome(this.panelBox, { affectsStruts: true });
this.panelBox.connect('allocation-changed',
Lang.bind(this, this._updatePanelBarriers));
this.trayBox = new St.Widget({ name: 'trayBox',
layout_manager: new Clutter.BinLayout() });
this.addChrome(this.trayBox);
this.trayBox = new St.BoxLayout({ name: 'trayBox' });
this.addChrome(this.trayBox, { visibleInFullscreen: true });
this.trayBox.connect('allocation-changed',
Lang.bind(this, this._updateTrayBarrier));
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
reactive: true,
track_hover: true });
this.addChrome(this.keyboardBox);
this.addChrome(this.keyboardBox, { visibleInFullscreen: true });
this._keyboardHeightNotifyId = 0;
global.screen.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged));
this._monitorsChanged();
this._chrome.connect('primary-fullscreen-changed', Lang.bind(this, function(chrome, state) {
this.emit('primary-fullscreen-changed', state);
}));
},
// This is called by Main after everything else is constructed;
@ -236,9 +145,6 @@ const LayoutManager = new Lang.Class({
},
_updateBoxes: function() {
this.screenShieldGroup.set_position(0, 0);
this.screenShieldGroup.set_size(global.screen_width, global.screen_height);
this.panelBox.set_position(this.primaryMonitor.x, this.primaryMonitor.y);
this.panelBox.set_size(this.primaryMonitor.width, -1);
@ -318,47 +224,33 @@ const LayoutManager = new Lang.Class({
return false;
},
get currentMonitor() {
let index = global.screen.get_current_monitor();
return this.monitors[index];
},
get focusIndex() {
let focusWindow = global.display.focus_window;
_startupAnimation: function() {
this.panelBox.anchor_y = this.panelBox.height;
if (focusWindow) {
let wrect = focusWindow.get_outer_rect();
for (let i = 0; i < this.monitors.length; i++) {
let monitor = this.monitors[i];
let plymouthTransitionRunning = false;
// If we're the greeter, put up the xrootpmap actor
// and fade it out to have a nice transition from plymouth
// to the greeter. Otherwise, we'll just animate the panel,
// as usual.
if (Main.sessionMode.isGreeter) {
this._rootPixmap = global.create_xrootpmap_texture();
if (this._rootPixmap != null) {
Main.uiGroup.add_actor(this._rootPixmap);
Tweener.addTween(this._rootPixmap,
{ opacity: 0,
time: PLYMOUTH_TRANSITION_TIME,
transition: 'linear',
onComplete: this._fadeRootpmapComplete,
onCompleteScope: this });
plymouthTransitionRunning = true;
if (monitor.x <= wrect.x && monitor.y <= wrect.y &&
monitor.x + monitor.width > wrect.x &&
monitor.y + monitor.height > wrect.y)
return i;
}
}
if (!plymouthTransitionRunning)
this._fadeRootpmapComplete();
return this.primaryIndex;
},
_fadeRootpmapComplete: function() {
get focusMonitor() {
return this.monitors[this.focusIndex];
},
_startupAnimation: function() {
// Don't animate the strut
this._chrome.freezeUpdateRegions();
if (this._rootPixmap != null) {
this._rootPixmap.destroy();
this._rootPixmap = null;
}
this.panelBox.anchor_y = this.panelBox.height;
Tweener.addTween(this.panelBox,
{ anchor_y: 0,
time: STARTUP_ANIMATION_TIME,
@ -373,6 +265,7 @@ const LayoutManager = new Lang.Class({
},
showKeyboard: function () {
Main.messageTray.hide();
this.keyboardBox.raise_top();
Tweener.addTween(this.keyboardBox,
{ anchor_y: this.keyboardBox.height,
@ -386,8 +279,6 @@ const LayoutManager = new Lang.Class({
time: KEYBOARD_ANIMATION_TIME,
transition: 'easeOutQuad'
});
this.emit('keyboard-visible-changed', true);
},
_showKeyboardComplete: function() {
@ -402,6 +293,7 @@ const LayoutManager = new Lang.Class({
},
hideKeyboard: function (immediate) {
Main.messageTray.hide();
if (this._keyboardHeightNotifyId) {
this.keyboardBox.disconnect(this._keyboardHeightNotifyId);
this._keyboardHeightNotifyId = 0;
@ -418,8 +310,6 @@ const LayoutManager = new Lang.Class({
time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
transition: 'easeOutQuad'
});
this.emit('keyboard-visible-changed', false);
},
_hideKeyboardComplete: function() {
@ -441,10 +331,8 @@ const LayoutManager = new Lang.Class({
// the window manager struts. Changes to @actor's visibility will
// NOT affect whether or not the strut is present, however.
//
// If %trackFullscreen in @params is %true, the actor's visibility
// will be bound to the presence of fullscreen windows on the same
// monitor (it will be hidden whenever a fullscreen window is visible,
// and shown otherwise)
// If %visibleInFullscreen in @params is %true, the actor will be
// visible even when a fullscreen window should be covering it.
addChrome: function(actor, params) {
this._chrome.addActor(actor, params);
},
@ -458,8 +346,10 @@ const LayoutManager = new Lang.Class({
// struts or input region to cover specific children.
//
// @params can have any of the same values as in addChrome(),
// though some possibilities don't make sense. By default, @actor has
// the same params as its chrome ancestor.
// though some possibilities don't make sense (eg, trying to have
// a %visibleInFullscreen child of a non-%visibleInFullscreen
// parent). By default, @actor has the same params as its chrome
// ancestor.
trackChrome: function(actor, params) {
this._chrome.trackActor(actor, params);
},
@ -665,7 +555,7 @@ const HotCorner = new Lang.Class({
// workspace content.
const defaultParams = {
trackFullscreen: false,
visibleInFullscreen: false,
affectsStruts: false,
affectsInputRegion: true
};
@ -692,13 +582,24 @@ const Chrome = new Lang.Class({
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._queueUpdateRegions));
this._screenSaverActive = false;
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
this._screenSaverProxy.connectSignal('ActiveChanged', Lang.bind(this, function(proxy, senderName, [isActive]) {
this._onScreenSaverActiveChanged(isActive);
}));
this._screenSaverProxy.GetActiveRemote(Lang.bind(this, function(result, err) {
if (!err)
this._onScreenSaverActiveChanged(result[0]);
}));
this._relayout();
},
init: function() {
Main.overview.connect('showing', Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden', Lang.bind(this, this._overviewHidden));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
Main.overview.connect('showing',
Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden',
Lang.bind(this, this._overviewHidden));
},
addActor: function(actor, params) {
@ -784,30 +685,28 @@ const Chrome = new Lang.Class({
_actorReparented: function(actor, oldParent) {
let newParent = actor.get_parent();
if (!newParent) {
if (!newParent)
this._untrackActor(actor);
} else {
let i = this._findActor(actor);
let actorData = this._trackedActors[i];
else
actorData.isToplevel = (newParent == Main.uiGroup);
}
},
_updateVisibility: function() {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i], visible;
if (!actorData.trackFullscreen)
continue;
if (!actorData.isToplevel)
continue;
if (this._inOverview || !Main.sessionMode.hasWindows)
if (this._screenSaverActive)
visible = false;
else if (this._inOverview)
visible = true;
else if (this.findMonitorForActor(actorData.actor).inFullscreen)
else if (!actorData.visibleInFullscreen &&
this.findMonitorForActor(actorData.actor).inFullscreen)
visible = false;
else
visible = true;
actorData.actor.visible = visible;
Main.uiGroup.set_skip_paint(actorData.actor, !visible);
}
},
@ -823,11 +722,6 @@ const Chrome = new Lang.Class({
this._queueUpdateRegions();
},
_sessionUpdated: function() {
this._updateVisibility();
this._queueUpdateRegions();
},
_relayout: function() {
this._monitors = this._layoutManager.monitors;
this._primaryMonitor = this._layoutManager.primaryMonitor;
@ -837,6 +731,12 @@ const Chrome = new Lang.Class({
this._queueUpdateRegions();
},
_onScreenSaverActiveChanged: function(screenSaverActive) {
this._screenSaverActive = screenSaverActive;
this._updateVisibility();
this._queueUpdateRegions();
},
_findMonitorForRect: function(x, y, w, h) {
// First look at what monitor the center of the rectangle is at
let cx = x + w/2;
@ -952,8 +852,6 @@ const Chrome = new Lang.Class({
for (let i = 0; i < this._monitors.length; i++)
wasInFullscreen[i] = this._monitors[i].inFullscreen;
let primaryWasInFullscreen = this._primaryMonitor.inFullscreen;
this._updateFullscreen();
let changed = false;
@ -963,15 +861,10 @@ const Chrome = new Lang.Class({
break;
}
}
if (changed) {
this._updateVisibility();
this._queueUpdateRegions();
}
if (primaryWasInFullscreen != this._primaryMonitor.inFullscreen) {
this.emit('primary-fullscreen-changed', this._primaryMonitor.inFullscreen);
}
},
updateRegions: function() {
@ -1086,5 +979,3 @@ const Chrome = new Lang.Class({
return false;
}
});
Signals.addSignalMethods(Chrome.prototype);

View File

@ -8,8 +8,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 +15,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
@ -41,16 +38,12 @@ const Lightbox = new Lang.Class({
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,7 +52,6 @@ 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));
@ -101,30 +93,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();

21
js/ui/link.js Normal file
View File

@ -0,0 +1,21 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Signals = imports.signals;
const St = imports.gi.St;
const Link = new Lang.Class({
Name: 'Link',
_init : function(props) {
let realProps = { reactive: true,
track_hover: true,
style_class: 'shell-link' };
// The user can pass in reactive: false to override the above and get
// a non-reactive link (a link to the current page, perhaps)
Lang.copyProperties(props, realProps);
this.actor = new St.Button(realProps);
}
});
Signals.addSignalMethods(Link.prototype);

View File

@ -12,18 +12,16 @@ const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const System = imports.system;
const History = imports.misc.history;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionUtils = imports.misc.extensionUtils;
const Link = imports.ui.link;
const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener;
const Main = imports.ui.main;
const JsParse = imports.misc.jsParse;
const CHEVRON = '>>> ';
/* Imports...feel free to add here as needed */
var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
'const GLib = imports.gi.GLib; ' +
@ -38,8 +36,9 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
/* Utility functions...we should probably be able to use these
* in the shell core code too. */
'const stage = global.stage; ' +
'const color = function(pixel) { let c= new Clutter.Color(); c.from_pixel(pixel); return c; }; ' +
/* Special lookingGlass functions */
'const it = Main.lookingGlass.getIt(); ' +
'const it = Main.lookingGlass.getIt(); ' +
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
const HISTORY_KEY = 'looking-glass-history';
@ -262,8 +261,9 @@ function objectToString(o) {
const ObjLink = new Lang.Class({
Name: 'ObjLink',
Extends: Link.Link,
_init: function(lookingGlass, o, title) {
_init: function(o, title) {
let text;
if (title)
text = title;
@ -272,30 +272,24 @@ const ObjLink = new Lang.Class({
text = GLib.markup_escape_text(text, -1);
this._obj = o;
this.actor = new St.Button({ reactive: true,
track_hover: true,
style_class: 'shell-link',
label: text });
this.parent({ label: text });
this.actor.get_child().single_line_mode = true;
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this._lookingGlass = lookingGlass;
},
_onClicked: function (link) {
this._lookingGlass.inspectObject(this._obj, this.actor);
Main.lookingGlass.inspectObject(this._obj, this.actor);
}
});
const Result = new Lang.Class({
Name: 'Result',
_init: function(lookingGlass, command, o, index) {
_init : function(command, o, index) {
this.index = index;
this.o = o;
this.actor = new St.BoxLayout({ vertical: true });
this._lookingGlass = lookingGlass;
let cmdTxt = new St.Label({ text: command });
cmdTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
@ -305,7 +299,7 @@ const Result = new Lang.Class({
let resultTxt = new St.Label({ text: 'r(' + index + ') = ' });
resultTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
box.add(resultTxt);
let objLink = new ObjLink(this._lookingGlass, o);
let objLink = new ObjLink(o);
box.add(objLink.actor);
let line = new Clutter.Rectangle({ name: 'Separator' });
let padBin = new St.Bin({ name: 'Separator', x_fill: true, y_fill: true });
@ -317,18 +311,16 @@ const Result = new Lang.Class({
const WindowList = new Lang.Class({
Name: 'WindowList',
_init: function(lookingGlass) {
_init : function () {
this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
let tracker = Shell.WindowTracker.get_default();
this._updateId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._updateWindowList));
global.display.connect('window-created', Lang.bind(this, this._updateWindowList));
tracker.connect('tracked-windows-changed', Lang.bind(this, this._updateWindowList));
this._lookingGlass = lookingGlass;
},
_updateWindowList: function() {
this.actor.destroy_all_children();
this.actor.get_children().forEach(function (actor) { actor.destroy(); });
let windows = global.get_window_actors();
let tracker = Shell.WindowTracker.get_default();
for (let i = 0; i < windows.length; i++) {
@ -340,7 +332,7 @@ const WindowList = new Lang.Class({
}
let box = new St.BoxLayout({ vertical: true });
this.actor.add(box);
let windowLink = new ObjLink(this._lookingGlass, metaWindow, metaWindow.title);
let windowLink = new ObjLink(metaWindow, metaWindow.title);
box.add(windowLink.actor, { x_align: St.Align.START, x_fill: false });
let propsBox = new St.BoxLayout({ vertical: true, style: 'padding-left: 6px;' });
box.add(propsBox);
@ -351,7 +343,7 @@ const WindowList = new Lang.Class({
let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
propsBox.add(propBox);
propBox.add(new St.Label({ text: 'app: ' }), { y_fill: false });
let appLink = new ObjLink(this._lookingGlass, app, app.get_id());
let appLink = new ObjLink(app, app.get_id());
propBox.add(appLink.actor, { y_fill: false });
propBox.add(icon, { y_fill: false });
} else {
@ -365,21 +357,18 @@ Signals.addSignalMethods(WindowList.prototype);
const ObjInspector = new Lang.Class({
Name: 'ObjInspector',
_init: function(lookingGlass) {
_init : function () {
this._obj = null;
this._previousObj = null;
this._parentList = [];
this.actor = new St.ScrollView({ pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
x_fill: true, y_fill: true });
this.actor = new St.ScrollView({ x_fill: true, y_fill: true });
this.actor.get_hscroll_bar().hide();
this._container = new St.BoxLayout({ name: 'LookingGlassPropertyInspector',
style_class: 'lg-dialog',
vertical: true });
this.actor.add_actor(this._container);
this._lookingGlass = lookingGlass;
},
selectObject: function(obj, skipPrevious) {
@ -389,7 +378,7 @@ const ObjInspector = new Lang.Class({
this._previousObj = null;
this._obj = obj;
this._container.destroy_all_children();
this._container.get_children().forEach(function (child) { child.destroy(); });
let hbox = new St.BoxLayout({ style_class: 'lg-obj-inspector-title' });
this._container.add_actor(hbox);
@ -423,7 +412,7 @@ const ObjInspector = new Lang.Class({
let link;
try {
let prop = obj[propName];
link = new ObjLink(this._lookingGlass, prop).actor;
link = new ObjLink(prop).actor;
} catch (e) {
link = new St.Label({ text: '<error>' });
}
@ -444,6 +433,10 @@ const ObjInspector = new Lang.Class({
this.actor.show();
if (sourceActor) {
this.actor.set_scale(0, 0);
let [sourceX, sourceY] = sourceActor.get_transformed_position();
let [sourceWidth, sourceHeight] = sourceActor.get_transformed_size();
this.actor.move_anchor_point(Math.floor(sourceX + sourceWidth / 2),
Math.floor(sourceY + sourceHeight / 2));
Tweener.addTween(this.actor, { scale_x: 1, scale_y: 1,
transition: 'easeOutQuad',
time: 0.2 });
@ -464,7 +457,7 @@ const ObjInspector = new Lang.Class({
_onInsert: function() {
let obj = this._obj;
this.close();
this._lookingGlass.insertObject(obj);
Main.lookingGlass.insertObject(obj);
},
_onBack: function() {
@ -472,36 +465,34 @@ const ObjInspector = new Lang.Class({
}
});
const RedBorderEffect = new Lang.Class({
Name: 'RedBorderEffect',
Extends: Clutter.Effect,
function addBorderPaintHook(actor) {
let signalId = actor.connect_after('paint',
function () {
let color = new Cogl.Color();
color.init_from_4ub(0xff, 0, 0, 0xc4);
Cogl.set_source_color(color);
vfunc_paint: function() {
let actor = this.get_actor();
actor.continue_paint();
let geom = actor.get_allocation_geometry();
let width = 2;
let color = new Cogl.Color();
color.init_from_4ub(0xff, 0, 0, 0xc4);
Cogl.set_source_color(color);
// clockwise order
Cogl.rectangle(0, 0, geom.width, width);
Cogl.rectangle(geom.width - width, width,
geom.width, geom.height);
Cogl.rectangle(0, geom.height,
geom.width - width, geom.height - width);
Cogl.rectangle(0, geom.height - width,
width, width);
});
let geom = actor.get_allocation_geometry();
let width = 2;
// clockwise order
Cogl.rectangle(0, 0, geom.width, width);
Cogl.rectangle(geom.width - width, width,
geom.width, geom.height);
Cogl.rectangle(0, geom.height,
geom.width - width, geom.height - width);
Cogl.rectangle(0, geom.height - width,
width, width);
},
});
actor.queue_redraw();
return signalId;
}
const Inspector = new Lang.Class({
Name: 'Inspector',
_init: function(lookingGlass) {
_init: function() {
let container = new Shell.GenericContainer({ width: 0,
height: 0 });
container.connect('allocate', Lang.bind(this, this._allocate));
@ -515,6 +506,9 @@ const Inspector = new Lang.Class({
this._displayText = new St.Label();
eventHandler.add(this._displayText, { expand: true });
this._borderPaintTarget = null;
this._borderPaintId = null;
eventHandler.connect('destroy', Lang.bind(this, this._onDestroy));
eventHandler.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
eventHandler.connect('button-press-event', Lang.bind(this, this._onButtonPressEvent));
eventHandler.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
@ -529,8 +523,6 @@ const Inspector = new Lang.Class({
// out, or move the pointer outside of _pointerTarget.
this._target = null;
this._pointerTarget = null;
this._lookingGlass = lookingGlass;
},
_allocate: function(actor, box, flags) {
@ -551,13 +543,18 @@ const Inspector = new Lang.Class({
},
_close: function() {
Clutter.ungrab_pointer();
Clutter.ungrab_keyboard();
Clutter.ungrab_pointer(this._eventHandler);
Clutter.ungrab_keyboard(this._eventHandler);
this._eventHandler.destroy();
this._eventHandler = null;
this.emit('closed');
},
_onDestroy: function() {
if (this._borderPaintTarget != null)
this._borderPaintTarget.disconnect(this._borderPaintId);
},
_onKeyPressEvent: function (actor, event) {
if (event.get_key_symbol() == Clutter.Escape)
this._close();
@ -626,7 +623,12 @@ const Inspector = new Lang.Class({
this._displayText.text = '';
this._displayText.text = position + ' ' + this._target;
this._lookingGlass.setBorderPaintTarget(this._target);
if (this._borderPaintTarget != this._target) {
if (this._borderPaintTarget != null)
this._borderPaintTarget.disconnect(this._borderPaintId);
this._borderPaintTarget = this._target;
this._borderPaintId = addBorderPaintHook(this._target);
}
}
});
@ -660,7 +662,7 @@ const Memory = new Lang.Class({
this._gcbutton = new St.Button({ label: 'Full GC',
style_class: 'lg-obj-inspector-button' });
this._gcbutton.connect('clicked', Lang.bind(this, function () { System.gc(); this._renderText(); }));
this._gcbutton.connect('clicked', Lang.bind(this, function () { global.gc(); this._renderText(); }));
this.actor.add(this._gcbutton, { x_align: St.Align.START,
x_fill: false });
@ -721,13 +723,13 @@ const Extensions = new Lang.Class({
let extension = actor._extension;
let uri = extension.dir.get_uri();
Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context());
this._lookingGlass.close();
Main.lookingGlass.close();
},
_onWebPage: function (actor) {
let extension = actor._extension;
Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context());
this._lookingGlass.close();
Main.lookingGlass.close();
},
_onViewErrors: function (actor) {
@ -791,33 +793,24 @@ const Extensions = new Lang.Class({
text: this._stateToString(extension.state) });
metaBox.add(state);
let viewsource = new St.Button({ reactive: true,
track_hover: true,
style_class: 'shell-link',
label: _("View Source") });
viewsource._extension = extension;
viewsource.connect('clicked', Lang.bind(this, this._onViewSource));
metaBox.add(viewsource);
let viewsource = new Link.Link({ label: _("View Source") });
viewsource.actor._extension = extension;
viewsource.actor.connect('clicked', Lang.bind(this, this._onViewSource));
metaBox.add(viewsource.actor);
if (extension.metadata.url) {
let webpage = new St.Button({ reactive: true,
track_hover: true,
style_class: 'shell-link',
label: _("Web Page") });
webpage._extension = extension;
webpage.connect('clicked', Lang.bind(this, this._onWebPage));
metaBox.add(webpage);
let webpage = new Link.Link({ label: _("Web Page") });
webpage.actor._extension = extension;
webpage.actor.connect('clicked', Lang.bind(this, this._onWebPage));
metaBox.add(webpage.actor);
}
let viewerrors = new St.Button({ reactive: true,
track_hover: true,
style_class: 'shell-link',
label: _("Show Errors") });
viewerrors._extension = extension;
viewerrors._parentBox = box;
viewerrors._isShowing = false;
viewerrors.connect('clicked', Lang.bind(this, this._onViewErrors));
metaBox.add(viewerrors);
let viewerrors = new Link.Link({ label: _("Show Errors") });
viewerrors.actor._extension = extension;
viewerrors.actor._parentBox = box;
viewerrors.actor._isShowing = false;
viewerrors.actor.connect('clicked', Lang.bind(this, this._onViewErrors));
metaBox.add(viewerrors.actor);
return box;
}
@ -828,7 +821,8 @@ const LookingGlass = new Lang.Class({
_init : function() {
this._borderPaintTarget = null;
this._redBorderEffect = new RedBorderEffect();
this._borderPaintId = 0;
this._borderDestroyId = 0;
this._open = false;
@ -858,18 +852,19 @@ const LookingGlass = new Lang.Class({
Main.layoutManager.keyboardBox.connect('allocation-changed',
Lang.bind(this, this._queueResize));
this._objInspector = new ObjInspector(this);
this._objInspector = new ObjInspector();
Main.uiGroup.add_actor(this._objInspector.actor);
this._objInspector.actor.hide();
let toolbar = new St.BoxLayout({ name: 'Toolbar' });
this.actor.add_actor(toolbar);
let inspectIcon = new St.Icon({ icon_name: 'gtk-color-picker',
icon_type: St.IconType.FULLCOLOR,
icon_size: 24 });
toolbar.add_actor(inspectIcon);
inspectIcon.reactive = true;
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
let inspector = new Inspector(this);
let inspector = new Inspector();
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
target);
@ -899,14 +894,18 @@ const LookingGlass = new Lang.Class({
this._entryArea = new St.BoxLayout({ name: 'EntryArea' });
this._evalBox.add_actor(this._entryArea);
let label = new St.Label({ text: CHEVRON });
let label = new St.Label({ text: 'js>>> ' });
this._entryArea.add(label);
this._entry = new St.Entry({ can_focus: true });
ShellEntry.addContextMenu(this._entry);
this._entryArea.add(this._entry, { expand: true });
this._windowList = new WindowList(this);
this._windowList = new WindowList();
this._windowList.connect('selected', Lang.bind(this, function(list, window) {
notebook.selectIndex(0);
this._pushResult('<window selection>', window);
}));
notebook.appendPage('Windows', this._windowList.actor);
this._memory = new Memory();
@ -960,22 +959,23 @@ const LookingGlass = new Lang.Class({
+ 'font-family: "' + fontDesc.get_family() + '";';
},
setBorderPaintTarget: function(obj) {
if (this._borderPaintTarget != null)
this._borderPaintTarget.remove_effect(this._redBorderEffect);
this._borderPaintTarget = obj;
if (this._borderPaintTarget != null)
this._borderPaintTarget.add_effect(this._redBorderEffect);
},
_pushResult: function(command, obj) {
let index = this._results.length + this._offset;
let result = new Result(this, CHEVRON + command, obj, index);
let result = new Result('>>> ' + command, obj, index);
this._results.push(result);
this._resultsArea.add(result.actor);
if (obj instanceof Clutter.Actor)
this.setBorderPaintTarget(obj);
if (this._borderPaintTarget != null) {
this._borderPaintTarget.disconnect(this._borderPaintId);
this._borderPaintTarget = null;
}
if (obj instanceof Clutter.Actor) {
this._borderPaintTarget = obj;
this._borderPaintId = addBorderPaintHook(obj);
this._borderDestroyId = obj.connect('destroy', Lang.bind(this, function () {
this._borderDestroyId = 0;
this._borderPaintTarget = null;
}));
}
let children = this._resultsArea.get_children();
if (children.length > this._maxItems) {
this._results.shift();
@ -1089,8 +1089,8 @@ const LookingGlass = new Lang.Class({
this.actor.width = myWidth;
this.actor.height = myHeight;
this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
this._objInspector.actor.set_position(primary.x + this.actor.x + Math.floor(myWidth * 0.1),
primary.y + this._targetY + Math.floor(myHeight * 0.1));
this._objInspector.actor.set_position(this.actor.x + Math.floor(myWidth * 0.1),
this._targetY + Math.floor(myHeight * 0.1));
},
insertObject: function(obj) {
@ -1156,7 +1156,11 @@ const LookingGlass = new Lang.Class({
this._open = false;
Tweener.removeTweens(this.actor);
this.setBorderPaintTarget(null);
if (this._borderPaintTarget != null) {
this._borderPaintTarget.disconnect(this._borderPaintId);
this._borderPaintTarget.disconnect(this._borderDestroyId);
this._borderPaintTarget = null;
}
Main.popModal(this._entry);

View File

@ -12,11 +12,10 @@ const Signals = imports.signals;
const Main = imports.ui.main;
const MagnifierDBus = imports.ui.magnifierDBus;
const Params = imports.misc.params;
const PointerWatcher = imports.ui.pointerWatcher;
const MOUSE_POLL_FREQUENCY = 50;
const CROSSHAIRS_CLIP_SIZE = [100, 100];
const NO_CHANGE = 0.0;
// Settings
const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
@ -25,14 +24,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';
@ -56,7 +47,7 @@ const Magnifier = new Lang.Class({
let xfixesCursor = Shell.XFixesCursor.get_for_stage(global.stage);
this._mouseSprite = new Clutter.Texture();
xfixesCursor.update_texture_image(this._mouseSprite);
this._cursorRoot = new Clutter.Actor();
this._cursorRoot = new Clutter.Group();
this._cursorRoot.add_actor(this._mouseSprite);
// Create the first ZoomRegion and initialize it according to the
@ -136,8 +127,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 +139,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 +294,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 +443,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 +465,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));
@ -583,47 +540,7 @@ 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);
@ -637,10 +554,6 @@ const ZoomRegion = new Lang.Class({
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._magView = null;
this._background = null;
@ -966,107 +879,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 +889,21 @@ const ZoomRegion = new Lang.Class({
// hide the magnified region from CLUTTER_PICK_ALL
Shell.util_set_hidden_from_pick (this._magView, true);
// Add a group to clip the contents of the magnified view.
let mainGroup = new Clutter.Actor({ clip_to_allocation: true });
// Append a Clutter.Group to clip the contents of the magnified view.
let mainGroup = new Clutter.Group({ clip_to_allocation: true });
this._magView.set_child(mainGroup);
// Add a background for when the magnified uiGroup is scrolled
// out of view (don't want to see desktop showing through).
this._background = new Clutter.Actor({ background_color: Main.DEFAULT_BACKGROUND_COLOR,
width: global.screen_width,
height: global.screen_height });
this._background = new Clutter.Rectangle({ color: Main.DEFAULT_BACKGROUND_COLOR });
mainGroup.add_actor(this._background);
// Clone the group that contains all of UI on the screen. This is the
// chrome, the windows, etc.
this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup });
mainGroup.add_actor(this._uiGroupClone);
Main.uiGroup.set_size(global.screen_width, global.screen_height);
this._background.set_size(global.screen_width, global.screen_height);
// Add either the given mouseSourceActor to the ZoomRegion, or a clone of
// it.
@ -1105,13 +917,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,8 +925,6 @@ 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;
@ -1325,7 +1128,7 @@ const ZoomRegion = new Lang.Class({
this._mouseActor.set_scale(this._xMagFactor, this._yMagFactor);
let [x, y] = this._screenToViewPort(0, 0);
this._uiGroupClone.set_position(Math.round(x), Math.round(y));
this._uiGroupClone.set_position(x, y);
this._updateMousePosition();
},
@ -1353,6 +1156,7 @@ const ZoomRegion = new Lang.Class({
if (!this.isActive())
return;
Main.uiGroup.set_size(global.screen_width, global.screen_height);
this._background.set_size(global.screen_width, global.screen_height);
if (this._screenPosition == GDesktopEnums.MagnifierScreenPosition.NONE)
@ -1376,15 +1180,15 @@ const Crosshairs = new Lang.Class({
let groupWidth = global.screen_width * 3;
let groupHeight = global.screen_height * 3;
this._actor = new Clutter.Actor({
this._actor = new Clutter.Group({
clip_to_allocation: false,
width: groupWidth,
height: groupHeight
});
this._horizLeftHair = new Clutter.Actor();
this._horizRightHair = new Clutter.Actor();
this._vertTopHair = new Clutter.Actor();
this._vertBottomHair = new Clutter.Actor();
this._horizLeftHair = new Clutter.Rectangle();
this._horizRightHair = new Clutter.Rectangle();
this._vertTopHair = new Clutter.Rectangle();
this._vertBottomHair = new Clutter.Rectangle();
this._actor.add_actor(this._horizLeftHair);
this._actor.add_actor(this._horizRightHair);
this._actor.add_actor(this._vertTopHair);
@ -1455,10 +1259,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 +1271,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;
},
/**
@ -1627,144 +1433,3 @@ const Crosshairs = new Lang.Class({
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;
}
});

View File

@ -10,68 +10,145 @@ const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Components = imports.ui.components;
const AutomountManager = imports.ui.automountManager;
const AutorunManager = imports.ui.autorunManager;
const CtrlAltTab = imports.ui.ctrlAltTab;
const EndSessionDialog = imports.ui.endSessionDialog;
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
const KeyringPrompt = imports.ui.keyringPrompt;
const Environment = imports.ui.environment;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
const Keyboard = imports.ui.keyboard;
const MessageTray = imports.ui.messageTray;
const Overview = imports.ui.overview;
const Panel = imports.ui.panel;
const PlaceDisplay = imports.ui.placeDisplay;
const RunDialog = imports.ui.runDialog;
const Layout = imports.ui.layout;
const LookingGlass = imports.ui.lookingGlass;
const NetworkAgent = imports.ui.networkAgent;
const NotificationDaemon = imports.ui.notificationDaemon;
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
const ScreenShield = imports.ui.screenShield;
const Scripting = imports.ui.scripting;
const SessionMode = imports.ui.sessionMode;
const ShellDBus = imports.ui.shellDBus;
const ShellMountOperation = imports.ui.shellMountOperation;
const UnlockDialog = imports.ui.unlockDialog;
const TelepathyClient = imports.ui.telepathyClient;
const WindowManager = imports.ui.windowManager;
const Magnifier = imports.ui.magnifier;
const XdndHandler = imports.ui.xdndHandler;
const StatusIconDispatcher = imports.ui.statusIconDispatcher;
const Util = imports.misc.util;
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
let componentManager = null;
let automountManager = null;
let autorunManager = null;
let panel = null;
let hotCorners = [];
let placesManager = null;
let overview = null;
let runDialog = null;
let lookingGlass = null;
let wm = null;
let messageTray = null;
let screenShield = null;
let notificationDaemon = null;
let windowAttentionHandler = null;
let telepathyClient = null;
let ctrlAltTabManager = null;
let sessionMode = null;
let recorder = null;
let shellDBusService = null;
let shellMountOpDBusService = null;
let screenSaverDBus = null;
let modalCount = 0;
let modalActorFocusStack = [];
let uiGroup = null;
let magnifier = null;
let xdndHandler = null;
let statusIconDispatcher = null;
let keyboard = null;
let layoutManager = null;
let networkAgent = null;
let _startDate;
let _defaultCssStylesheet = null;
let _cssStylesheet = null;
let _gdmCssStylesheet = null;
let _overridesSettings = null;
let background = null;
function _sessionUpdated() {
Meta.keybindings_set_custom_handler('panel-run-dialog', sessionMode.hasRunDialog ? openRunDialog : null);
if (sessionMode.isGreeter)
screenShield.showDialog();
function _createUserSession() {
// Load the calendar server. Note that we are careful about
// not loading any events until the user presses the clock
global.launch_calendar_server();
placesManager = new PlaceDisplay.PlacesManager();
telepathyClient = new TelepathyClient.Client();
automountManager = new AutomountManager.AutomountManager();
autorunManager = new AutorunManager.AutorunManager();
networkAgent = new NetworkAgent.NetworkAgent();
}
function _createGDMSession() {
// We do this this here instead of at the top to prevent GDM
// related code from getting loaded in normal user sessions
const LoginDialog = imports.gdm.loginDialog;
let loginDialog = new LoginDialog.LoginDialog();
loginDialog.connect('loaded', function() {
loginDialog.open();
});
}
function _initRecorder() {
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
let bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
global.display.add_keybinding('toggle-recording',
bindingSettings,
Meta.KeyBindingFlags.NONE, function() {
if (recorder == null) {
recorder = new Shell.Recorder({ stage: global.stage });
}
if (recorder.is_recording()) {
recorder.close();
Meta.enable_unredirect_for_screen(global.screen);
} else {
// read the parameters from GSettings always in case they have changed
recorder.set_framerate(recorderSettings.get_int('framerate'));
/* Translators: this is a filename used for screencast recording */
// xgettext:no-c-format
recorder.set_filename(_("Screencast from %d %t") + '.' + recorderSettings.get_string('file-extension'));
let pipeline = recorderSettings.get_string('pipeline');
if (!pipeline.match(/^\s*$/))
recorder.set_pipeline(pipeline);
else
recorder.set_pipeline(null);
Meta.disable_unredirect_for_screen(global.screen);
recorder.record();
}
});
}
function _initUserSession() {
_initRecorder();
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
ExtensionSystem.init();
ExtensionSystem.loadExtensions();
Meta.keybindings_set_custom_handler('panel-run-dialog', function() {
getRunDialog().open();
});
Meta.keybindings_set_custom_handler('panel-main-menu', function () {
overview.toggle();
});
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
}
function start() {
@ -84,9 +161,7 @@ function start() {
Gio.DesktopAppInfo.set_desktop_env('GNOME');
sessionMode = new SessionMode.SessionMode();
shellDBusService = new ShellDBus.GnomeShell();
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
// also initialize ShellAppSystem first. ShellAppSystem
@ -108,6 +183,7 @@ function start() {
global.stage.no_clear_hint = true;
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
_gdmCssStylesheet = global.datadir + '/theme/gdm.css';
loadTheme();
// Set up stage hierarchy to group all UI actors under one container.
@ -135,36 +211,42 @@ function start() {
layoutManager = new Layout.LayoutManager();
xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
overview = new Overview.Overview();
// This overview object is just a stub for non-user sessions
overview = new Overview.Overview({ isDummy: global.session_type != Shell.SessionType.USER });
magnifier = new Magnifier.Magnifier();
if (UnlockDialog.isSupported())
screenShield = new ScreenShield.ScreenShield();
else
screenShield = new ScreenShield.ScreenShieldFallback();
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
panel = new Panel.Panel();
wm = new WindowManager.WindowManager();
messageTray = new MessageTray.MessageTray();
keyboard = new Keyboard.Keyboard();
notificationDaemon = new NotificationDaemon.NotificationDaemon();
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
componentManager = new Components.ComponentManager();
if (global.session_type == Shell.SessionType.USER)
_createUserSession();
else if (global.session_type == Shell.SessionType.GDM)
_createGDMSession();
panel.startStatusArea();
layoutManager.init();
keyboard.init();
overview.init();
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
false, -1, 1);
Meta.keybindings_set_custom_handler('panel-main-menu', Lang.bind(overview, overview.toggle));
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
sessionMode.connect('update', _sessionUpdated);
_sessionUpdated();
if (global.session_type == Shell.SessionType.USER)
_initUserSession();
statusIconDispatcher.start(messageTray.actor);
// Provide the bus object for gnome-session to
// initiate logouts.
EndSessionDialog.init();
// Attempt to become a PolicyKit authentication agent
PolkitAuthenticationAgent.init()
// Become a prompter for gnome keyring
KeyringPrompt.init();
_startDate = new Date();
global.stage.connect('captured-event', _globalKeyPressHandler);
@ -188,9 +270,6 @@ function start() {
global.screen.connect('restacked', _windowsRestacked);
_nWorkspacesChanged();
ExtensionDownloader.init();
ExtensionSystem.init();
}
let _workspaces = [];
@ -414,6 +493,9 @@ function loadTheme() {
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
if (global.session_type == Shell.SessionType.GDM)
theme.load_stylesheet(_gdmCssStylesheet);
if (previousTheme) {
let customStylesheets = previousTheme.get_custom_stylesheets();
@ -476,11 +558,6 @@ function _globalKeyPressHandler(actor, event) {
if (event.type() != Clutter.EventType.KEY_PRESS)
return false;
if (!sessionMode.allowKeybindingsWhenModal) {
if (modalCount > (overview.visible ? 1 : 0))
return false;
}
let symbol = event.get_key_symbol();
let keyCode = event.get_key_code();
let ignoredModifiers = global.display.get_ignored_modifier_mask();
@ -489,49 +566,48 @@ function _globalKeyPressHandler(actor, event) {
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
let action = global.display.get_keybinding_action(keyCode, modifierState);
// Other bindings are only available to the user session when the overview is up and
// no modal dialog is present.
if (global.session_type == Shell.SessionType.USER && (!overview.visible || modalCount > 1))
return false;
// This isn't a Meta.KeyBindingAction yet
if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
overview.hide();
return true;
}
if (action == Meta.KeyBindingAction.SWITCH_PANELS) {
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK,
modifierState);
return true;
}
// None of the other bindings are relevant outside of the user's session
if (global.session_type != Shell.SessionType.USER)
return false;
switch (action) {
// left/right would effectively act as synonyms for up/down if we enabled them;
// but that could be considered confusing; we also disable them in the main view.
//
// case Meta.KeyBindingAction.WORKSPACE_LEFT:
// if (!sessionMode.hasWorkspaces)
// return false;
//
// wm.actionMoveWorkspaceLeft();
// return true;
// case Meta.KeyBindingAction.WORKSPACE_RIGHT:
// if (!sessionMode.hasWorkspaces)
// return false;
//
// wm.actionMoveWorkspaceRight();
// return true;
case Meta.KeyBindingAction.WORKSPACE_UP:
if (!sessionMode.hasWorkspaces)
return false;
wm.actionMoveWorkspace(Meta.MotionDirection.UP);
wm.actionMoveWorkspaceUp();
return true;
case Meta.KeyBindingAction.WORKSPACE_DOWN:
if (!sessionMode.hasWorkspaces)
return false;
wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
wm.actionMoveWorkspaceDown();
return true;
case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
case Meta.KeyBindingAction.COMMAND_2:
if (!sessionMode.hasRunDialog)
return false;
openRunDialog();
getRunDialog().open();
return true;
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
case Meta.KeyBindingAction.OVERLAY_KEY:
overview.hide();
return true;
}
@ -547,10 +623,6 @@ function _findModal(actor) {
return -1;
}
function isInModalStack(actor) {
return _findModal(actor) != -1;
}
/**
* pushModal:
* @actor: #ClutterActor which will be given keyboard focus
@ -583,7 +655,6 @@ function pushModal(actor, timestamp, options) {
log('pushModal: invocation of begin_modal failed');
return false;
}
Meta.disable_unredirect_for_screen(global.screen);
}
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
@ -592,7 +663,7 @@ function pushModal(actor, timestamp, options) {
let actorDestroyId = actor.connect('destroy', function() {
let index = _findModal(actor);
if (index >= 0)
popModal(actor);
modalActorFocusStack.splice(index, 1);
});
let curFocus = global.stage.get_key_focus();
let curFocusDestroyId;
@ -664,7 +735,6 @@ function popModal(actor, timestamp) {
global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
Meta.enable_unredirect_for_screen(global.screen);
}
function createLookingGlass() {
@ -674,11 +744,11 @@ function createLookingGlass() {
return lookingGlass;
}
function openRunDialog() {
function getRunDialog() {
if (runDialog == null) {
runDialog = new RunDialog.RunDialog();
}
runDialog.open();
return runDialog;
}
/**
@ -807,8 +877,7 @@ function initializeDeferredWork(actor, callback, props) {
function queueDeferredWork(workId) {
let data = _deferredWorkData[workId];
if (!data) {
let message = 'Invalid work id %d'.format(workId);
logError(new Error(message), message);
global.logError('invalid work id ', workId);
return;
}
if (_deferredWorkQueue.indexOf(workId) < 0)

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,6 @@ const Atk = imports.gi.Atk;
const Params = imports.misc.params;
const Layout = imports.ui.layout;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
@ -36,20 +35,17 @@ const ModalDialog = new Lang.Class({
_init: function(params) {
params = Params.parse(params, { shellReactive: false,
styleClass: null,
parentActor: Main.uiGroup,
shouldFadeIn: true });
styleClass: null });
this.state = State.CLOSED;
this._hasModal = false;
this._shellReactive = params.shellReactive;
this._shouldFadeIn = params.shouldFadeIn;
this._group = new St.Widget({ visible: false,
x: 0,
y: 0,
accessible_role: Atk.Role.DIALOG });
params.parentActor.add_actor(this._group);
Main.uiGroup.add_actor(this._group);
let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.ALL });
@ -58,17 +54,15 @@ const ModalDialog = new Lang.Class({
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
this._actionKeys = {};
this._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent));
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
this._backgroundBin = new St.Bin();
this._monitorConstraint = new Layout.MonitorConstraint();
this._backgroundBin.add_constraint(this._monitorConstraint);
this._group.add_actor(this._backgroundBin);
this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
vertical: true });
this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
vertical: true });
if (params.styleClass != null) {
this.dialogLayout.add_style_class_name(params.styleClass);
this._dialogLayout.add_style_class_name(params.styleClass);
}
if (!this._shellReactive) {
@ -81,29 +75,29 @@ const ModalDialog = new Lang.Class({
this._eventBlocker = new Clutter.Group({ reactive: true });
stack.add_actor(this._eventBlocker);
stack.add_actor(this.dialogLayout);
stack.add_actor(this._dialogLayout);
} else {
this._backgroundBin.child = this.dialogLayout;
this._backgroundBin.child = this._dialogLayout;
}
this.contentLayout = new St.BoxLayout({ vertical: true });
this.dialogLayout.add(this.contentLayout,
{ x_fill: true,
y_fill: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.START });
this._dialogLayout.add(this.contentLayout,
{ x_fill: true,
y_fill: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.START });
this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
visible: false,
vertical: false });
this.dialogLayout.add(this._buttonLayout,
{ expand: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.END });
this._dialogLayout.add(this._buttonLayout,
{ expand: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.END });
global.focus_manager.add_group(this.dialogLayout);
this._initialKeyFocus = this.dialogLayout;
global.focus_manager.add_group(this._dialogLayout);
this._initialKeyFocus = this._dialogLayout;
this._initialKeyFocusDestroyId = 0;
this._savedKeyFocus = null;
},
@ -112,10 +106,6 @@ const ModalDialog = new Lang.Class({
this._group.destroy();
},
setActionKey: function(key, action) {
this._actionKeys[key] = action;
},
setButtons: function(buttons) {
let hadChildren = this._buttonLayout.get_children() > 0;
@ -129,20 +119,11 @@ const ModalDialog = new Lang.Class({
let label = buttonInfo['label'];
let action = buttonInfo['action'];
let key = buttonInfo['key'];
let isDefault = buttonInfo['default'];
if (isDefault && !key) {
this._actionKeys[Clutter.KEY_KP_Enter] = action;
this._actionKeys[Clutter.KEY_ISO_Enter] = action;
key = Clutter.KEY_Return;
}
buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button',
reactive: true,
can_focus: true,
label: label });
if (isDefault)
buttonInfo.button.add_style_pseudo_class('default');
let x_alignment;
if (buttons.length == 1)
@ -186,38 +167,34 @@ const ModalDialog = new Lang.Class({
},
_onKeyReleaseEvent: function(object, event) {
let symbol = event.get_key_symbol();
_onKeyPressEvent: function(object, keyPressEvent) {
let symbol = keyPressEvent.get_key_symbol();
let action = this._actionKeys[symbol];
if (action) {
if (action)
action();
return true;
}
return false;
},
_onGroupDestroy: function() {
this.emit('destroy');
},
_fadeOpen: function(onPrimary) {
if (onPrimary)
this._monitorConstraint.primary = true;
else
this._monitorConstraint.index = global.screen.get_current_monitor();
_fadeOpen: function() {
let monitor = Main.layoutManager.focusMonitor;
this._backgroundBin.set_position(monitor.x, monitor.y);
this._backgroundBin.set_size(monitor.width, monitor.height);
this.state = State.OPENING;
this.dialogLayout.opacity = 255;
this._dialogLayout.opacity = 255;
if (this._lightbox)
this._lightbox.show();
this._group.opacity = 0;
this._group.show();
Tweener.addTween(this._group,
{ opacity: 255,
time: this._shouldFadeIn ? OPEN_AND_CLOSE_TIME : 0,
time: OPEN_AND_CLOSE_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
@ -234,19 +211,19 @@ const ModalDialog = new Lang.Class({
this._initialKeyFocus = actor;
this._initialKeyFocusDestroyId = actor.connect('destroy', Lang.bind(this, function() {
this._initialKeyFocus = this.dialogLayout;
this._initialKeyFocus = this._dialogLayout;
this._initialKeyFocusDestroyId = 0;
}));
},
open: function(timestamp, onPrimary) {
open: function(timestamp) {
if (this.state == State.OPENED || this.state == State.OPENING)
return true;
if (!this.pushModal(timestamp))
return false;
this._fadeOpen(onPrimary);
this._fadeOpen();
return true;
},
@ -327,7 +304,7 @@ const ModalDialog = new Lang.Class({
return;
this.popModal(timestamp);
Tweener.addTween(this.dialogLayout,
Tweener.addTween(this._dialogLayout,
{ opacity: 0,
time: FADE_OUT_DIALOG_TIME,
transition: 'easeOutQuad',

View File

@ -1,4 +1,23 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/*
* Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
*/
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
@ -116,10 +135,7 @@ const NetworkSecretDialog = new Lang.Class({
} else
secret.valid = true;
secretTable.add(label, { row: pos, col: 0,
x_expand: false, x_fill: true,
x_align: St.Align.START,
y_fill: false, y_align: St.Align.MIDDLE });
secretTable.add(label, { row: pos, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START, y_align: St.Align.START });
secretTable.add(secret.entry, { row: pos, col: 1, x_expand: true, x_fill: true, y_align: St.Align.END });
pos++;
@ -131,7 +147,7 @@ const NetworkSecretDialog = new Lang.Class({
this._okButton = { label: _("Connect"),
action: Lang.bind(this, this._onOk),
default: true
key: Clutter.KEY_Return,
};
this.setButtons([{ label: _("Cancel"),
@ -150,6 +166,10 @@ const NetworkSecretDialog = new Lang.Class({
this._okButton.button.reactive = valid;
this._okButton.button.can_focus = valid;
if (valid)
this._okButton.button.remove_style_pseudo_class('disabled');
else
this._okButton.button.add_style_pseudo_class('disabled');
},
_onOk: function() {
@ -408,10 +428,7 @@ const VPNRequestHandler = new Lang.Class({
}
},
cancel: function(respond) {
if (respond)
this._agent.respond(this._requestId, Shell.NetworkAgentResponse.USER_CANCELED);
cancel: function() {
if (this._newStylePlugin && this._shellDialog) {
this._shellDialog.close(global.get_current_time());
this._shellDialog.destroy();
@ -587,7 +604,7 @@ const NetworkAgent = new Lang.Class({
Name: 'NetworkAgent',
_init: function() {
this._native = new Shell.NetworkAgent({ auto_register: false,
this._native = new Shell.NetworkAgent({ auto_register: true,
identifier: 'org.gnome.Shell.NetworkAgent' });
this._dialogs = { };
@ -597,24 +614,6 @@ const NetworkAgent = new Lang.Class({
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
},
enable: function() {
this._native.register();
},
disable: function() {
let requestId;
for (requestId in this._dialogs)
this._dialogs[requestId].cancel();
this._dialogs = { };
for (requestId in this._vpnRequests)
this._vpnRequests[requestId].cancel(true);
this._vpnRequests = { };
this._native.unregister();
},
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {
if (settingName == 'vpn') {
this._vpnRequest(requestId, connection, hints, flags);
@ -635,7 +634,7 @@ const NetworkAgent = new Lang.Class({
this._dialogs[requestId].destroy();
delete this._dialogs[requestId];
} else if (this._vpnRequests[requestId]) {
this._vpnRequests[requestId].cancel(false);
this._vpnRequests[requestId].cancel();
delete this._vpnRequests[requestId];
}
},
@ -684,10 +683,7 @@ const NetworkAgent = new Lang.Class({
try {
externalUIMode = keyfile.get_boolean('GNOME', 'supports-external-ui-mode');
} catch(e) { } // ignore errors if key does not exist
let path = binary;
if (!GLib.path_is_absolute(path)) {
path = GLib.build_filenamev([Config.LIBEXECDIR, path]);
}
let path = GLib.build_filenamev([Config.LIBEXECDIR, binary]);
if (GLib.file_test(path, GLib.FileTest.IS_EXECUTABLE))
this._vpnBinaries[service] = { fileName: path, externalUIMode: externalUIMode };
@ -704,4 +700,3 @@ const NetworkAgent = new Lang.Class({
}
}
});
const Component = NetworkAgent;

View File

@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GdkPixbuf = imports.gi.GdkPixbuf;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
@ -88,21 +87,6 @@ const rewriteRules = {
]
};
const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
'bluetooth-applet': 'bluetooth',
'gnome-volume-control-applet': 'volume', // renamed to gnome-sound-applet
// when moved to control center
'gnome-sound-applet': 'volume',
'nm-applet': 'network',
'gnome-power-manager': 'battery',
'keyboard': 'keyboard',
'a11y-keyboard': 'a11y',
'kbd-scrolllock': 'keyboard',
'kbd-numlock': 'keyboard',
'kbd-capslock': 'keyboard',
'ibus-ui-gtk': 'keyboard'
};
const NotificationDaemon = new Lang.Class({
Name: 'NotificationDaemon',
@ -115,19 +99,18 @@ const NotificationDaemon = new Lang.Class({
this._notifications = {};
this._busProxy = new Bus();
this._trayManager = new Shell.TrayManager();
this._trayIconAddedId = this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
this._trayIconRemovedId = this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
Main.statusIconDispatcher.connect('message-icon-added', Lang.bind(this, this._onTrayIconAdded));
Main.statusIconDispatcher.connect('message-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
Shell.WindowTracker.get_default().connect('notify::focus-app',
Lang.bind(this, this._onFocusAppChanged));
Main.overview.connect('hidden',
Lang.bind(this, this._onFocusAppChanged));
this._trayManager.manage_stage(global.stage, Main.messageTray.actor);
},
_iconForNotificationData: function(icon, hints) {
_iconForNotificationData: function(icon, hints, size) {
let textureCache = St.TextureCache.get_default();
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
// and don't show a large image. There are currently many applications that use
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
@ -138,18 +121,20 @@ const NotificationDaemon = new Lang.Class({
// a large image.
if (icon) {
if (icon.substr(0, 7) == 'file://')
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
return textureCache.load_uri_async(icon, size, size);
else if (icon[0] == '/') {
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
let uri = GLib.filename_to_uri(icon, null);
return textureCache.load_uri_async(uri, size, size);
} else
return new Gio.ThemedIcon({ name: icon });
return new St.Icon({ icon_name: icon,
icon_type: St.IconType.FULLCOLOR,
icon_size: size });
} else if (hints['image-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
bitsPerSample, width, height, rowStride);
return textureCache.load_from_raw(data, hasAlpha, width, height, rowStride, size);
} else if (hints['image-path']) {
return new Gio.FileIcon({ file: Gio.File.new_for_path(hints['image-path']) });
return textureCache.load_uri_async(GLib.filename_to_uri(hints['image-path'], null), size, size);
} else {
let stockIcon;
switch (hints.urgency) {
@ -161,7 +146,9 @@ const NotificationDaemon = new Lang.Class({
stockIcon = 'gtk-dialog-error';
break;
}
return new Gio.ThemedIcon({ name: stockIcon });
return new St.Icon({ icon_name: stockIcon,
icon_type: St.IconType.FULLCOLOR,
icon_size: size });
}
},
@ -234,19 +221,12 @@ const NotificationDaemon = new Lang.Class({
let [appName, replacesId, icon, summary, body, actions, hints, timeout] = params;
let id;
for (let hint in hints) {
// unpack the variants
hints[hint] = hints[hint].deep_unpack();
}
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
// Filter out chat, presence, calls and invitation notifications from
// Empathy, since we handle that information from telepathyClient.js
if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
hints['category'] == 'x-empathy.im.room-invitation' ||
hints['category'] == 'x-empathy.call.incoming' ||
hints['category'] == 'x-empathy.transfer.incoming' ||
hints['category'] == 'x-empathy.call.incoming"' ||
hints['category'] == 'x-empathy.im.subscription-request' ||
hints['category'] == 'presence.online' ||
hints['category'] == 'presence.offline')) {
@ -256,7 +236,6 @@ const NotificationDaemon = new Lang.Class({
Mainloop.idle_add(Lang.bind(this,
function () {
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
return false;
}));
return invocation.return_value(GLib.Variant.new('(u)', [id]));
}
@ -270,6 +249,13 @@ const NotificationDaemon = new Lang.Class({
}
}
for (let hint in hints) {
// unpack the variants
hints[hint] = hints[hint].deep_unpack();
}
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
// Be compatible with the various hints for image data and image path
// 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2
@ -355,9 +341,7 @@ const NotificationDaemon = new Lang.Class({
[ndata.id, ndata.icon, ndata.summary, ndata.body,
ndata.actions, ndata.hints, ndata.notification];
let gicon = this._iconForNotificationData(icon, hints);
let iconActor = new St.Icon({ gicon: gicon,
icon_size: MessageTray.NOTIFICATION_ICON_SIZE });
let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
if (notification == null) {
notification = new MessageTray.Notification(source, summary, body,
@ -437,8 +421,8 @@ const NotificationDaemon = new Lang.Class({
// of the 'transient' hint with hints['transient'] rather than hints.transient
notification.setTransient(hints['transient'] == true);
let sourceGIcon = source.useNotificationIcon ? this._iconForNotificationData(icon, hints) : null;
source.processNotification(notification, sourceGIcon);
let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
source.processNotification(notification, sourceIconActor);
},
CloseNotification: function(id) {
@ -499,11 +483,7 @@ const NotificationDaemon = new Lang.Class({
},
_onTrayIconAdded: function(o, icon) {
let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : '';
if (STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass] !== undefined)
return;
let source = this._getSource(icon.title || icon.wm_class || C_("program", "Unknown"), icon.pid, null, null, icon);
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null, null, icon);
},
_onTrayIconRemoved: function(o, icon) {
@ -554,11 +534,11 @@ const Source = new Lang.Class({
this.destroy();
},
processNotification: function(notification, gicon) {
if (gicon)
this._gicon = gicon;
if (!this.trayIcon)
this.iconUpdated();
processNotification: function(notification, icon) {
if (!this.app)
this._setApp();
if (!this.app && icon)
this._setSummaryIcon(icon);
let tracker = Shell.WindowTracker.get_default();
if (notification.resident && this.app && tracker.focus_app == this.app)
@ -583,12 +563,18 @@ const Source = new Lang.Class({
this.notifications.length > 0)
return false;
let id = global.connect('notify::stage-input-mode', Lang.bind(this, function () {
global.disconnect(id);
if (Main.overview.visible) {
// We can't just connect to Main.overview's 'hidden' signal,
// because it's emitted *before* it calls popModal()...
let id = global.connect('notify::stage-input-mode', Lang.bind(this,
function () {
global.disconnect(id);
this.trayIcon.click(event);
}));
Main.overview.hide();
} else {
this.trayIcon.click(event);
}));
Main.overview.hide();
}
return true;
},
@ -620,20 +606,10 @@ const Source = new Lang.Class({
// notification-based icons (ie, not a trayicon) or if it was unset before
if (!this.trayIcon) {
this.useNotificationIcon = false;
this.iconUpdated();
this._setSummaryIcon(this.app.create_icon_texture (this.ICON_SIZE));
}
},
setTitle: function(title) {
// Do nothing if .app is set, we don't want to override the
// app name with whatever is provided through libnotify (usually
// garbage)
if (this.app)
return;
this.parent(title);
},
open: function(notification) {
this.destroyNonResidentNotifications();
this.openApp();
@ -662,20 +638,5 @@ const Source = new Lang.Class({
}
this.parent();
},
createIcon: function(size) {
if (this.trayIcon) {
return new Clutter.Clone({ width: size,
height: size,
source: this.trayIcon });
} else if (this.app) {
return this.app.create_icon_texture(size);
} else if (this._gicon) {
return new St.Icon({ gicon: this._gicon,
icon_size: size });
} else {
return null;
}
}
});

View File

@ -10,14 +10,21 @@ const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Gdk = imports.gi.Gdk;
const AppDisplay = imports.ui.appDisplay;
const ContactDisplay = imports.ui.contactDisplay;
const Dash = imports.ui.dash;
const DND = imports.ui.dnd;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Panel = imports.ui.panel;
const Params = imports.misc.params;
const PlaceDisplay = imports.ui.placeDisplay;
const RemoteSearch = imports.ui.remoteSearch;
const Tweener = imports.ui.tweener;
const ViewSelector = imports.ui.viewSelector;
const Wanda = imports.ui.wanda;
const WorkspacesView = imports.ui.workspacesView;
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
// Time for initial animation going into Overview mode
@ -70,12 +77,13 @@ const ShellInfo = new Lang.Class({
let notification = null;
if (this._source.notifications.length == 0) {
notification = new MessageTray.Notification(this._source, text, null);
notification.setTransient(true);
} else {
notification = this._source.notifications[0];
notification.update(text, null, { clear: true });
}
notification.setTransient(true);
this._undoCallback = undoCallback;
if (undoCallback) {
notification.addButton('system-undo',
@ -91,21 +99,18 @@ const ShellInfo = new Lang.Class({
const Overview = new Lang.Class({
Name: 'Overview',
_init: function() {
this._overviewCreated = false;
_init : function(params) {
params = Params.parse(params, { isDummy: false });
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
},
this.isDummy = params.isDummy;
_createOverview: function() {
if (this._overviewCreated)
// We only have an overview in user sessions, so
// create a dummy overview in other cases
if (this.isDummy) {
this.animationInProgress = false;
this.visible = false;
return;
if (this.isDummy)
return;
this._overviewCreated = true;
}
// The main BackgroundActor is inside global.window_group which is
// hidden when displaying the overview, so we create a new
@ -142,6 +147,8 @@ const Overview = new Lang.Class({
this._capturedEventId = 0;
this._buttonPressId = 0;
this._workspacesDisplay = null;
this.visible = false; // animating to overview, in overview, animating out
this._shown = false; // show() and not hide()
this._shownTemporarily = false; // showTemporarily() and not hideTemporarily()
@ -178,11 +185,6 @@ const Overview = new Lang.Class({
this._needsFakePointerEvent = false;
},
_sessionUpdated: function() {
this.isDummy = !Main.sessionMode.hasOverview;
this._createOverview();
},
// The members we construct that are implemented in JS might
// want to access the overview as Main.overview to connect
// signal handlers and so forth. So we create them after
@ -193,23 +195,30 @@ const Overview = new Lang.Class({
this._shellInfo = new ShellInfo();
this._searchEntry = new St.Entry({ name: 'searchEntry',
/* Translators: this is the text displayed
in the search entry when no search is
active; it should not exceed ~30
characters. */
hint_text: _("Type to search..."),
track_hover: true,
can_focus: true });
this._group.add_actor(this._searchEntry);
this._dash = new Dash.Dash();
this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
this._dash.showAppsButton);
this._viewSelector = new ViewSelector.ViewSelector();
this._group.add_actor(this._viewSelector.actor);
this._group.add_actor(this._dash.actor);
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
this._viewSelector.addViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
let appView = new AppDisplay.AllAppDisplay();
this._viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run');
// Default search providers
// Wanda comes obviously first
this.addSearchProvider(new Wanda.WandaSearchProvider());
this.addSearchProvider(new AppDisplay.AppSearchProvider());
this.addSearchProvider(new AppDisplay.SettingsSearchProvider());
this.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
this.addSearchProvider(new ContactDisplay.ContactSearchProvider());
// Load remote search providers provided by applications
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
// TODO - recalculate everything when desktop size changes
this._dash = new Dash.Dash();
this._group.add_actor(this._dash.actor);
this._dash.actor.add_constraint(this._viewSelector.constrainY);
this._dash.actor.add_constraint(this._viewSelector.constrainHeight);
this.dashIconSize = this._dash.iconSize;
this._dash.connect('icon-size-changed',
@ -219,7 +228,7 @@ const Overview = new Lang.Class({
// Translators: this is the name of the dock/favorites area on
// the left of the overview
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks-symbolic');
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks');
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
this._relayout();
@ -470,9 +479,7 @@ const Overview = new Lang.Class({
if (windows.length == 0)
return null;
let window = windows[0];
let clone = new Clutter.Clone({ source: window.get_texture(),
x: window.x, y: window.y });
let clone = new Clutter.Clone({ source: windows[0].get_texture() });
clone.source.connect('destroy', Lang.bind(this, function() {
clone.destroy();
}));
@ -497,13 +504,13 @@ const Overview = new Lang.Class({
this._coverPane.set_position(0, contentY);
this._coverPane.set_size(primary.width, contentHeight);
let searchWidth = this._searchEntry.get_width();
let searchHeight = this._searchEntry.get_height();
let searchX = (primary.width - searchWidth) / 2;
let searchY = contentY + this._spacing;
let dashWidth = Math.round(DASH_SPLIT_FRACTION * primary.width);
let dashY = searchY + searchHeight + this._spacing;
let viewWidth = primary.width - dashWidth - this._spacing;
let viewHeight = contentHeight - 2 * this._spacing;
let viewY = contentY + this._spacing;
let viewX = rtl ? 0 : dashWidth + this._spacing;
// Set the dash's x position - y is handled by a constraint
let dashX;
if (rtl) {
this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
@ -511,14 +518,8 @@ const Overview = new Lang.Class({
} else {
dashX = 0;
}
this._dash.actor.set_x(dashX);
let viewX = rtl ? 0 : dashWidth + this._spacing;
let viewY = searchY + searchHeight + this._spacing;
let viewWidth = primary.width - dashWidth - this._spacing;
let viewHeight = contentHeight - this._spacing - viewY;
this._searchEntry.set_position(searchX, searchY);
this._dash.actor.set_position(dashX, dashY);
this._viewSelector.actor.set_position(viewX, viewY);
this._viewSelector.actor.set_size(viewWidth, viewHeight);
},
@ -568,28 +569,6 @@ const Overview = new Lang.Class({
Lang.bind(this, this._onButtonPress));
},
fadeInDesktop: function() {
this._desktopFade.opacity = 0;
this._desktopFade.show();
Tweener.addTween(this._desktopFade,
{ opacity: 255,
time: ANIMATION_TIME,
transition: 'easeOutQuad' });
},
fadeOutDesktop: function() {
if (!this._desktopFade.child)
this._desktopFade.child = this._getDesktopClone();
this._desktopFade.opacity = 255;
this._desktopFade.show();
Tweener.addTween(this._desktopFade,
{ opacity: 0,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
_animateVisible: function() {
if (this.visible || this.animationInProgress)
return;
@ -610,7 +589,21 @@ const Overview = new Lang.Class({
global.window_group.hide();
this._group.show();
this._background.show();
this._viewSelector.show();
this._workspacesDisplay.show();
if (!this._desktopFade.child)
this._desktopFade.child = this._getDesktopClone();
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) {
this._desktopFade.opacity = 255;
this._desktopFade.show();
Tweener.addTween(this._desktopFade,
{ opacity: 0,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
}
this._group.opacity = 0;
Tweener.addTween(this._group,
@ -737,7 +730,16 @@ const Overview = new Lang.Class({
this.animationInProgress = true;
this._hideInProgress = true;
this._viewSelector.zoomFromOverview();
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) {
this._desktopFade.opacity = 0;
this._desktopFade.show();
Tweener.addTween(this._desktopFade,
{ opacity: 255,
time: ANIMATION_TIME,
transition: 'easeOutQuad' });
}
this._workspacesDisplay.zoomFromOverview();
// Make other elements fade out.
Tweener.addTween(this._group,
@ -779,7 +781,8 @@ const Overview = new Lang.Class({
global.window_group.show();
this._viewSelector.hide();
this._workspacesDisplay.hide();
this._desktopFade.hide();
this._background.hide();
this._group.hide();

View File

@ -14,7 +14,6 @@ const St = imports.gi.St;
const Signals = imports.signals;
const Atk = imports.gi.Atk;
const Config = imports.misc.config;
const CtrlAltTab = imports.ui.ctrlAltTab;
const DND = imports.ui.dnd;
@ -22,6 +21,7 @@ const Layout = imports.ui.layout;
const Overview = imports.ui.overview;
const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
const DateMenu = imports.ui.dateMenu;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
@ -32,6 +32,33 @@ const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
const SPINNER_ANIMATION_TIME = 0.2;
const STANDARD_STATUS_AREA_ORDER = ['a11y', 'keyboard', 'volume', 'bluetooth', 'network', 'battery', 'userMenu'];
const STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
'userMenu': imports.ui.userMenu.UserMenuButton
};
if (Config.HAVE_BLUETOOTH)
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['bluetooth'] = imports.ui.status.bluetooth.Indicator;
try {
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['network'] = imports.ui.status.network.NMApplet;
} catch(e) {
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
}
const GDM_STATUS_AREA_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'battery', 'powerMenu'];
const GDM_STATUS_AREA_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
'powerMenu': imports.gdm.powerMenu.PowerMenuButton
};
// To make sure the panel corners blend nicely with the panel,
// we draw background and borders the same way, e.g. drawing
// them as filled shapes from the outside inwards instead of
@ -222,14 +249,14 @@ const AppMenuButton = new Lang.Class({
Name: 'AppMenuButton',
Extends: PanelMenu.Button,
_init: function(panel) {
_init: function(menuManager) {
this.parent(0.0, null, true);
this.actor.accessible_role = Atk.Role.MENU;
this._startingApps = [];
this._menuManager = panel.menuManager;
this._menuManager = menuManager;
this._targetApp = null;
this._appMenuNotifyId = 0;
this._actionGroupNotifyId = 0;
@ -286,7 +313,7 @@ const AppMenuButton = new Lang.Class({
},
show: function() {
if (this._visible || Main.screenShield.locked)
if (this._visible)
return;
this._visible = true;
@ -347,7 +374,6 @@ const AppMenuButton = new Lang.Class({
return;
this._stop = true;
this.actor.reactive = true;
Tweener.addTween(this._spinner.actor,
{ opacity: 0,
time: SPINNER_ANIMATION_TIME,
@ -362,7 +388,6 @@ const AppMenuButton = new Lang.Class({
startAnimation: function() {
this._stop = false;
this.actor.reactive = false;
this._spinner.actor.show();
},
@ -601,8 +626,8 @@ const ActivitiesButton = new Lang.Class({
this.actor.label_actor = this._label;
this.hotCorner = new Layout.HotCorner();
container.add_actor(this.hotCorner.actor);
this._hotCorner = new Layout.HotCorner();
container.add_actor(this._hotCorner.actor);
// Hack up our menu...
this.menu.open = Lang.bind(this, this._onMenuOpenRequest);
@ -652,10 +677,10 @@ const ActivitiesButton = new Lang.Class({
}
hotBox.x1 = Math.round(x);
hotBox.x2 = hotBox.x1 + this.hotCorner.actor.width;
hotBox.x2 = hotBox.x1 + this._hotCorner.actor.width;
hotBox.y1 = Math.round(y);
hotBox.y2 = hotBox.y1 + this.hotCorner.actor.height;
this.hotCorner.actor.allocate(hotBox, flags);
hotBox.y2 = hotBox.y1 + this._hotCorner.actor.height;
this._hotCorner.actor.allocate(hotBox, flags);
},
handleDragOver: function(source, actor, x, y, time) {
@ -677,7 +702,7 @@ const ActivitiesButton = new Lang.Class({
_onCapturedEvent: function(actor, event) {
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
if (!this.hotCorner.shouldToggleOverviewOnClick())
if (!this._hotCorner.shouldToggleOverviewOnClick())
return true;
}
return false;
@ -754,11 +779,10 @@ const PanelCorner = new Lang.Class({
return null;
// Start at the back and work backward
let index;
for (index = children.length - 1; index >= 0; index--) {
if (children[index].visible)
break;
}
let index = children.length - 1;
while (!children[index].visible && index >= 0)
index--;
if (index < 0)
return null;
@ -779,11 +803,10 @@ const PanelCorner = new Lang.Class({
return null;
// Start at the front and work forward
let index;
for (index = 0; index < children.length; index++) {
if (children[index].visible)
break;
}
let index = 0;
while (!children[index].visible && index < children.length)
index++;
if (index == children.length)
return null;
@ -893,29 +916,6 @@ const PanelCorner = new Lang.Class({
}
});
const PANEL_ITEM_IMPLEMENTATIONS = {
'activities': ActivitiesButton,
'appMenu': AppMenuButton,
'dateMenu': imports.ui.dateMenu.DateMenuButton,
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
'powerMenu': imports.gdm.powerMenu.PowerMenuButton,
'userMenu': imports.ui.userMenu.UserMenuButton
};
if (Config.HAVE_BLUETOOTH)
PANEL_ITEM_IMPLEMENTATIONS['bluetooth'] =
imports.ui.status.bluetooth.Indicator;
try {
PANEL_ITEM_IMPLEMENTATIONS['network'] =
imports.ui.status.network.NMApplet;
} catch(e) {
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
}
const Panel = new Lang.Class({
Name: 'Panel',
@ -925,7 +925,7 @@ const Panel = new Lang.Class({
reactive: true });
this.actor._delegate = this;
this.statusArea = {};
this._statusArea = {};
Main.overview.connect('shown', Lang.bind(this, function () {
this.actor.add_style_class_name('in-overview');
@ -934,7 +934,7 @@ const Panel = new Lang.Class({
this.actor.remove_style_class_name('in-overview');
}));
this.menuManager = new PopupMenu.PopupMenuManager(this);
this._menus = new PopupMenu.PopupMenuManager(this);
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
this.actor.add_actor(this._leftBox);
@ -961,12 +961,43 @@ const Panel = new Lang.Class({
this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
Main.layoutManager.panelBox.add(this.actor);
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here-symbolic',
{ sortGroup: CtrlAltTab.SortGroup.TOP });
/* Button on the left side of the panel. */
if (global.session_type == Shell.SessionType.USER) {
this._activitiesButton = new ActivitiesButton();
this._activities = this._activitiesButton.actor;
this._leftBox.add(this._activities);
Main.sessionMode.connect('updated', Lang.bind(this, this._updatePanel));
this._updatePanel();
// The activities button has a pretend menu, so as to integrate
// more cleanly with the rest of the panel
this._menus.addMenu(this._activitiesButton.menu);
this._appMenu = new AppMenuButton(this._menus);
this._leftBox.add(this._appMenu.actor);
}
/* center */
if (global.session_type == Shell.SessionType.USER)
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: true });
else
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: false });
this._centerBox.add(this._dateMenu.actor, { y_fill: true });
this._menus.addMenu(this._dateMenu.menu);
/* right */
if (global.session_type == Shell.SessionType.GDM) {
this._status_area_order = GDM_STATUS_AREA_ORDER;
this._status_area_shell_implementation = GDM_STATUS_AREA_SHELL_IMPLEMENTATION;
} else {
this._status_area_order = STANDARD_STATUS_AREA_ORDER;
this._status_area_shell_implementation = STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION;
}
Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
Main.layoutManager.panelBox.add(this.actor);
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here',
{ sortGroup: CtrlAltTab.SortGroup.TOP });
},
_getPreferredWidth: function(actor, forHeight, alloc) {
@ -1026,19 +1057,16 @@ const Panel = new Lang.Class({
}
this._rightBox.allocate(childBox, flags);
let cornerMinWidth, cornerMinHeight;
let cornerWidth, cornerHeight;
[cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
[cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_height(-1);
let [cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
let [cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_width(-1);
childBox.x1 = 0;
childBox.x2 = cornerWidth;
childBox.y1 = allocHeight;
childBox.y2 = allocHeight + cornerHeight;
this._leftCorner.actor.allocate(childBox, flags);
[cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
[cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_height(-1);
let [cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
let [cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_width(-1);
childBox.x1 = allocWidth - cornerWidth;
childBox.x2 = allocWidth;
childBox.y1 = allocHeight;
@ -1086,126 +1114,84 @@ const Panel = new Lang.Class({
},
openAppMenu: function() {
let indicator = this.statusArea.appMenu;
if (!indicator) // appMenu not supported by current session mode
return;
let menu = indicator.menu;
if (!indicator.actor.reactive || menu.isOpen)
return;
let menu = this._appMenu.menu;
if (Main.overview.visible || menu.isOpen)
return;
menu.open();
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
},
set boxOpacity(value) {
let isReactive = value > 0;
this._leftBox.opacity = value;
this._leftBox.reactive = isReactive;
this._centerBox.opacity = value;
this._centerBox.reactive = isReactive;
this._rightBox.opacity = value;
this._rightBox.reactive = isReactive;
},
get boxOpacity() {
return this._leftBox.opacity;
},
_updatePanel: function() {
let panel = Main.sessionMode.panel;
this._hideIndicators();
this._updateBox(panel.left, this._leftBox);
this._updateBox(panel.center, this._centerBox);
this._updateBox(panel.right, this._rightBox);
},
_initBox: function(elements, box) {
for (let i = 0; i < elements.length; i++) {
let role = elements[i];
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
if (!constructor) {
// panel icon is not supported (can happen for
// bluetooth or network)
continue;
}
}
},
_hideIndicators: function() {
for (let role in PANEL_ITEM_IMPLEMENTATIONS) {
let indicator = this.statusArea[role];
if (!indicator)
continue;
if (indicator.menu)
indicator.menu.close();
indicator.container.hide();
}
},
_ensureIndicator: function(role) {
let indicator = this.statusArea[role];
if (!indicator) {
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
startStatusArea: function() {
for (let i = 0; i < this._status_area_order.length; i++) {
let role = this._status_area_order[i];
let constructor = this._status_area_shell_implementation[role];
if (!constructor) {
// This icon is not implemented (this is a bug)
return null;
}
indicator = new constructor(this);
this.statusArea[role] = indicator;
}
return indicator;
},
_updateBox: function(elements, box) {
let nChildren = box.get_n_children();
for (let i = 0; i < elements.length; i++) {
let role = elements[i];
let indicator = this._ensureIndicator(role);
if (indicator == null)
continue;
}
this._addToPanelBox(role, indicator, i + nChildren, box);
let indicator = new constructor();
this.addToStatusArea(role, indicator, i);
}
},
_addToPanelBox: function(role, indicator, position, box) {
let container = indicator.container;
container.show();
let parent = container.get_parent();
if (parent)
parent.remove_actor(container);
box.insert_child_at_index(container, position);
if (indicator.menu)
this.menuManager.addMenu(indicator.menu);
this.statusArea[role] = indicator;
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
delete this.statusArea[role];
emitter.disconnect(destroyId);
container.destroy();
}));
_insertStatusItem: function(actor, position) {
let children = this._rightBox.get_children();
let i;
for (i = children.length - 1; i >= 0; i--) {
let rolePosition = children[i]._rolePosition;
if (position > rolePosition) {
this._rightBox.insert_child_at_index(actor, i + 1);
break;
}
}
if (i == -1) {
// If we didn't find a position, we must be first
this._rightBox.insert_child_at_index(actor, 0);
}
actor._rolePosition = position;
},
addToStatusArea: function(role, indicator, position, box) {
if (this.statusArea[role])
addToStatusArea: function(role, indicator, position) {
if (this._statusArea[role])
throw new Error('Extension point conflict: there is already a status indicator for role ' + role);
if (!(indicator instanceof PanelMenu.Button))
throw new TypeError('Status indicator must be an instance of PanelMenu.Button');
position = position || 0;
let boxes = {
left: this._leftBox,
center: this._centerBox,
right: this._rightBox
};
let boxContainer = boxes[box] || this._rightBox;
this.statusArea[role] = indicator;
this._addToPanelBox(role, indicator, position, boxContainer);
if (!position)
position = 0;
this._insertStatusItem(indicator.actor, position);
this._menus.addMenu(indicator.menu);
this._statusArea[role] = indicator;
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
this._statusArea[role] = null;
emitter.disconnect(destroyId);
}));
return indicator;
}
},
_onTrayIconAdded: function(o, icon, role) {
if (this._status_area_shell_implementation[role]) {
// This icon is legacy, and replaced by a Shell version
// Hide it
return;
}
icon.height = PANEL_ICON_SIZE;
let buttonBox = new PanelMenu.ButtonBox();
let box = buttonBox.actor;
box.add_actor(icon);
this._insertStatusItem(box, this._status_area_order.indexOf(role));
},
_onTrayIconRemoved: function(o, icon) {
let box = icon.get_parent();
if (box && box._delegate instanceof PanelMenu.ButtonBox)
box.destroy();
},
});

View File

@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
@ -21,10 +20,6 @@ const ButtonBox = new Lang.Class({
this.actor = new Shell.GenericContainer(params);
this.actor._delegate = this;
this.container = new St.Bin({ y_fill: true,
x_fill: true,
child: this.actor });
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
@ -119,12 +114,6 @@ const Button = new Lang.Class({
this.setName(nameText);
},
setSensitive: function(sensitive) {
this.actor.reactive = sensitive;
this.actor.can_focus = sensitive;
this.actor.track_hover = sensitive;
},
setName: function(text) {
if (text != null) {
// This is the easiest way to provide a accessible name to
@ -186,7 +175,8 @@ const Button = new Lang.Class({
_onMenuKeyPress: function(actor, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
let group = global.focus_manager.get_group(this.actor);
let focusManager = St.FocusManager.get_for_stage(global.stage);
let group = focusManager.get_group(this.actor);
if (group) {
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
group.navigate_focus(this.actor, direction, false);
@ -214,8 +204,7 @@ const Button = new Lang.Class({
destroy: function() {
this.actor._delegate = null;
if (this.menu)
this.menu.destroy();
this.menu.destroy();
this.actor.destroy();
this.emit('destroy');
@ -235,36 +224,19 @@ const SystemStatusButton = new Lang.Class({
_init: function(iconName, nameText) {
this.parent(0.0, nameText);
this._iconActor = new St.Icon({ icon_name: iconName,
icon_type: St.IconType.SYMBOLIC,
style_class: 'system-status-icon' });
this.actor.add_actor(this._iconActor);
this.actor.add_style_class_name('panel-status-button');
this._box = new St.BoxLayout({ style_class: 'panel-status-button-box' });
this.actor.add_actor(this._box);
if (iconName)
this.setIcon(iconName);
},
addIcon: function(gicon) {
let icon = new St.Icon({ gicon: gicon,
style_class: 'system-status-icon' });
this._box.add_actor(icon);
return icon;
},
setIcon: function(iconName) {
// Need to first add a NULL GIcon and then set icon_name, to ensure
// compatibility with -symbolic fallbacks
if (!this.mainIcon)
this.mainIcon = this.addIcon(null);
this.mainIcon.icon_name = iconName;
this._iconActor.icon_name = iconName;
},
setGIcon: function(gicon) {
if (this.mainIcon)
this.mainIcon.gicon = gicon;
else
this.mainIcon = this.addIcon(gicon);
this._iconActor.gicon = gicon;
}
});

434
js/ui/placeDisplay.js Normal file
View File

@ -0,0 +1,434 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Signals = imports.signals;
const St = imports.gi.St;
const DND = imports.ui.dnd;
const Main = imports.ui.main;
const Params = imports.misc.params;
const Search = imports.ui.search;
const Util = imports.misc.util;
/**
* Represents a place object, which is most normally a bookmark entry,
* a mount/volume, or a special place like the Home Folder, Computer, and Network.
*
* @name: String title
* @iconFactory: A JavaScript callback which will create an icon texture given a size parameter
* @launch: A JavaScript callback to launch the entry
*/
const PlaceInfo = new Lang.Class({
Name: 'PlaceInfo',
_init: function(id, name, iconFactory, launch) {
this.id = id;
this.name = name;
this._lowerName = name.toLowerCase();
this.iconFactory = iconFactory;
this.launch = launch;
},
matchTerms: function(terms) {
let mtype = Search.MatchType.NONE;
for (let i = 0; i < terms.length; i++) {
let term = terms[i];
let idx = this._lowerName.indexOf(term);
if (idx == 0) {
mtype = Search.MatchType.PREFIX;
} else if (idx > 0) {
if (mtype == Search.MatchType.NONE)
mtype = Search.MatchType.SUBSTRING;
} else {
return Search.MatchType.NONE;
}
}
return mtype;
},
isRemovable: function() {
return false;
}
});
// Helper function to translate launch parameters into a GAppLaunchContext
function _makeLaunchContext(params)
{
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
let launchContext = global.create_app_launch_context();
if (params.workspace != -1)
launchContext.set_desktop(params.workspace);
if (params.timestamp != 0)
launchContext.set_timestamp(params.timestamp);
return launchContext;
}
const PlaceDeviceInfo = new Lang.Class({
Name: 'PlaceDeviceInfo',
Extends: PlaceInfo,
_init: function(mount) {
this._mount = mount;
this.name = mount.get_name();
this._lowerName = this.name.toLowerCase();
this.id = 'mount:' + mount.get_root().get_uri();
},
iconFactory: function(size) {
let icon = this._mount.get_icon();
return St.TextureCache.get_default().load_gicon(null, icon, size);
},
launch: function(params) {
Gio.app_info_launch_default_for_uri(this._mount.get_root().get_uri(),
_makeLaunchContext(params));
},
isRemovable: function() {
return this._mount.can_unmount();
},
remove: function() {
if (!this.isRemovable())
return;
if (this._mount.can_eject())
this._mount.eject(0, null, Lang.bind(this, this._removeFinish));
else
this._mount.unmount(0, null, Lang.bind(this, this._removeFinish));
},
_removeFinish: function(o, res, data) {
try {
if (this._mount.can_eject())
this._mount.eject_finish(res);
else
this._mount.unmount_finish(res);
} catch (e) {
let message = _("Failed to unmount '%s'").format(o.get_name());
Main.overview.setMessage(message,
Lang.bind(this, this.remove),
_("Retry"));
}
}
});
const PlacesManager = new Lang.Class({
Name: 'PlacesManager',
_init: function() {
this._defaultPlaces = [];
this._mounts = [];
this._bookmarks = [];
let homeFile = Gio.file_new_for_path (GLib.get_home_dir());
let homeUri = homeFile.get_uri();
let homeLabel = Shell.util_get_label_for_uri (homeUri);
let homeIcon = Shell.util_get_icon_for_uri (homeUri);
this._home = new PlaceInfo('special:home', homeLabel,
function(size) {
return St.TextureCache.get_default().load_gicon(null, homeIcon, size);
},
function(params) {
Gio.app_info_launch_default_for_uri(homeUri, _makeLaunchContext(params));
});
let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
let desktopFile = Gio.file_new_for_path (desktopPath);
let desktopUri = desktopFile.get_uri();
let desktopLabel = Shell.util_get_label_for_uri (desktopUri);
let desktopIcon = Shell.util_get_icon_for_uri (desktopUri);
this._desktopMenu = new PlaceInfo('special:desktop', desktopLabel,
function(size) {
return St.TextureCache.get_default().load_gicon(null, desktopIcon, size);
},
function(params) {
Gio.app_info_launch_default_for_uri(desktopUri, _makeLaunchContext(params));
});
this._connect = new PlaceInfo('special:connect', _("Connect to..."),
function (size) {
// do NOT use St.Icon here, it crashes the shell
// see wanda.js for details
return St.TextureCache.get_default().load_icon_name(null,
'applications-internet',
St.IconType.FULLCOLOR,
size);
},
function (params) {
// BUG: nautilus-connect-server doesn't have a desktop file, so we can't
// launch it with the workspace from params. It's probably pretty rare
// and odd to drag this place onto a workspace in any case
Util.spawn(['nautilus-connect-server']);
});
this._defaultPlaces.push(this._home);
this._defaultPlaces.push(this._desktopMenu);
this._defaultPlaces.push(this._connect);
/*
* Show devices, code more or less ported from nautilus-places-sidebar.c
*/
this._volumeMonitor = Gio.VolumeMonitor.get();
this._volumeMonitor.connect('volume-added', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('volume-removed',Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('volume-changed', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('mount-added', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('mount-changed', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('drive-changed', Lang.bind(this, this._updateDevices));
this._updateDevices();
this._bookmarksPath = GLib.build_filenamev([GLib.get_home_dir(), '.gtk-bookmarks']);
this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath);
this._monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
this._bookmarkTimeoutId = 0;
this._monitor.connect('changed', Lang.bind(this, function () {
if (this._bookmarkTimeoutId > 0)
return;
/* Defensive event compression */
this._bookmarkTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function () {
this._bookmarkTimeoutId = 0;
this._reloadBookmarks();
return false;
}));
}));
this._reloadBookmarks();
},
_updateDevices: function() {
this._mounts = [];
/* first go through all connected drives */
let drives = this._volumeMonitor.get_connected_drives();
for (let i = 0; i < drives.length; i++) {
let volumes = drives[i].get_volumes();
for(let j = 0; j < volumes.length; j++) {
let mount = volumes[j].get_mount();
if(mount != null) {
this._addMount(mount);
}
}
}
/* add all volumes that is not associated with a drive */
let volumes = this._volumeMonitor.get_volumes();
for(let i = 0; i < volumes.length; i++) {
if(volumes[i].get_drive() != null)
continue;
let mount = volumes[i].get_mount();
if(mount != null) {
this._addMount(mount);
}
}
/* add mounts that have no volume (/etc/mtab mounts, ftp, sftp,...) */
let mounts = this._volumeMonitor.get_mounts();
for(let i = 0; i < mounts.length; i++) {
if(mounts[i].is_shadowed())
continue;
if(mounts[i].get_volume())
continue;
this._addMount(mounts[i]);
}
/* We emit two signals, one for a generic 'all places' update
* and the other for one specific to mounts. We do this because
* clients like PlaceDisplay may only care about places in general
* being updated while clients like DashPlaceDisplay care which
* specific type of place got updated.
*/
this.emit('mounts-updated');
this.emit('places-updated');
},
_reloadBookmarks: function() {
this._bookmarks = [];
if (!GLib.file_test(this._bookmarksPath, GLib.FileTest.EXISTS))
return;
let bookmarksContent = Shell.get_file_contents_utf8_sync(this._bookmarksPath);
let bookmarks = bookmarksContent.split('\n');
let bookmarksToLabel = {};
let bookmarksOrder = [];
for (let i = 0; i < bookmarks.length; i++) {
let bookmarkLine = bookmarks[i];
let components = bookmarkLine.split(' ');
let bookmark = components[0];
if (bookmark in bookmarksToLabel)
continue;
let label = null;
if (components.length > 1)
label = components.slice(1).join(' ');
bookmarksToLabel[bookmark] = label;
bookmarksOrder.push(bookmark);
}
for (let i = 0; i < bookmarksOrder.length; i++) {
let bookmark = bookmarksOrder[i];
let label = bookmarksToLabel[bookmark];
let file = Gio.file_new_for_uri(bookmark);
if (!file.query_exists(null))
continue;
if (label == null)
label = Shell.util_get_label_for_uri(bookmark);
if (label == null)
continue;
let icon = Shell.util_get_icon_for_uri(bookmark);
let item = new PlaceInfo('bookmark:' + bookmark, label,
function(size) {
return St.TextureCache.get_default().load_gicon(null, icon, size);
},
function(params) {
Gio.app_info_launch_default_for_uri(bookmark, _makeLaunchContext(params));
});
this._bookmarks.push(item);
}
/* See comment in _updateDevices for explanation why there are two signals. */
this.emit('bookmarks-updated');
this.emit('places-updated');
},
_addMount: function(mount) {
let devItem = new PlaceDeviceInfo(mount);
this._mounts.push(devItem);
},
getAllPlaces: function () {
return this.getDefaultPlaces().concat(this.getBookmarks(), this.getMounts());
},
getDefaultPlaces: function () {
return this._defaultPlaces;
},
getBookmarks: function () {
return this._bookmarks;
},
getMounts: function () {
return this._mounts;
},
_lookupIndexById: function(sourceArray, id) {
for (let i = 0; i < sourceArray.length; i++) {
let place = sourceArray[i];
if (place.id == id)
return i;
}
return -1;
},
lookupPlaceById: function(id) {
let colonIdx = id.indexOf(':');
let type = id.substring(0, colonIdx);
let sourceArray = null;
if (type == 'special')
sourceArray = this._defaultPlaces;
else if (type == 'mount')
sourceArray = this._mounts;
else if (type == 'bookmark')
sourceArray = this._bookmarks;
return sourceArray[this._lookupIndexById(sourceArray, id)];
},
_removeById: function(sourceArray, id) {
sourceArray.splice(this._lookupIndexById(sourceArray, id), 1);
}
});
Signals.addSignalMethods(PlacesManager.prototype);
const PlaceSearchProvider = new Lang.Class({
Name: 'PlaceSearchProvider',
Extends: Search.SearchProvider,
_init: function() {
this.parent(_("PLACES & DEVICES"));
},
getResultMetas: function(resultIds) {
let metas = [];
for (let i = 0; i < resultIds.length; i++) {
let placeInfo = Main.placesManager.lookupPlaceById(resultIds[i]);
if (!placeInfo)
metas.push(null);
else
metas.push({ 'id': resultIds[i],
'name': placeInfo.name,
'createIcon': function(size) {
return placeInfo.iconFactory(size);
}
});
}
return metas;
},
activateResult: function(id, params) {
let placeInfo = Main.placesManager.lookupPlaceById(id);
placeInfo.launch(params);
},
_compareResultMeta: function (idA, idB) {
let infoA = Main.placesManager.lookupPlaceById(idA);
let infoB = Main.placesManager.lookupPlaceById(idB);
return infoA.name.localeCompare(infoB.name);
},
_searchPlaces: function(places, terms) {
let multiplePrefixResults = [];
let prefixResults = [];
let multipleSubstringResults = [];
let substringResults = [];
terms = terms.map(String.toLowerCase);
for (let i = 0; i < places.length; i++) {
let place = places[i];
let mtype = place.matchTerms(terms);
if (mtype == Search.MatchType.MULTIPLE_PREFIX)
multiplePrefixResults.push(place.id);
else if (mtype == Search.MatchType.PREFIX)
prefixResults.push(place.id);
else if (mtype == Search.MatchType.MULTIPLE_SUBSTRING)
multipleSubstringResults.push(place.id);
else if (mtype == Search.MatchType.SUBSTRING)
substringResults.push(place.id);
}
multiplePrefixResults.sort(this._compareResultMeta);
prefixResults.sort(this._compareResultMeta);
multipleSubstringResults.sort(this._compareResultMeta);
substringResults.sort(this._compareResultMeta);
return multiplePrefixResults.concat(prefixResults.concat(multipleSubstringResults.concat(substringResults)));
},
getInitialResultSet: function(terms) {
let places = Main.placesManager.getAllPlaces();
return this._searchPlaces(places, terms);
},
getSubsearchResultSet: function(previousResults, terms) {
let places = previousResults.map(function (id) { return Main.placesManager.lookupPlaceById(id); });
return this._searchPlaces(places, terms);
}
});

View File

@ -1,126 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
// We stop polling if the user is idle for more than this amount of time
const IDLE_TIME = 1000;
// This file implements a reasonably efficient system for tracking the position
// of the mouse pointer. We simply query the pointer from the X server in a loop,
// but we turn off the polling when the user is idle.
let _pointerWatcher = null;
function getPointerWatcher() {
if (_pointerWatcher == null)
_pointerWatcher = new PointerWatcher();
return _pointerWatcher;
}
const PointerWatch = new Lang.Class({
Name: 'PointerWatch',
_init: function(watcher, interval, callback) {
this.watcher = watcher;
this.interval = interval;
this.callback = callback;
},
// remove:
// remove this watch. This function may safely be called
// while the callback is executing.
remove: function() {
this.watcher._removeWatch(this);
}
});
const PointerWatcher = new Lang.Class({
Name: 'PointerWatcher',
_init: function() {
let idleMonitor = Shell.IdleMonitor.get();
idleMonitor.add_watch(IDLE_TIME,
Lang.bind(this, this._onIdleMonitorWatch));
this._idle = idleMonitor.get_idletime() > IDLE_TIME;
this._watches = [];
this.pointerX = null;
this.pointerY = null;
},
// addWatch:
// @interval: hint as to the time resolution needed. When the user is
// not idle, the position of the pointer will be queried at least
// once every this many milliseconds.
// @callback: function to call when the pointer position changes - takes
// two arguments, X and Y.
//
// Set up a watch on the position of the mouse pointer. Returns a
// PointerWatch object which has a remove() method to remove the watch.
addWatch: function(interval, callback) {
// Avoid unreliably calling the watch for the current position
this._updatePointer();
let watch = new PointerWatch(this, interval, callback);
this._watches.push(watch);
this._updateTimeout();
return watch;
},
_removeWatch: function(watch) {
for (let i = 0; i < this._watches.length; i++) {
if (this._watches[i] == watch) {
this._watches.splice(i, 1);
this._updateTimeout();
return;
}
}
},
_onIdleMonitorWatch: function(monitor, id, userBecameIdle) {
this._idle = userBecameIdle;
if (!userBecameIdle)
this._updatePointer();
this._updateTimeout();
},
_updateTimeout: function() {
if (this._timeoutId) {
Mainloop.source_remove(this._timeoutId);
this._timeoutId = 0;
}
if (this._idle || this._watches.length == 0)
return;
let minInterval = this._watches[0].interval;
for (let i = 1; i < this._watches.length; i++)
minInterval = Math.min(this._watches[i].interval, minInterval);
this._timeoutId = Mainloop.timeout_add(minInterval,
Lang.bind(this, this._onTimeout));
},
_onTimeout: function() {
this._updatePointer();
return true;
},
_updatePointer: function() {
let [x, y, mods] = global.get_pointer();
if (this.pointerX == x && this.pointerY == y)
return;
this.pointerX = x;
this.pointerY = y;
for (let i = 0; i < this._watches.length;) {
let watch = this._watches[i];
watch.callback(x, y);
if (watch == this._watches[i]) // guard against self-removal
i++;
}
}
});

View File

@ -1,4 +1,24 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/*
* Copyright 2010 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
const Lang = imports.lang;
const Signals = imports.signals;
@ -13,12 +33,8 @@ const Mainloop = imports.mainloop;
const Polkit = imports.gi.Polkit;
const PolkitAgent = imports.gi.PolkitAgent;
const Components = imports.ui.components;
const ModalDialog = imports.ui.modalDialog;
const ShellEntry = imports.ui.shellEntry;
const UserMenu = imports.ui.userMenu;
const DIALOG_ICON_SIZE = 48;
const AuthenticationDialog = new Lang.Class({
Name: 'AuthenticationDialog',
@ -49,7 +65,7 @@ const AuthenticationDialog = new Lang.Class({
let messageBox = new St.BoxLayout({ style_class: 'prompt-dialog-message-layout',
vertical: true });
mainContentBox.add(messageBox,
{ expand: true, y_align: St.Align.START });
{ y_align: St.Align.START });
this._subjectLabel = new St.Label({ style_class: 'prompt-dialog-headline',
text: _("Authentication Required") });
@ -101,11 +117,9 @@ const AuthenticationDialog = new Lang.Class({
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
vertical: false });
messageBox.add(userBox);
this._userAvatar = new UserMenu.UserAvatarWidget(this._user,
{ iconSize: DIALOG_ICON_SIZE,
styleClass: 'polkit-dialog-user-icon' });
this._userAvatar.actor.hide();
userBox.add(this._userAvatar.actor,
this._userIcon = new St.Icon();
this._userIcon.hide();
userBox.add(this._userIcon,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
@ -121,17 +135,17 @@ const AuthenticationDialog = new Lang.Class({
this._onUserChanged();
this._passwordBox = new St.BoxLayout({ vertical: false, style_class: 'prompt-dialog-password-box' });
this._passwordBox = new St.BoxLayout({ vertical: false });
messageBox.add(this._passwordBox);
this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
this._passwordBox.add(this._passwordLabel, { y_fill: false, y_align: St.Align.MIDDLE });
this._passwordBox.add(this._passwordLabel);
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
text: "",
can_focus: true});
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
this._passwordBox.add(this._passwordEntry,
{ expand: true });
{expand: true });
this.setInitialKeyFocus(this._passwordEntry);
this._passwordBox.hide();
@ -153,7 +167,6 @@ const AuthenticationDialog = new Lang.Class({
*/
this._nullMessageLabel = new St.Label({ style_class: 'prompt-dialog-null-label',
text: 'abc'});
this._nullMessageLabel.add_style_class_name('hidden');
this._nullMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._nullMessageLabel.clutter_text.line_wrap = true;
messageBox.add(this._nullMessageLabel);
@ -164,8 +177,7 @@ const AuthenticationDialog = new Lang.Class({
key: Clutter.Escape
},
{ label: _("Authenticate"),
action: Lang.bind(this, this._onAuthenticateButtonPressed),
default: true
action: Lang.bind(this, this._onAuthenticateButtonPressed)
}]);
this._doneEmitted = false;
@ -256,7 +268,7 @@ const AuthenticationDialog = new Lang.Class({
_onSessionRequest: function(session, request, echo_on) {
// Cheap localization trick
if (request == 'Password:' || request == 'Password: ')
if (request == 'Password:')
this._passwordLabel.set_text(_("Password:"));
else
this._passwordLabel.set_text(request);
@ -299,9 +311,19 @@ const AuthenticationDialog = new Lang.Class({
},
_onUserChanged: function() {
if (this._user.is_loaded && this._userAvatar) {
this._userAvatar.update();
this._userAvatar.actor.show();
if (this._user.is_loaded) {
if (this._userIcon) {
let iconFileName = this._user.get_icon_file();
let iconFile = Gio.file_new_for_path(iconFileName);
let icon;
if (iconFile.query_exists(null)) {
icon = new Gio.FileIcon({file: iconFile});
} else {
icon = new Gio.ThemedIcon({name: 'avatar-default'});
}
this._userIcon.set_gicon (icon);
this._userIcon.show();
}
}
},
@ -317,20 +339,11 @@ const AuthenticationAgent = new Lang.Class({
Name: 'AuthenticationAgent',
_init: function() {
this._currentDialog = null;
this._isCompleting = false;
this._handle = null;
this._native = new Shell.PolkitAuthenticationAgent();
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
this._native.connect('cancel', Lang.bind(this, this._onCancel));
},
enable: function() {
this._native.register();
},
disable: function() {
this._native.unregister();
this._currentDialog = null;
this._isCompleting = false;
},
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
@ -388,4 +401,6 @@ const AuthenticationAgent = new Lang.Class({
}
});
const Component = AuthenticationAgent;
function init() {
let agent = new AuthenticationAgent();
}

View File

@ -37,13 +37,12 @@ const PopupBaseMenuItem = new Lang.Class({
activate: true,
hover: true,
sensitive: true,
style_class: null,
can_focus: true
style_class: null
});
this.actor = new Shell.GenericContainer({ style_class: 'popup-menu-item',
reactive: params.reactive,
track_hover: params.reactive,
can_focus: params.can_focus,
can_focus: params.reactive,
accessible_role: Atk.Role.MENU_ITEM});
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
@ -61,9 +60,6 @@ const PopupBaseMenuItem = new Lang.Class({
this.setSensitive(this.sensitive);
if (!this._activatable)
this.actor.add_style_class_name('popup-inactive-menu-item');
if (params.style_class)
this.actor.add_style_class_name(params.style_class);
@ -73,9 +69,10 @@ const PopupBaseMenuItem = new Lang.Class({
}
if (params.reactive && params.hover)
this.actor.connect('notify::hover', Lang.bind(this, this._onHoverChanged));
this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
this.actor.connect('key-focus-out', Lang.bind(this, this._onKeyFocusOut));
if (params.reactive) {
this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
this.actor.connect('key-focus-out', Lang.bind(this, this._onKeyFocusOut));
}
},
_onStyleChanged: function (actor) {
@ -139,6 +136,10 @@ const PopupBaseMenuItem = new Lang.Class({
this.actor.reactive = sensitive;
this.actor.can_focus = sensitive;
if (sensitive)
this.actor.remove_style_pseudo_class('insensitive');
else
this.actor.add_style_pseudo_class('insensitive');
this.emit('sensitive-changed', sensitive);
},
@ -183,14 +184,12 @@ const PopupBaseMenuItem = new Lang.Class({
this._dot = new St.DrawingArea({ style_class: 'popup-menu-item-dot' });
this._dot.connect('repaint', Lang.bind(this, this._onRepaintDot));
this.actor.add_actor(this._dot);
this.actor.add_accessible_state (Atk.StateType.CHECKED);
} else {
if (!this._dot)
return;
this._dot.destroy();
this._dot = null;
this.actor.remove_accessible_state (Atk.StateType.CHECKED);
}
},
@ -401,8 +400,7 @@ const PopupSeparatorMenuItem = new Lang.Class({
Extends: PopupBaseMenuItem,
_init: function () {
this.parent({ reactive: false,
can_focus: false});
this.parent({ reactive: false });
this._drawingArea = new St.DrawingArea({ style_class: 'popup-separator-menu-item' });
this.addActor(this._drawingArea, { span: -1, expand: true });
@ -721,8 +719,7 @@ const Switch = new Lang.Class({
_init: function(state) {
this.actor = new St.Bin({ style_class: 'toggle-switch',
accessible_role: Atk.Role.CHECK_BOX,
can_focus: true });
accessible_role: Atk.Role.CHECK_BOX});
// Translators: this MUST be either "toggle-switch-us"
// (for toggle switches containing the English words
// "ON" and "OFF") or "toggle-switch-intl" (for toggle
@ -766,7 +763,7 @@ const PopupSwitchMenuItem = new Lang.Class({
{ expand: true, span: -1, align: St.Align.END });
this._statusLabel = new St.Label({ text: '',
style_class: 'popup-status-menu-item'
style_class: 'popup-inactive-menu-item'
});
this._statusBin.child = this._switch.actor;
},
@ -776,10 +773,12 @@ const PopupSwitchMenuItem = new Lang.Class({
this._statusLabel.text = text;
this._statusBin.child = this._statusLabel;
this.actor.reactive = false;
this.actor.can_focus = false;
this.actor.accessible_role = Atk.Role.MENU_ITEM;
} else {
this._statusBin.child = this._switch.actor;
this.actor.reactive = true;
this.actor.can_focus = true;
this.actor.accessible_role = Atk.Role.CHECK_MENU_ITEM;
}
this.checkAccessibleState();
@ -870,15 +869,12 @@ const PopupMenuBase = new Lang.Class({
// for the menu which causes its prelight state to freeze
this.blockSourceEvents = false;
// Can be set while a menu is up to let all events through without special
// menu handling useful for scrollbars in menus, and probably not otherwise.
this.passEvents = false;
this._activeMenuItem = null;
this._childMenus = [];
this._settingsActions = { };
this._sessionUpdatedId = Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
},
_sessionUpdated: function() {
this._setSettingsVisibility(Main.sessionMode.allowSettings);
},
addAction: function(title, callback) {
@ -892,6 +888,10 @@ const PopupMenuBase = new Lang.Class({
},
addSettingsAction: function(title, desktopFile) {
// Don't allow user settings to get edited unless we're in a user session
if (global.session_type != Shell.SessionType.USER)
return null;
let menuItem = this.addAction(title, function() {
let app = Shell.AppSystem.get_default().lookup_setting(desktopFile);
@ -903,22 +903,11 @@ const PopupMenuBase = new Lang.Class({
Main.overview.hide();
app.activate();
});
menuItem.actor.visible = Main.sessionMode.allowSettings;
this._settingsActions[desktopFile] = menuItem;
return menuItem;
},
_setSettingsVisibility: function(visible) {
for (let id in this._settingsActions) {
let item = this._settingsActions[id];
item.actor.visible = visible;
}
},
isEmpty: function() {
return this.box.get_n_children() == 0;
return this.box.get_children().length == 0;
},
isChildMenu: function(menu) {
@ -954,7 +943,7 @@ const PopupMenuBase = new Lang.Class({
_connectSubMenuSignals: function(object, menu) {
object._subMenuActivateId = menu.connect('activate', Lang.bind(this, function() {
this.emit('activate');
this.close(BoxPointer.PopupAnimation.FULL);
this.close(true);
}));
object._subMenuActiveChangeId = menu.connect('active-changed', Lang.bind(this, function(submenu, submenuItem) {
if (this._activeMenuItem && this._activeMenuItem != submenuItem)
@ -989,7 +978,7 @@ const PopupMenuBase = new Lang.Class({
}));
menuItem._activateId = menuItem.connect('activate', Lang.bind(this, function (menuItem, event) {
this.emit('activate', menuItem);
this.close(BoxPointer.PopupAnimation.FULL);
this.close(true);
}));
// the weird name is to avoid a conflict with some random property
// the menuItem may have, called destroyId
@ -1059,17 +1048,14 @@ const PopupMenuBase = new Lang.Class({
if (menuItem instanceof PopupMenuSection) {
this._connectSubMenuSignals(menuItem, menuItem);
menuItem._parentOpenStateChangedId = this.connect('open-state-changed',
menuItem._closingId = this.connect('open-state-changed',
function(self, open) {
if (open)
menuItem.open();
else
menuItem.close();
if (!open)
menuItem.close(false);
});
menuItem.connect('destroy', Lang.bind(this, function() {
menuItem.disconnect(menuItem._subMenuActivateId);
menuItem.disconnect(menuItem._subMenuActiveChangeId);
this.disconnect(menuItem._parentOpenStateChangedId);
this.length--;
}));
@ -1082,7 +1068,7 @@ const PopupMenuBase = new Lang.Class({
this._connectItemSignals(menuItem);
menuItem._closingId = this.connect('open-state-changed', function(self, open) {
if (!open)
menuItem.menu.close(BoxPointer.PopupAnimation.FADE);
menuItem.menu.close(false);
});
} else if (menuItem instanceof PopupSeparatorMenuItem) {
this._connectItemSignals(menuItem);
@ -1104,8 +1090,7 @@ const PopupMenuBase = new Lang.Class({
let columnWidths = [];
let items = this.box.get_children();
for (let i = 0; i < items.length; i++) {
if (!items[i].visible &&
!(items[i]._delegate instanceof PopupSubMenu && items[i-1].visible))
if (!items[i].visible)
continue;
if (items[i]._delegate instanceof PopupBaseMenuItem || items[i]._delegate instanceof PopupMenuBase) {
let itemColumnWidths = items[i]._delegate.getColumnWidths();
@ -1170,9 +1155,9 @@ const PopupMenuBase = new Lang.Class({
toggle: function() {
if (this.isOpen)
this.close(BoxPointer.PopupAnimation.FULL);
this.close(true);
else
this.open(BoxPointer.PopupAnimation.FULL);
this.open(true);
},
destroy: function() {
@ -1180,9 +1165,6 @@ const PopupMenuBase = new Lang.Class({
this.actor.destroy();
this.emit('destroy');
Main.sessionMode.disconnect(this._sessionUpdatedId);
this._sessionUpdatedId = 0;
}
});
Signals.addSignalMethods(PopupMenuBase.prototype);
@ -1236,7 +1218,7 @@ const PopupMenu = new Lang.Class({
_onKeyPressEvent: function(actor, event) {
if (event.get_key_symbol() == Clutter.Escape) {
this.close(BoxPointer.PopupAnimation.FULL);
this.close(true);
return true;
}
@ -1269,14 +1251,13 @@ const PopupMenu = new Lang.Class({
},
close: function(animate) {
if (!this.isOpen)
return;
if (this._activeMenuItem)
this._activeMenuItem.setActive(false);
if (this._boxPointer.actor.visible)
this._boxPointer.hide(animate);
if (!this.isOpen)
return;
this._boxPointer.hide(animate);
this.isOpen = false;
this.emit('open-state-changed', false);
@ -1300,6 +1281,24 @@ const PopupSubMenu = new Lang.Class({
hscrollbar_policy: Gtk.PolicyType.NEVER,
vscrollbar_policy: Gtk.PolicyType.NEVER });
// StScrollbar plays dirty tricks with events, calling
// clutter_set_motion_events_enabled (FALSE) during the scroll; this
// confuses our event tracking, so we just turn it off during the
// scroll.
let vscroll = this.actor.get_vscroll_bar();
vscroll.connect('scroll-start',
Lang.bind(this, function() {
let topMenu = this._getTopMenu();
if (topMenu)
topMenu.passEvents = true;
}));
vscroll.connect('scroll-stop',
Lang.bind(this, function() {
let topMenu = this._getTopMenu();
if (topMenu)
topMenu.passEvents = false;
}));
this.actor.add_actor(this.box);
this.actor._delegate = this;
this.actor.clip_to_allocation = true;
@ -1349,11 +1348,6 @@ const PopupSubMenu = new Lang.Class({
this.actor.vscrollbar_policy =
needsScrollbar ? Gtk.PolicyType.AUTOMATIC : Gtk.PolicyType.NEVER;
if (needsScrollbar)
this.actor.add_style_pseudo_class('scrolled');
else
this.actor.remove_style_pseudo_class('scrolled');
// It looks funny if we animate with a scrollbar (at what point is
// the scrollbar added?) so just skip that case
if (animate && needsScrollbar)
@ -1426,7 +1420,7 @@ const PopupSubMenu = new Lang.Class({
// Move focus back to parent menu if the user types Left.
if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) {
this.close(BoxPointer.PopupAnimation.FULL);
this.close(true);
this.sourceActor._delegate.setActive(true);
return true;
}
@ -1460,7 +1454,7 @@ const PopupMenuSection = new Lang.Class({
// deliberately ignore any attempt to open() or close(), but emit the
// corresponding signal so children can still pick it up
open: function() { this.emit('open-state-changed', true); },
open: function(animate) { this.emit('open-state-changed', true); },
close: function() { this.emit('open-state-changed', false); },
destroy: function() {
@ -1508,7 +1502,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Right) {
this.menu.open(BoxPointer.PopupAnimation.FULL);
this.menu.open(true);
this.menu.actor.navigate_focus(null, Gtk.DirectionType.DOWN, false);
return true;
} else if (symbol == Clutter.KEY_Left && this.menu.isOpen) {
@ -1520,7 +1514,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
},
activate: function(event) {
this.menu.open(BoxPointer.PopupAnimation.FULL);
this.menu.open(true);
},
_onButtonReleaseEvent: function(actor) {
@ -1547,7 +1541,7 @@ const PopupComboMenu = new Lang.Class({
_onKeyPressEvent: function(actor, event) {
if (event.get_key_symbol() == Clutter.Escape) {
this.close(BoxPointer.PopupAnimation.FULL);
this.close(true);
return true;
}
@ -1899,6 +1893,10 @@ const RemoteMenu = new Lang.Class({
}
item.actor.reactive = item.actor.can_focus = action.enabled;
if (action.enabled)
item.actor.remove_style_pseudo_class('insensitive');
else
item.actor.add_style_pseudo_class('insensitive');
destroyId = item.connect('destroy', Lang.bind(this, function() {
item.disconnect(destroyId);
@ -1923,7 +1921,7 @@ const RemoteMenu = new Lang.Class({
while (k0 < currentItems.length && currentItems[k0]._ignored)
k0++;
// find the right menu item matching the model item
for (j0 = 0; k0 < currentItems.length && j0 < position; j0++, k0++) {
for (j0 = 0; j0 < position; j0++, k0++) {
if (currentItems[k0]._ignored)
k0++;
}
@ -1933,7 +1931,7 @@ const RemoteMenu = new Lang.Class({
for (k = k0; k < currentItems.length; k++)
currentItems[k].destroy();
} else {
for (j = j0, k = k0; k < currentItems.length && j < j0 + removed; j++, k++) {
for (j = j0, k = k0; j < j0 + removed; j++, k++) {
currentItems[k].destroy();
if (currentItems[k]._ignored)
@ -1964,9 +1962,8 @@ const RemoteMenu = new Lang.Class({
k++;
}
} else if (changeSignal) {
let signalId = this.actionGroup.connect(changeSignal, Lang.bind(this, function(actionGroup, actionName) {
actionGroup.disconnect(signalId);
if (this._actions[actionName]) return;
let signalId = this.actionGroup.connect(changeSignal, Lang.bind(this, function() {
this.actionGroup.disconnect(signalId);
// force a full update
this._modelChanged(model, 0, -1, model.get_n_items(), target);
@ -2031,6 +2028,11 @@ const RemoteMenu = new Lang.Class({
for (let i = 0; i < action.items.length; i++) {
let item = action.items[i];
item.actor.reactive = item.actor.can_focus = action.enabled;
if (action.enabled)
item.actor.remove_style_pseudo_class('insensitive');
else
item.actor.add_style_pseudo_class('insensitive');
}
}
}
@ -2058,9 +2060,6 @@ const PopupMenuManager = new Lang.Class({
},
addMenu: function(menu, position) {
if (this._findMenu(menu) > -1)
return;
let menudata = {
menu: menu,
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)),
@ -2195,11 +2194,11 @@ const PopupMenuManager = new Lang.Class({
let oldMenu = this._activeMenu;
this._activeMenu = null;
for (let i = this._menuStack.length - 1; i >= 0; i--)
this._menuStack[i].close(BoxPointer.PopupAnimation.FADE);
oldMenu.close(BoxPointer.PopupAnimation.FADE);
newMenu.open(BoxPointer.PopupAnimation.FADE);
this._menuStack[i].close(false);
oldMenu.close(false);
newMenu.open(false);
} else
newMenu.open(BoxPointer.PopupAnimation.FULL);
newMenu.open(true);
},
_onMenuSourceEnter: function(menu) {
@ -2284,6 +2283,9 @@ const PopupMenuManager = new Lang.Class({
this._owner.menuEventFilter(event))
return true;
if (this._activeMenu != null && this._activeMenu.passEvents)
return false;
if (this._didPop) {
this._didPop = false;
return true;
@ -2311,6 +2313,6 @@ const PopupMenuManager = new Lang.Class({
_closeMenu: function() {
if (this._activeMenu != null)
this._activeMenu.close(BoxPointer.PopupAnimation.FULL);
this._activeMenu.close(true);
}
});

View File

@ -34,17 +34,16 @@ var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
function loadRemoteSearchProviders(addProviderCallback) {
let dataDirs = GLib.get_system_data_dirs();
let loadedProviders = {};
for (let i = 0; i < dataDirs.length; i++) {
let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', 'search-providers']);
let dir = Gio.file_new_for_path(path);
if (!dir.query_exists(null))
continue;
loadRemoteSearchProvidersFromDir(dir, loadedProviders, addProviderCallback);
loadRemoteSearchProvidersFromDir(dir, addProviderCallback);
}
};
function loadRemoteSearchProvidersFromDir(dir, loadedProviders, addProviderCallback) {
function loadRemoteSearchProvidersFromDir(dir, addProviderCallback) {
let dirPath = dir.get_path();
FileUtils.listDirAsync(dir, Lang.bind(this, function(files) {
for (let i = 0; i < files.length; i++) {
@ -63,34 +62,15 @@ function loadRemoteSearchProvidersFromDir(dir, loadedProviders, addProviderCallb
let remoteProvider, title;
try {
let group = KEY_FILE_GROUP;
let icon = keyfile.get_string(group, 'Icon');
let busName = keyfile.get_string(group, 'BusName');
let objectPath = keyfile.get_string(group, 'ObjectPath');
if (loadedProviders[objectPath])
continue;
let appInfo = null;
try {
let desktopId = keyfile.get_string(group, 'DesktopId');
appInfo = Gio.DesktopAppInfo.new(desktopId);
} catch (e) {
}
let icon;
if (appInfo) {
icon = appInfo.get_icon();
title = appInfo.get_name();
} else {
let iconName = keyfile.get_string(group, 'Icon');
icon = new Gio.ThemedIcon({ name: iconName });
title = keyfile.get_locale_string(group, 'Title', null);
}
title = keyfile.get_locale_string(group, 'Title', null);
remoteProvider = new RemoteSearchProvider(title,
icon,
busName,
objectPath);
loadedProviders[objectPath] = remoteProvider;
} catch(e) {
log('Failed to add search provider "%s": %s'.format(title, e.toString()));
continue;
@ -111,13 +91,15 @@ const RemoteSearchProvider = new Lang.Class({
dbusName, dbusPath);
this.parent(title.toUpperCase());
this.async = true;
this._cancellable = new Gio.Cancellable();
},
createIcon: function(size, meta) {
if (meta['gicon']) {
return new St.Icon({ gicon: Gio.icon_new_for_string(meta['gicon']),
icon_size: size });
icon_size: size,
icon_type: St.IconType.FULLCOLOR });
} else if (meta['icon-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = meta['icon-data'];
@ -128,7 +110,8 @@ const RemoteSearchProvider = new Lang.Class({
// Ugh, but we want to fall back to something ...
return new St.Icon({ icon_name: 'text-x-generic',
icon_size: size });
icon_size: size,
icon_type: St.IconType.FULLCOLOR });
},
_getResultsFinished: function(results, error) {
@ -137,7 +120,7 @@ const RemoteSearchProvider = new Lang.Class({
this.searchSystem.pushResults(this, results[0]);
},
getInitialResultSet: function(terms) {
getInitialResultSetAsync: function(terms) {
this._cancellable.cancel();
this._cancellable.reset();
try {
@ -150,7 +133,7 @@ const RemoteSearchProvider = new Lang.Class({
}
},
getSubsearchResultSet: function(previousResults, newTerms) {
getSubsearchResultSetAsync: function(previousResults, newTerms) {
this._cancellable.cancel();
this._cancellable.reset();
try {
@ -181,7 +164,7 @@ const RemoteSearchProvider = new Lang.Class({
callback(resultMetas);
},
getResultMetas: function(ids, callback) {
getResultMetasAsync: function(ids, callback) {
this._cancellable.cancel();
this._cancellable.reset();
try {

View File

@ -219,9 +219,7 @@ const RunDialog = new Lang.Class({
this.contentLayout.add(this._errorBox, { expand: true });
let errorIcon = new St.Icon({ icon_name: 'dialog-error-symbolic',
icon_size: 24,
style_class: 'run-dialog-error-icon' });
let errorIcon = new St.Icon({ icon_name: 'dialog-error', icon_size: 24, style_class: 'run-dialog-error-icon' });
this._errorBox.add(errorIcon, { y_align: St.Align.MIDDLE });

View File

@ -1,973 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Cairo = imports.cairo;
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const TweenerEquations = imports.tweener.equations;
const GnomeSession = imports.misc.gnomeSession;
const Layout = imports.ui.layout;
const LoginManager = imports.misc.loginManager;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const MessageTray = imports.ui.messageTray;
const ShellDBus = imports.ui.shellDBus;
const Tweener = imports.ui.tweener;
const Util = imports.misc.util;
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const LOCK_ENABLED_KEY = 'lock-enabled';
const LOCK_DELAY_KEY = 'lock-delay';
const CURTAIN_SLIDE_TIME = 0.3;
// fraction of screen height the arrow must reach before completing
// the slide up automatically
const ARROW_DRAG_THRESHOLD = 0.1;
// Parameters for the arrow animation
const N_ARROWS = 3;
const ARROW_ANIMATION_TIME = 0.6;
const ARROW_ANIMATION_PEAK_OPACITY = 0.4;
// The distance in px that the lock screen will move to when pressing
// a key that has no effect in the lock screen (bumping it)
const BUMP_SIZE = 25;
const BUMP_TIME = 0.3;
const SUMMARY_ICON_SIZE = 48;
// Lightbox fading times
// STANDARD_FADE_TIME is used when the session goes idle, while
// SHORT_FADE_TIME is used when requesting lock explicitly from the user menu
const STANDARD_FADE_TIME = 10;
const SHORT_FADE_TIME = 0.3;
const Clock = new Lang.Class({
Name: 'ScreenShieldClock',
CLOCK_FORMAT_KEY: 'clock-format',
CLOCK_SHOW_SECONDS_KEY: 'clock-show-seconds',
_init: function() {
this.actor = new St.BoxLayout({ style_class: 'screen-shield-clock',
vertical: true });
this._time = new St.Label({ style_class: 'screen-shield-clock-time' });
this._date = new St.Label({ style_class: 'screen-shield-clock-date' });
this.actor.add(this._time, { x_align: St.Align.MIDDLE });
this.actor.add(this._date, { x_align: St.Align.MIDDLE });
this._wallClock = new GnomeDesktop.WallClock({ time_only: true });
this._wallClock.connect('notify::clock', Lang.bind(this, this._updateClock));
this._updateClock();
},
_updateClock: function() {
this._time.text = this._wallClock.clock;
let date = new Date();
/* Translators: This is a time format for a date in
long format */
this._date.text = date.toLocaleFormat(_("%A, %B %d"));
},
destroy: function() {
this.actor.destroy();
this._wallClock.run_dispose();
}
});
const NotificationsBox = new Lang.Class({
Name: 'NotificationsBox',
_init: function() {
this.actor = new St.BoxLayout({ vertical: true,
name: 'screenShieldNotifications',
style_class: 'screen-shield-notifications-box' });
this._residentNotificationBox = new St.BoxLayout({ vertical: true,
style_class: 'screen-shield-notifications-box' });
let scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START });
this._persistentNotificationBox = new St.BoxLayout({ vertical: true,
style_class: 'screen-shield-notifications-box' });
scrollView.add_actor(this._persistentNotificationBox);
this.actor.add(this._residentNotificationBox, { x_fill: true });
this.actor.add(scrollView, { x_fill: true, x_align: St.Align.START });
this._items = [];
Main.messageTray.getSummaryItems().forEach(Lang.bind(this, function(item) {
this._summaryItemAdded(Main.messageTray, item, true);
}));
this._updateVisibility();
this._summaryAddedId = Main.messageTray.connect('summary-item-added', Lang.bind(this, this._summaryItemAdded));
},
destroy: function() {
if (this._summaryAddedId) {
Main.messageTray.disconnect(this._summaryAddedId);
this._summaryAddedId = 0;
}
for (let i = 0; i < this._items.length; i++)
this._removeItem(this._items[i]);
this._items = [];
this.actor.destroy();
},
_updateVisibility: function() {
this._residentNotificationBox.visible = this._residentNotificationBox.get_n_children() > 0;
this._persistentNotificationBox.visible = this._persistentNotificationBox.get_children().some(function(a) {
return a.visible;
});
this.actor.visible = this._residentNotificationBox.visible || this._persistentNotificationBox.visible;
},
_sourceIsResident: function(source) {
return source.hasResidentNotification() && !source.isChat;
},
_makeNotificationCountText: function(count, isChat) {
if (isChat)
return ngettext("%d new message", "%d new messages", count).format(count);
else
return ngettext("%d new notification", "%d new notifications", count).format(count);
},
_makeNotificationSource: function(source) {
let box = new St.BoxLayout({ style_class: 'screen-shield-notification-source' });
let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
box.add(sourceActor.actor, { y_fill: true });
let textBox = new St.BoxLayout({ vertical: true });
box.add(textBox, { y_fill: false, y_align: St.Align.START });
let label = new St.Label({ text: source.title,
style_class: 'screen-shield-notification-label' });
textBox.add(label);
let count = source.unseenCount;
let countLabel = new St.Label({ text: this._makeNotificationCountText(count, source.isChat),
style_class: 'screen-shield-notification-count-text' });
textBox.add(countLabel);
box.visible = count != 0;
return [box, countLabel];
},
_summaryItemAdded: function(tray, item, dontUpdateVisibility) {
// Ignore transient sources, or sources explicitly marked not to show
// in the lock screen
if (item.source.isTransient || !item.source.showInLockScreen)
return;
let obj = {
item: item,
source: item.source,
resident: this._sourceIsResident(item.source),
contentUpdatedId: 0,
sourceDestroyId: 0,
sourceBox: null,
countLabel: null,
};
if (obj.resident) {
this._residentNotificationBox.add(item.notificationStackWidget);
item.closeButton.hide();
item.prepareNotificationStackForShowing();
} else {
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(item.source);
this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
}
obj.contentUpdatedId = item.connect('content-updated', Lang.bind(this, this._onItemContentUpdated));
obj.sourceCountChangedId = item.source.connect('count-updated', Lang.bind(this, this._onSourceChanged));
obj.sourceTitleChangedId = item.source.connect('title-changed', Lang.bind(this, this._onSourceChanged));
obj.sourceDestroyId = item.source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
this._items.push(obj);
if (!dontUpdateVisibility)
this._updateVisibility();
},
_findSource: function(source) {
for (let i = 0; i < this._items.length; i++) {
if (this._items[i].source == source)
return i;
}
return -1;
},
_onItemContentUpdated: function(item) {
let obj = this._items[this._findSource(item.source)];
this._updateItem(obj);
},
_onSourceChanged: function(source) {
let obj = this._items[this._findSource(source)];
this._updateItem(obj);
},
_updateItem: function(obj) {
let itemShouldBeResident = this._sourceIsResident(obj.source);
if (itemShouldBeResident && obj.resident) {
// Nothing to do here, the actor is already updated
return;
}
if (obj.resident && !itemShouldBeResident) {
// make into a regular item
obj.item.doneShowingNotificationStack();
this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget);
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(obj.source);
this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
} else if (itemShouldBeResident && !obj.resident) {
// make into a resident item
obj.sourceBox.destroy();
obj.sourceBox = obj.countLabel = null;
obj.resident = true;
this._residentNotificationBox.add(obj.item.notificationStackWidget);
obj.item.closeButton.hide();
obj.item.prepareNotificationStackForShowing();
} else {
// just update the counter
let count = obj.source.unseenCount;
obj.countLabel.text = this._makeNotificationCountText(count, obj.source.isChat);
obj.sourceBox.visible = count != 0;
}
this._updateVisibility();
},
_onSourceDestroy: function(source) {
let idx = this._findSource(source);
this._removeItem(this._items[idx]);
this._items.splice(idx, 1);
this._updateVisibility();
},
_removeItem: function(obj) {
if (obj.resident) {
obj.item.doneShowingNotificationStack();
this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget);
} else {
obj.sourceBox.destroy();
}
obj.item.disconnect(obj.contentUpdatedId);
obj.source.disconnect(obj.sourceDestroyId);
obj.source.disconnect(obj.sourceCountChangedId);
},
});
const Arrow = new Lang.Class({
Name: 'Arrow',
Extends: St.Bin,
_init: function(params) {
this.parent(params);
this.x_fill = this.y_fill = true;
this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
this._drawingArea = new St.DrawingArea();
this._drawingArea.connect('repaint', Lang.bind(this, this._drawArrow));
this.child = this._drawingArea;
this._shadowHelper = null;
this._shadowWidth = this._shadowHeight = 0;
},
_drawArrow: function(arrow) {
let cr = arrow.get_context();
let [w, h] = arrow.get_surface_size();
let node = this.get_theme_node();
let thickness = node.get_length('-arrow-thickness');
Clutter.cairo_set_source_color(cr, node.get_foreground_color());
cr.setLineCap(Cairo.LineCap.ROUND);
cr.setLineWidth(thickness);
cr.moveTo(thickness / 2, h - thickness / 2);
cr.lineTo(w/2, thickness);
cr.lineTo(w - thickness / 2, h - thickness / 2);
cr.stroke();
},
vfunc_style_changed: function() {
let node = this.get_theme_node();
this._shadow = node.get_shadow('-arrow-shadow');
if (this._shadow)
this._shadowHelper = St.ShadowHelper.new(this._shadow);
else
this._shadowHelper = null;
},
vfunc_paint: function() {
if (this._shadowHelper) {
this._shadowHelper.update(this._drawingArea);
let allocation = this._drawingArea.get_allocation_box();
let paintOpacity = this._drawingArea.get_paint_opacity();
this._shadowHelper.paint(allocation, paintOpacity);
}
this._drawingArea.paint();
}
});
/**
* To test screen shield, make sure to kill gnome-screensaver.
*
* If you are setting org.gnome.desktop.session.idle-delay directly in dconf,
* rather than through System Settings, you also need to set
* org.gnome.settings-daemon.plugins.power.sleep-display-ac and
* org.gnome.settings-daemon.plugins.power.sleep-display-battery to the same value.
* This will ensure that the screen blanks at the right time when it fades out.
* https://bugzilla.gnome.org/show_bug.cgi?id=668703 explains the dependance.
*/
const ScreenShield = new Lang.Class({
Name: 'ScreenShield',
_init: function() {
this.actor = Main.layoutManager.screenShieldGroup;
this._lockScreenState = MessageTray.State.HIDDEN;
this._lockScreenGroup = new St.Widget({ x_expand: true,
y_expand: true,
reactive: true,
can_focus: true,
name: 'lockScreenGroup',
});
this._lockScreenGroup.connect('key-release-event',
Lang.bind(this, this._onLockScreenKeyRelease));
this._lockScreenGroup.connect('scroll-event',
Lang.bind(this, this._onLockScreenScroll));
this._lockScreenContents = new St.Widget({ layout_manager: new Clutter.BinLayout(),
name: 'lockScreenContents' });
this._lockScreenContents.add_constraint(new Layout.MonitorConstraint({ primary: true }));
this._background = new St.Bin({ style_class: 'screen-shield-background',
child: Meta.BackgroundActor.new_for_screen(global.screen) });
this._lockScreenGroup.add_actor(this._background);
this._lockScreenGroup.add_actor(this._lockScreenContents);
this._arrowContainer = new St.BoxLayout({ style_class: 'screen-shield-arrows',
vertical: true,
x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.END,
// HACK: without these, ClutterBinLayout
// ignores alignment properties on the actor
x_expand: true,
y_expand: true });
for (let i = 0; i < N_ARROWS; i++) {
let arrow = new Arrow({ opacity: 0 });
this._arrowContainer.add_actor(arrow);
}
this._lockScreenContents.add_actor(this._arrowContainer);
let dragArea = new Clutter.Rect({ origin: new Clutter.Point({ x: 0, y: -global.screen_height, }),
size: new Clutter.Size({ width: global.screen_width,
height: global.screen_height }) });
let action = new Clutter.DragAction({ drag_axis: Clutter.DragAxis.Y_AXIS,
drag_area: dragArea });
action.connect('drag-begin', Lang.bind(this, this._onDragBegin));
action.connect('drag-end', Lang.bind(this, this._onDragEnd));
this._lockScreenGroup.add_action(action);
this._lockDialogGroup = new St.Widget({ x_expand: true,
y_expand: true,
pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
name: 'lockDialogGroup' });
this.actor.add_actor(this._lockDialogGroup);
this.actor.add_actor(this._lockScreenGroup);
this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) {
if (error) {
logError(error, 'Error while reading gnome-session presence');
return;
}
this._onStatusChanged(proxy.status);
}));
this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
this._onStatusChanged(status);
}));
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
this._loginManager = LoginManager.getLoginManager();
this._loginSession = this._loginManager.getCurrentSessionProxy();
this._loginSession.connectSignal('Lock', Lang.bind(this, function() { this.lock(false); }));
this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.unlock(); }));
this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
this._isModal = false;
this._hasLockScreen = false;
this._isGreeter = false;
this._isActive = false;
this._inUnlockAnimation = false;
this._activationTime = 0;
this._lightbox = new Lightbox.Lightbox(Main.uiGroup,
{ inhibitEvents: true,
fadeInTime: STANDARD_FADE_TIME,
fadeFactor: 1 });
this.idleMonitor = Shell.IdleMonitor.get();
},
_becomeModal: function() {
if (this._isModal)
return true;
this._isModal = Main.pushModal(this.actor);
if (this._isModal)
return true;
// We failed to get a pointer grab, it means that
// something else has it. Try with a keyboard grab only
this._isModal = Main.pushModal(this.actor, { options: Meta.ModalOptions.POINTER_ALREADY_GRABBED });
return this._isModal;
},
_onLockScreenKeyRelease: function(actor, event) {
let symbol = event.get_key_symbol();
// Do nothing if the lock screen is not fully shown.
// This avoids reusing the previous (and stale) unlock
// dialog if esc is pressed while the curtain is going
// down after cancel.
// Similarly, don't bump if the lock screen is not showing or is
// animating, as the bump overrides the animation and would
// remove any onComplete handler.
if (this._lockScreenState != MessageTray.State.SHOWN)
return false;
if (symbol == Clutter.KEY_Escape ||
symbol == Clutter.KEY_Return ||
symbol == Clutter.KEY_KP_Enter) {
this._ensureUnlockDialog(true, true);
this._hideLockScreen(true);
return true;
}
this._bumpLockScreen();
return true;
},
_onLockScreenScroll: function(actor, event) {
if (this._lockScreenState != MessageTray.State.SHOWN)
return false;
let delta = 0;
if (event.get_scroll_direction() == Clutter.ScrollDirection.UP)
delta = 5;
else if (event.get_scroll_direction() == Clutter.ScrollDirection.SMOOTH)
delta = Math.max(0, event.get_scroll_delta()[0]);
this._lockScreenScrollCounter += delta;
// 7 standard scrolls to lift up
if (this._lockScreenScrollCounter > 35) {
this._ensureUnlockDialog(false, true);
this._hideLockScreen(true);
}
return true;
},
_animateArrows: function() {
let arrows = this._arrowContainer.get_children();
let unitaryDelay = ARROW_ANIMATION_TIME / (arrows.length + 1);
let maxOpacity = 255 * ARROW_ANIMATION_PEAK_OPACITY;
for (let i = 0; i < arrows.length; i++) {
arrows.opacity = 0;
Tweener.addTween(arrows[i],
{ opacity: 0,
delay: unitaryDelay * (N_ARROWS - (i + 1)),
time: ARROW_ANIMATION_TIME,
transition: function(t, b, c, d) {
if (t < d/2)
return TweenerEquations.easeOutQuad(t, 0, maxOpacity, d/2);
else
return TweenerEquations.easeInQuad(t - d/2, maxOpacity, -maxOpacity, d/2);
}
});
}
return true;
},
_onDragBegin: function() {
Tweener.removeTweens(this._lockScreenGroup);
this._lockScreenState = MessageTray.State.HIDING;
this._ensureUnlockDialog(false, false);
},
_onDragEnd: function(action, actor, eventX, eventY, modifiers) {
if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) {
// Complete motion automatically
this._hideLockScreen(true);
this._ensureUnlockDialog(false, true);
} else {
// restore the lock screen to its original place
// try to use the same speed as the normal animation
let h = global.stage.height;
let time = CURTAIN_SLIDE_TIME * (-this._lockScreenGroup.y) / h;
Tweener.removeTweens(this._lockScreenGroup);
Tweener.addTween(this._lockScreenGroup,
{ y: 0,
time: time,
transition: 'easeInQuad',
onComplete: function() {
this._lockScreenGroup.fixed_position_set = false;
this._lockScreenState = MessageTray.State.SHOWN;
},
onCompleteScope: this,
});
// If we have a unlock dialog, cancel it
if (this._dialog) {
this._dialog.cancel();
if (!this._isGreeter) {
this._dialog = null;
}
}
}
},
_onStatusChanged: function(status) {
if (status != GnomeSession.PresenceStatus.IDLE)
return;
if (this._dialog) {
this._dialog.cancel();
if (!this._isGreeter) {
this._dialog = null;
}
}
if (!this._becomeModal()) {
// We could not become modal, so we can't activate the
// screenshield. The user is probably very upset at this
// point, but any application using global grabs is broken
// Just tell him to stop using this app
//
// XXX: another option is to kick the user into the gdm login
// screen, where we're not affected by grabs
Main.notifyError("Unable to lock",
"Lock was blocked by an application");
return;
}
if (!this._isActive) {
this._lightbox.show();
if (this._activationTime == 0)
this._activationTime = GLib.get_monotonic_time();
// What we want is a negative transition to 0, so we install a watch for
// 1 second and trigger it if the idle_time becomes lower than that
// The correct fix will come in GNOME 3.8 with GnomeDesktop.IdleMonitor
this._becameActiveId = this.idleMonitor.add_watch(1000, Lang.bind(this, function() {
if (this.idleMonitor.get_idletime() >= 1000)
return;
this.idleMonitor.remove_watch(this._becameActiveId);
let lightboxWasShown = this._lightbox.shown;
this._lightbox.hide();
// GLib.get_monotonic_time() returns microseconds, convert to seconds
let elapsedTime = (GLib.get_monotonic_time() - this._activationTime) / 1000000;
let shouldLock = lightboxWasShown &&
this._settings.get_boolean(LOCK_ENABLED_KEY) &&
(elapsedTime >= this._settings.get_uint(LOCK_DELAY_KEY));
if (shouldLock || this._isLocked) {
this.lock(false);
} else if (this._isActive) {
this.unlock();
}
}));
this._isActive = true;
this.emit('lock-status-changed');
}
},
showDialog: function() {
// Ensure that the stage window is mapped, before taking a grab
// otherwise X errors out
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
if (!this._becomeModal()) {
// In the login screen, this is a hard error. Fail-whale
log('Could not acquire modal grab for the login screen. Aborting login process.');
Meta.quit(Meta.ExitCode.ERROR);
}
return false;
}));
this.actor.show();
this._isGreeter = Main.sessionMode.isGreeter;
this._ensureUnlockDialog(true, true);
this._hideLockScreen(false);
},
_bumpLockScreen: function() {
Tweener.removeTweens(this._lockScreenGroup);
Tweener.addTween(this._lockScreenGroup,
{ y: -BUMP_SIZE,
time: BUMP_TIME / 2,
transition: 'easeOutQuad',
onComplete: function() {
Tweener.addTween(this,
{ y: 0,
time: BUMP_TIME / 2,
transition: 'easeInQuad' });
}
});
},
_hideLockScreen: function(animate) {
this._lockScreenState = MessageTray.State.HIDING;
if (animate) {
// Tween the lock screen out of screen
// try to use the same speed regardless of original position
let h = global.stage.height;
let time = CURTAIN_SLIDE_TIME * (h + this._lockScreenGroup.y) / h;
Tweener.removeTweens(this._lockScreenGroup);
Tweener.addTween(this._lockScreenGroup,
{ y: -h,
time: time,
transition: 'easeInQuad',
onComplete: function() {
this._lockScreenState = MessageTray.State.HIDDEN;
this._lockScreenGroup.hide();
},
onCompleteScope: this,
});
} else {
this._lockScreenState = MessageTray.State.HIDDEN;
this._lockScreenGroup.hide();
}
if (Main.sessionMode.currentMode == 'lock-screen')
Main.sessionMode.popMode('lock-screen');
},
_ensureUnlockDialog: function(onPrimary, allowCancel) {
if (!this._dialog) {
let constructor = Main.sessionMode.unlockDialog;
if (!constructor) {
// This session mode has no locking capabilities
this.unlock();
return;
}
this._dialog = new constructor(this._lockDialogGroup);
let time = global.get_current_time();
this._dialog.connect('loaded', Lang.bind(this, function() {
if (!this._dialog.open(time, onPrimary)) {
log('Could not open login dialog: failed to acquire grab');
this.unlock();
}
}));
this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
}
this._dialog.allowCancel = allowCancel;
},
_onUnlockFailed: function() {
this._resetLockScreen(true, false);
},
_onUnlockSucceded: function() {
this._tweenUnlocked();
},
_resetLockScreen: function(animateLockScreen, animateLockDialog) {
this._ensureLockScreen();
this._lockDialogGroup.scale_x = 1;
this._lockDialogGroup.scale_y = 1;
this._lockScreenGroup.show();
this._lockScreenState = MessageTray.State.SHOWING;
if (animateLockScreen) {
this._lockScreenGroup.y = -global.screen_height;
Tweener.removeTweens(this._lockScreenGroup);
Tweener.addTween(this._lockScreenGroup,
{ y: 0,
time: SHORT_FADE_TIME,
transition: 'easeOutQuad',
onComplete: function() {
this._lockScreenShown();
},
onCompleteScope: this
});
} else {
this._lockScreenGroup.fixed_position_set = false;
this._lockScreenShown();
}
if (animateLockDialog) {
this._lockDialogGroup.opacity = 0;
Tweener.removeTweens(this._lockDialogGroup);
Tweener.addTween(this._lockDialogGroup,
{ opacity: 255,
time: SHORT_FADE_TIME,
transition: 'easeOutQuad' });
} else {
this._lockDialogGroup.opacity = 255;
}
this._lockScreenGroup.grab_key_focus();
if (Main.sessionMode.currentMode != 'lock-screen')
Main.sessionMode.pushMode('lock-screen');
},
_lockScreenShown: function() {
if (this._dialog && !this._isGreeter) {
this._dialog.destroy();
this._dialog = null;
}
if (this._arrowAnimationId)
Mainloop.source_remove(this._arrowAnimationId);
this._arrowAnimationId = Mainloop.timeout_add(6000, Lang.bind(this, this._animateArrows));
this._animateArrows();
this._lockScreenState = MessageTray.State.SHOWN;
this._lockScreenGroup.fixed_position_set = false;
this._lockScreenScrollCounter = 0;
this.emit('lock-screen-shown');
},
// Some of the actors in the lock screen are heavy in
// resources, so we only create them when needed
_ensureLockScreen: function() {
if (this._hasLockScreen)
return;
this._lockScreenContentsBox = new St.BoxLayout({ x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER,
x_expand: true,
y_expand: true,
vertical: true,
style_class: 'screen-shield-contents-box' });
this._clock = new Clock();
this._lockScreenContentsBox.add(this._clock.actor, { x_fill: true,
y_fill: true });
this._lockScreenContents.add_actor(this._lockScreenContentsBox);
if (this._settings.get_boolean('show-notifications')) {
this._notificationsBox = new NotificationsBox();
this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
y_fill: true,
expand: true });
}
this._hasLockScreen = true;
},
_clearLockScreen: function() {
this._clock.destroy();
this._clock = null;
if (this._notificationsBox) {
this._notificationsBox.destroy();
this._notificationsBox = null;
}
this._lockScreenContentsBox.destroy();
if (this._arrowAnimationId) {
Mainloop.source_remove(this._arrowAnimationId);
this._arrowAnimationId = 0;
}
this._hasLockScreen = false;
},
get locked() {
return this._isActive;
},
get activationTime() {
return this._activationTime;
},
_tweenUnlocked: function() {
this._inUnlockAnimation = true;
this.unlock();
Tweener.addTween(this._lockDialogGroup, {
scale_x: 0,
scale_y: 0,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
if (this._dialog) {
this._dialog.destroy();
this._dialog = null;
}
this.actor.hide();
this._inUnlockAnimation = false;
},
onCompleteScope: this
});
},
unlock: function() {
if (this._hasLockScreen)
this._clearLockScreen();
if (this._dialog && !this._isGreeter) {
this._dialog.destroy();
this._dialog = null;
}
this._lightbox.hide();
if (this._isModal) {
Main.popModal(this.actor);
this._isModal = false;
}
if (!this._inUnlockAnimation)
this.actor.hide();
if (Main.sessionMode.currentMode == 'lock-screen')
Main.sessionMode.popMode('lock-screen');
if (Main.sessionMode.currentMode == 'unlock-dialog')
Main.sessionMode.popMode('unlock-dialog');
this._activationTime = 0;
this._isActive = false;
this._isLocked = false;
this.emit('lock-status-changed');
},
lock: function(animate) {
// Warn the user if we can't become modal
if (!this._becomeModal()) {
Main.notifyError("Unable to lock",
"Lock was blocked by an application");
return;
}
if (this._activationTime == 0)
this._activationTime = GLib.get_monotonic_time();
this.actor.show();
if (Main.sessionMode.currentMode != 'unlock-dialog' &&
Main.sessionMode.currentMode != 'lock-screen') {
this._isGreeter = Main.sessionMode.isGreeter;
if (!this._isGreeter)
Main.sessionMode.pushMode('unlock-dialog');
}
this._resetLockScreen(animate, animate);
this._isActive = true;
this._isLocked = true;
this.emit('lock-status-changed');
},
});
Signals.addSignalMethods(ScreenShield.prototype);
/* Fallback code to handle session locking using gnome-screensaver,
in case the required GDM dependency is not there
*/
const ScreenShieldFallback = new Lang.Class({
Name: 'ScreenShieldFallback',
_init: function() {
Util.spawn(['gnome-screensaver']);
this._proxy = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
g_name: 'org.gnome.ScreenSaver',
g_object_path: '/org/gnome/ScreenSaver',
g_interface_name: 'org.gnome.ScreenSaver',
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES),
});
this._proxy.init(null);
this._proxy.connect('g-signal', Lang.bind(this, this._onSignal));
this._proxy.connect('notify::g-name-owner', Lang.bind(this, this._onNameOwnerChanged));
},
_onNameOwnerChanged: function(object, pspec) {
if (this._proxy.g_name_owner)
[this._locked] = this._proxy.call_sync('GetActive', null,
Gio.DBusCallFlags.NONE, -1, null).deep_unpack();
else
this._locked = false;
this.emit('lock-status-changed', this._locked);
},
_onSignal: function(proxy, senderName, signalName, params) {
if (signalName == 'ActiveChanged') {
[this._locked] = params.deep_unpack();
this.emit('lock-status-changed', this._locked);
}
},
get locked() {
return this._locked;
},
lock: function() {
this._proxy.call('Lock', null, Gio.DBusCallFlags.NONE, -1, null,
Lang.bind(this, function(proxy, result) {
proxy.call_finish(result);
this.emit('lock-screen-shown');
}));
},
unlock: function() {
this._proxy.call('SetActive', GLib.Variant.new('(b)', false),
Gio.DBusCallFlags.NONE, -1, null, null);
},
});
Signals.addSignalMethods(ScreenShieldFallback.prototype);

View File

@ -10,13 +10,17 @@ const Util = imports.misc.util;
const FileUtils = imports.misc.fileUtils;
const Main = imports.ui.main;
const DISABLED_OPEN_SEARCH_PROVIDERS_KEY = 'disabled-open-search-providers';
// Not currently referenced by the search API, but
// this enumeration can be useful for provider
// implementations.
const MatchType = {
NONE: 0,
SUBSTRING: 1,
PREFIX: 2
MULTIPLE_SUBSTRING: 2,
PREFIX: 3,
MULTIPLE_PREFIX: 4
};
const SearchResultDisplay = new Lang.Class({
@ -49,7 +53,7 @@ const SearchResultDisplay = new Lang.Class({
* Remove all results from this display.
*/
clear: function() {
this.actor.destroy_all_children();
this.actor.get_children().forEach(function (actor) { actor.destroy(); });
},
/**
@ -68,8 +72,11 @@ const SearchResultDisplay = new Lang.Class({
* Subclass this object to add a new result type
* to the search system, then call registerProvider()
* in SearchSystem with an instance.
* Search is asynchronous and uses the
* By default, search is synchronous and uses the
* getInitialResultSet()/getSubsearchResultSet() methods.
* For asynchronous search, set the async property to true
* and implement getInitialResultSetAsync()/getSubsearchResultSetAsync()
* instead.
*/
const SearchProvider = new Lang.Class({
Name: 'SearchProvider',
@ -77,6 +84,7 @@ const SearchProvider = new Lang.Class({
_init: function(title) {
this.title = title;
this.searchSystem = null;
this.async = false;
},
/**
@ -87,7 +95,7 @@ const SearchProvider = new Lang.Class({
* therefore a single term of length one or two), or when
* a new term is added.
*
* Should "return" an array of result identifier strings representing
* Should return an array of result identifier strings representing
* items which match the given search terms. This
* is expected to be a substring match on the metadata for a given
* item. Ordering of returned results is up to the discretion of the provider,
@ -97,9 +105,6 @@ const SearchProvider = new Lang.Class({
* description) before single matches
* * Put items which match on a prefix before non-prefix substring matches
*
* We say "return" above, but in order to make the query asynchronous, use
* this.searchSystem.pushResults();. The return value should be ignored.
*
* This function should be fast; do not perform unindexed full-text searches
* or network queries.
*/
@ -107,6 +112,18 @@ const SearchProvider = new Lang.Class({
throw new Error('Not implemented');
},
/**
* getInitialResultSetAsync:
* @terms: Array of search terms, treated as logical AND
*
* Like getInitialResultSet(), but the method should return immediately
* without a return value - use SearchSystem.pushResults() when the
* corresponding results are ready.
*/
getInitialResultSetAsync: function(terms) {
throw new Error('Not implemented');
},
/**
* getSubsearchResultSet:
* @previousResults: Array of item identifiers
@ -119,26 +136,62 @@ const SearchProvider = new Lang.Class({
*
* This allows search providers to only search through the previous
* result set, rather than possibly performing a full re-query.
*
* Similar to getInitialResultSet, the return value for this will
* be ignored; use this.searchSystem.pushResults();.
*/
getSubsearchResultSet: function(previousResults, newTerms) {
throw new Error('Not implemented');
},
/**
* getSubsearchResultSetAsync:
* @previousResults: Array of item identifiers
* @newTerms: Updated search terms
*
* Like getSubsearchResultSet(), but the method should return immediately
* without a return value - use SearchSystem.pushResults() when the
* corresponding results are ready.
*/
getSubsearchResultSetAsync: function(previousResults, newTerms) {
throw new Error('Not implemented');
},
/**
* getResultMetas:
* @ids: Result identifier strings
*
* Call callback with array of objects with 'id', 'name', (both strings) and
* Return an array of objects with 'id', 'name', (both strings) and
* 'createIcon' (function(size) returning a Clutter.Texture) properties
* with the same number of members as @ids
*/
getResultMetas: function(ids, callback) {
getResultMetas: function(ids) {
throw new Error('Not implemented');
},
/**
* getResultMetasAsync:
* @ids: Result identifier strings
* @callback: callback to pass the results to when ready
*
* Like getResultMetas(), but the method should return immediately
* without a return value - pass the results to the provided @callback
* when ready.
*/
getResultMetasAsync: function(ids, callback) {
throw new Error('Not implemented');
},
/**
* createResultContainer:
*
* Search providers may optionally override this to render their
* results in a custom fashion. The default implementation
* will create a vertical list.
*
* Returns: An instance of SearchResultDisplay.
*/
createResultContainerActor: function() {
return null;
},
/**
* createResultActor:
* @resultMeta: Object with result metadata
@ -167,6 +220,99 @@ const SearchProvider = new Lang.Class({
});
Signals.addSignalMethods(SearchProvider.prototype);
const OpenSearchSystem = new Lang.Class({
Name: 'OpenSearchSystem',
_init: function() {
this._providers = [];
global.settings.connect('changed::' + DISABLED_OPEN_SEARCH_PROVIDERS_KEY, Lang.bind(this, this._refresh));
this._refresh();
},
getProviders: function() {
let res = [];
for (let i = 0; i < this._providers.length; i++)
res.push({ id: i, name: this._providers[i].name });
return res;
},
setSearchTerms: function(terms) {
this._terms = terms;
},
_checkSupportedProviderLanguage: function(provider) {
if (provider.url.search(/{language}/) == -1)
return true;
let langs = GLib.get_language_names();
langs.push('en');
let lang = null;
for (let i = 0; i < langs.length; i++) {
for (let k = 0; k < provider.langs.length; k++) {
if (langs[i] == provider.langs[k])
lang = langs[i];
}
if (lang)
break;
}
provider.lang = lang;
return lang != null;
},
activateResult: function(id, params) {
let searchTerms = this._terms.join(' ');
let url = this._providers[id].url.replace('{searchTerms}', encodeURIComponent(searchTerms));
if (url.match('{language}'))
url = url.replace('{language}', this._providers[id].lang);
try {
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context());
} catch (e) {
// TODO: remove this after glib will be removed from moduleset
// In the default jhbuild, gio is in our prefix but gvfs is not
Util.spawn(['gvfs-open', url])
}
Main.overview.hide();
},
_addProvider: function(fileName) {
let path = global.datadir + '/open-search-providers/' + fileName;
let source = Shell.get_file_contents_utf8_sync(path);
let [success, name, url, langs, icon_uri] = Shell.parse_search_provider(source);
let provider ={ name: name,
url: url,
id: this._providers.length,
icon_uri: icon_uri,
langs: langs };
if (this._checkSupportedProviderLanguage(provider)) {
this._providers.push(provider);
this.emit('changed');
}
},
_refresh: function() {
this._providers = [];
let names = global.settings.get_strv(DISABLED_OPEN_SEARCH_PROVIDERS_KEY);
let file = Gio.file_new_for_path(global.datadir + '/open-search-providers');
FileUtils.listDirAsync(file, Lang.bind(this, function(files) {
for (let i = 0; i < files.length; i++) {
let enabled = true;
let name = files[i].get_name();
for (let k = 0; k < names.length; k++)
if (names[k] == name)
enabled = false;
if (enabled)
this._addProvider(name);
}
}));
}
});
Signals.addSignalMethods(OpenSearchSystem.prototype);
const SearchSystem = new Lang.Class({
Name: 'SearchSystem',
@ -233,33 +379,42 @@ const SearchSystem = new Lang.Class({
}
}
let previousResultsArr = this._previousResults;
let results = [];
this._previousTerms = terms;
this._previousResults = results;
if (isSubSearch) {
for (let i = 0; i < this._providers.length; i++) {
let [provider, previousResults] = previousResultsArr[i];
let [provider, previousResults] = this._previousResults[i];
try {
results.push([provider, []]);
provider.getSubsearchResultSet(previousResults, terms);
if (provider.async) {
provider.getSubsearchResultSetAsync(previousResults, terms);
results.push([provider, []]);
} else {
let providerResults = provider.getSubsearchResultSet(previousResults, terms);
results.push([provider, providerResults]);
}
} catch (error) {
log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
}
}
} else {
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
try {
results.push([provider, []]);
provider.getInitialResultSet(terms);
if (provider.async) {
provider.getInitialResultSetAsync(terms);
results.push([provider, []]);
} else {
let providerResults = provider.getInitialResultSet(terms);
results.push([provider, providerResults]);
}
} catch (error) {
log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
}
}
}
this._previousTerms = terms;
this._previousResults = results;
this.emit('search-completed', results);
},
});
Signals.addSignalMethods(SearchSystem.prototype);

View File

@ -5,7 +5,6 @@ const Lang = imports.lang;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const St = imports.gi.St;
const Atk = imports.gi.Atk;
const DND = imports.ui.dnd;
const IconGrid = imports.ui.iconGrid;
@ -34,13 +33,12 @@ const SearchResult = new Lang.Class({
content = new St.Bin({ style_class: 'search-result-content',
reactive: true,
can_focus: true,
track_hover: true,
accessible_role: Atk.Role.PUSH_BUTTON });
track_hover: true });
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
{ createIcon: this.metaInfo['createIcon'] });
content.set_child(icon.actor);
this._dragActorSource = icon.icon;
content.label_actor = icon.label;
this.actor.label_actor = icon.label;
} else {
if (content._delegate && content._delegate.getDragActorSource)
this._dragActorSource = content._delegate.getDragActorSource();
@ -121,7 +119,13 @@ const GridSearchResults = new Lang.Class({
if (results.length == 0)
return;
provider.getResultMetas(results, Lang.bind(this, this.renderResults));
if (provider.async) {
provider.getResultMetasAsync(results,
Lang.bind(this, this.renderResults));
} else {
let metas = provider.getResultMetas(results);
this.renderResults(metas);
}
}));
}));
this._notDisplayedResult = [];
@ -131,7 +135,7 @@ const GridSearchResults = new Lang.Class({
getResultsForDisplay: function() {
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
let canDisplay = this._grid.childrenInRow(this._width) * this._grid.getRowLimit()
let canDisplay = this._grid.childrenInRow(this._width) * MAX_SEARCH_RESULTS_ROWS
- alreadyVisible;
let numResults = Math.min(this._notDisplayedResult.length, canDisplay);
@ -173,9 +177,11 @@ const GridSearchResults = new Lang.Class({
const SearchResults = new Lang.Class({
Name: 'SearchResults',
_init: function(searchSystem) {
_init: function(searchSystem, openSearchSystem) {
this._searchSystem = searchSystem;
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults));
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateCurrentResults));
this._searchSystem.connect('search-completed', Lang.bind(this, this._updateResults));
this._openSearchSystem = openSearchSystem;
this.actor = new St.BoxLayout({ name: 'searchResults',
vertical: true });
@ -190,7 +196,7 @@ const SearchResults = new Lang.Class({
scrollView.add_actor(this._content);
this.actor.add(scrollView, { x_fill: true,
y_fill: true,
y_fill: false,
expand: true,
x_align: St.Align.START,
y_align: St.Align.START });
@ -205,37 +211,87 @@ const SearchResults = new Lang.Class({
}));
this._statusText = new St.Label({ style_class: 'search-statustext' });
this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE,
y_align: St.Align.MIDDLE });
this._content.add(this._statusBin, { expand: true });
this._statusBin.add_actor(this._statusText);
this._content.add(this._statusText);
this._providers = this._searchSystem.getProviders();
this._providerMeta = [];
this._providerMetaResults = {};
for (let i = 0; i < this._providers.length; i++) {
this.createProviderMeta(this._providers[i]);
this._providerMetaResults[this.providers[i].title] = [];
}
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
this.actor.add(this._searchProvidersBox);
this._openSearchProviders = [];
this._openSearchSystem.connect('changed', Lang.bind(this, this._updateOpenSearchProviderButtons));
this._updateOpenSearchProviderButtons();
this._highlightDefault = false;
this._defaultResult = null;
},
_updateOpenSearchProviderButtons: function() {
for (let i = 0; i < this._openSearchProviders.length; i++)
this._openSearchProviders[i].actor.destroy();
this._openSearchProviders = this._openSearchSystem.getProviders();
for (let i = 0; i < this._openSearchProviders.length; i++)
this._createOpenSearchProviderButton(this._openSearchProviders[i]);
},
_createOpenSearchProviderButton: function(provider) {
let button = new St.Button({ style_class: 'dash-search-button',
reactive: true,
can_focus: true,
x_fill: true,
y_align: St.Align.MIDDLE });
let bin = new St.Bin({ x_fill: false,
x_align:St.Align.MIDDLE });
button.connect('clicked', Lang.bind(this, function() {
this._openSearchSystem.activateResult(provider.id);
}));
let title = new St.Label({ text: provider.name,
style_class: 'dash-search-button-label' });
button.label_actor = title;
bin.set_child(title);
button.set_child(bin);
provider.actor = button;
button.setSelected = function(selected) {
if (selected)
button.add_style_pseudo_class('selected');
else
button.remove_style_pseudo_class('selected');
};
button.activate = Lang.bind(this, function() {
this._openSearchSystem.activateResult(provider.id);
});
button.actor = button;
this._searchProvidersBox.add(button);
},
createProviderMeta: function(provider) {
let providerBox = new St.BoxLayout({ style_class: 'search-section',
vertical: true });
let title = new St.Label({ style_class: 'search-section-header',
text: provider.title });
providerBox.add(title, { x_fill: false, x_align: St.Align.START });
providerBox.add(title);
let resultDisplayBin = new St.Bin({ style_class: 'search-section-results',
x_fill: true,
y_fill: true });
providerBox.add(resultDisplayBin, { expand: true });
let resultDisplay = new GridSearchResults(provider);
let resultDisplay = provider.createResultContainerActor();
if (resultDisplay == null) {
resultDisplay = new GridSearchResults(provider);
}
resultDisplayBin.set_child(resultDisplay.actor);
this._providerMeta.push({ provider: provider,
actor: providerBox,
resultDisplay: resultDisplay });
resultDisplay: resultDisplay,
hasPendingResults: false });
this._content.add(providerBox);
},
@ -251,6 +307,7 @@ const SearchResults = new Lang.Class({
},
_clearDisplay: function() {
this._visibleResultsCount = 0;
for (let i = 0; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i];
meta.resultDisplay.clear();
@ -266,14 +323,14 @@ const SearchResults = new Lang.Class({
reset: function() {
this._searchSystem.reset();
this._statusBin.hide();
this._statusText.hide();
this._clearDisplay();
},
startingSearch: function() {
this.reset();
this._statusText.set_text(_("Searching..."));
this._statusBin.show();
this._statusText.show();
},
doSearch: function (searchString) {
@ -289,6 +346,8 @@ const SearchResults = new Lang.Class({
for (let i = 0; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i];
if (meta.hasPendingResults)
return;
if (!meta.actor.visible)
continue;
@ -300,6 +359,9 @@ const SearchResults = new Lang.Class({
}
}
if (!newDefaultResult)
newDefaultResult = this._searchProvidersBox.get_first_child();
if (newDefaultResult != this._defaultResult) {
if (this._defaultResult)
this._defaultResult.setSelected(false);
@ -310,57 +372,78 @@ const SearchResults = new Lang.Class({
}
},
_updateStatusText: function () {
let haveResults = false;
for (let i = 0; i < this._providerMeta.length; ++i)
if (this._providerMeta[i].resultDisplay.getFirstResult()) {
haveResults = true;
break;
}
if (!haveResults) {
this._statusText.set_text(_("No results."));
this._statusBin.show();
} else {
this._statusBin.hide();
}
},
_updateResults: function(searchSystem, results) {
_updateCurrentResults: function(searchSystem, results) {
let terms = searchSystem.getTerms();
let [provider, providerResults] = results;
let meta = this._metaForProvider(provider);
meta.hasPendingResults = false;
this._updateProviderResults(provider, providerResults, terms);
},
_updateProviderResults: function(provider, providerResults, terms) {
let meta = this._metaForProvider(provider);
if (providerResults.length == 0) {
this._clearDisplayForProvider(provider);
meta.resultDisplay.setResults([], []);
this._maybeSetInitialSelection();
this._updateStatusText();
} else {
this._providerMetaResults[provider.title] = providerResults;
meta.resultDisplay.setResults(providerResults, terms);
let results = meta.resultDisplay.getResultsForDisplay();
provider.getResultMetas(results, Lang.bind(this, function(metas) {
if (provider.async) {
provider.getResultMetasAsync(results, Lang.bind(this,
function(metas) {
this._clearDisplayForProvider(provider);
meta.actor.show();
// Hinding drops the key focus if we have it
let focus = global.stage.get_key_focus();
this._content.hide();
meta.resultDisplay.renderResults(metas);
this._maybeSetInitialSelection();
this._content.show();
if (this._content.contains(focus))
global.stage.set_key_focus(focus);
}));
} else {
let metas = provider.getResultMetas(results);
this._clearDisplayForProvider(provider);
meta.actor.show();
// Hiding drops the key focus if we have it
let focus = global.stage.get_key_focus();
// To avoid CSS transitions causing flickering when
// the first search result stays the same, we hide the
// content while filling in the results.
this._content.hide();
meta.resultDisplay.renderResults(metas);
this._maybeSetInitialSelection();
this._updateStatusText();
this._content.show();
if (this._content.contains(focus))
global.stage.set_key_focus(focus);
}));
}
}
this._maybeSetInitialSelection();
},
_updateResults: function(searchSystem, results) {
if (results.length == 0) {
this._statusText.set_text(_("No matching results."));
this._statusText.show();
} else {
this._statusText.hide();
}
let terms = searchSystem.getTerms();
this._openSearchSystem.setSearchTerms(terms);
// To avoid CSS transitions causing flickering when the first search
// result stays the same, we hide the content while filling in the
// results.
this._content.hide();
for (let i = 0; i < results.length; i++) {
let [provider, providerResults] = results[i];
let meta = this._metaForProvider(provider);
meta.hasPendingResults = provider.async;
if (!meta.hasPendingResults)
this._updateProviderResults(provider, providerResults, terms);
}
this._content.show();
return true;
},
activateDefault: function() {

View File

@ -1,160 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Signals = imports.signals;
const Main = imports.ui.main;
const Params = imports.misc.params;
const DEFAULT_MODE = 'restrictive';
const _modes = {
'restrictive': {
hasOverview: false,
showCalendarEvents: false,
allowSettings: false,
allowExtensions: false,
allowKeybindingsWhenModal: false,
hasRunDialog: false,
hasWorkspaces: false,
hasWindows: false,
hasNotifications: false,
isLocked: false,
isGreeter: false,
isPrimary: false,
unlockDialog: null,
components: [],
panel: {
left: [],
center: [],
right: []
},
},
'gdm': {
allowKeybindingsWhenModal: true,
hasNotifications: true,
isGreeter: true,
isPrimary: true,
unlockDialog: imports.gdm.loginDialog.LoginDialog,
components: ['polkitAgent'],
panel: {
left: [],
center: ['dateMenu'],
right: ['a11y', 'display', 'keyboard',
'volume', 'battery', 'powerMenu']
}
},
'lock-screen': {
isLocked: true,
isGreeter: undefined,
unlockDialog: undefined,
components: ['polkitAgent', 'telepathyClient'],
panel: {
left: ['userMenu'],
center: [],
right: ['lockScreen']
},
},
'unlock-dialog': {
isLocked: true,
unlockDialog: undefined,
components: ['polkitAgent', 'telepathyClient'],
panel: {
left: ['userMenu'],
center: [],
right: ['a11y', 'keyboard', 'lockScreen']
},
},
'initial-setup': {
isPrimary: true,
components: ['keyring'],
panel: {
left: [],
center: ['dateMenu'],
right: ['a11y', 'keyboard', 'volume']
}
},
'user': {
hasOverview: true,
showCalendarEvents: true,
allowSettings: true,
allowExtensions: true,
hasRunDialog: true,
hasWorkspaces: true,
hasWindows: true,
hasNotifications: true,
isLocked: false,
isPrimary: true,
unlockDialog: imports.ui.unlockDialog.UnlockDialog,
components: ['networkAgent', 'polkitAgent', 'telepathyClient',
'keyring', 'recorder', 'autorunManager', 'automountManager'],
panel: {
left: ['activities', 'appMenu'],
center: ['dateMenu'],
right: ['a11y', 'keyboard', 'volume', 'bluetooth',
'network', 'battery', 'userMenu']
}
}
};
function listModes() {
let modes = Object.getOwnPropertyNames(_modes);
for (let i = 0; i < modes.length; i++)
if (_modes[modes[i]].isPrimary)
print(modes[i]);
}
const SessionMode = new Lang.Class({
Name: 'SessionMode',
_init: function() {
global.connect('notify::session-mode', Lang.bind(this, this._sync));
let mode = _modes[global.session_mode].isPrimary ? global.session_mode
: 'user';
this._modeStack = [mode];
this._sync();
},
pushMode: function(mode) {
this._modeStack.push(mode);
this._sync();
},
popMode: function(mode) {
if (this.currentMode != mode || this._modeStack.length === 1)
throw new Error("Invalid SessionMode.popMode");
this._modeStack.pop();
this._sync();
},
switchMode: function(to) {
if (this.currentMode == to)
return;
this._modeStack[this._modeStack.length - 1] = to;
this._sync();
},
get currentMode() {
return this._modeStack[this._modeStack.length - 1];
},
_sync: function() {
let params = _modes[this.currentMode];
params = Params.parse(params, _modes[DEFAULT_MODE]);
// A simplified version of Lang.copyProperties, handles
// undefined as a special case for "no change / inherit from previous mode"
for (let prop in params) {
if (params[prop] !== undefined)
this[prop] = params[prop];
}
this.emit('updated');
}
});
Signals.addSignalMethods(SessionMode.prototype);

View File

@ -7,7 +7,6 @@ const Shell = imports.gi.Shell;
const Config = imports.misc.config;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
const ExtensionUtils = imports.misc.extensionUtils;
const Flashspot = imports.ui.flashspot;
const Main = imports.ui.main;
@ -18,6 +17,17 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
<arg type="b" direction="out" name="success" />
<arg type="s" direction="out" name="result" />
</method>
<method name="ListExtensions">
<arg type="a{sa{sv}}" direction="out" name="extensions" />
</method>
<method name="GetExtensionInfo">
<arg type="s" direction="in" name="extension" />
<arg type="a{sv}" direction="out" name="info" />
</method>
<method name="GetExtensionErrors">
<arg type="s" direction="in" name="extension" />
<arg type="as" direction="out" name="errors" />
</method>
<method name="ScreenshotArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
@ -46,24 +56,30 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
</method>
<method name="EnableExtension">
<arg type="s" direction="in" name="uuid"/>
</method>
<method name="DisableExtension">
<arg type="s" direction="in" name="uuid"/>
</method>
<method name="InstallRemoteExtension">
<arg type="s" direction="in" name="uuid"/>
<arg type="s" direction="in" name="version"/>
</method>
<method name="UninstallExtension">
<arg type="s" direction="in" name="uuid"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="LaunchExtensionPrefs">
<arg type="s" direction="in" name="uuid"/>
</method>
<property name="OverviewActive" type="b" access="readwrite" />
<property name="ApiVersion" type="i" access="read" />
<property name="ShellVersion" type="s" access="read" />
</interface>;
const ScreenSaverIface = <interface name="org.gnome.ScreenSaver">
<method name="Lock">
</method>
<method name="GetActive">
<arg name="active" direction="out" type="b" />
</method>
<method name="SetActive">
<arg name="value" direction="in" type="b" />
</method>
<method name="GetActiveTime">
<arg name="value" direction="out" type="u" />
</method>
<signal name="ActiveChanged">
<arg name="new_value" type="b" />
<signal name="ExtensionStatusChanged">
<arg type="s" name="uuid"/>
<arg type="i" name="state"/>
<arg type="s" name="error"/>
</signal>
</interface>;
@ -73,8 +89,8 @@ const GnomeShell = new Lang.Class({
_init: function() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
this._extensionsSerivce = new GnomeShellExtensions();
ExtensionSystem.connect('extension-state-changed',
Lang.bind(this, this._extensionStateChanged));
},
/**
@ -186,67 +202,6 @@ const GnomeShell = new Lang.Class({
flashspot.fire();
},
get OverviewActive() {
return Main.overview.visible;
},
set OverviewActive(visible) {
if (visible)
Main.overview.show();
else
Main.overview.hide();
},
ShellVersion: Config.PACKAGE_VERSION
});
const GnomeShellExtensionsIface = <interface name="org.gnome.Shell.Extensions">
<method name="ListExtensions">
<arg type="a{sa{sv}}" direction="out" name="extensions" />
</method>
<method name="GetExtensionInfo">
<arg type="s" direction="in" name="extension" />
<arg type="a{sv}" direction="out" name="info" />
</method>
<method name="GetExtensionErrors">
<arg type="s" direction="in" name="extension" />
<arg type="as" direction="out" name="errors" />
</method>
<signal name="ExtensionStatusChanged">
<arg type="s" name="uuid"/>
<arg type="i" name="state"/>
<arg type="s" name="error"/>
</signal>
<method name="InstallRemoteExtension">
<arg type="s" direction="in" name="uuid"/>
<arg type="s" direction="out" name="result"/>
</method>
<method name="UninstallExtension">
<arg type="s" direction="in" name="uuid"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="LaunchExtensionPrefs">
<arg type="s" direction="in" name="uuid"/>
</method>
<method name="ReloadExtension">
<arg type="s" direction="in" name="uuid"/>
</method>
<method name="CheckForUpdates">
</method>
<property name="ShellVersion" type="s" access="read" />
</interface>;
const GnomeShellExtensions = new Lang.Class({
Name: 'GnomeShellExtensionsDBus',
_init: function() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellExtensionsIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
ExtensionSystem.connect('extension-state-changed',
Lang.bind(this, this._extensionStateChanged));
},
ListExtensions: function() {
let out = {};
for (let uuid in ExtensionUtils.extensions) {
@ -305,12 +260,26 @@ const GnomeShellExtensions = new Lang.Class({
return extension.errors;
},
InstallRemoteExtensionAsync: function([uuid], invocation) {
return ExtensionDownloader.installExtension(uuid, invocation);
EnableExtension: function(uuid) {
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
if (enabledExtensions.indexOf(uuid) == -1)
enabledExtensions.push(uuid);
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
},
DisableExtension: function(uuid) {
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
while (enabledExtensions.indexOf(uuid) != -1)
enabledExtensions.splice(enabledExtensions.indexOf(uuid), 1);
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
},
InstallRemoteExtension: function(uuid, version_tag) {
ExtensionSystem.installExtensionFromUUID(uuid, version_tag);
},
UninstallExtension: function(uuid) {
return ExtensionDownloader.uninstallExtension(uuid);
return ExtensionSystem.uninstallExtensionFromUUID(uuid);
},
LaunchExtensionPrefs: function(uuid) {
@ -320,18 +289,19 @@ const GnomeShellExtensions = new Lang.Class({
['extension:///' + uuid], -1, null);
},
ReloadExtension: function(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return;
ExtensionSystem.reloadExtension(extension);
get OverviewActive() {
return Main.overview.visible;
},
CheckForUpdates: function() {
ExtensionDownloader.checkForUpdates();
set OverviewActive(visible) {
if (visible)
Main.overview.show();
else
Main.overview.hide();
},
ApiVersion: ExtensionSystem.API_VERSION,
ShellVersion: Config.PACKAGE_VERSION,
_extensionStateChanged: function(_, newState) {
@ -339,50 +309,3 @@ const GnomeShellExtensions = new Lang.Class({
GLib.Variant.new('(sis)', [newState.uuid, newState.state, newState.error]));
}
});
const ScreenSaverDBus = new Lang.Class({
Name: 'ScreenSaverDBus',
_init: function(screenShield) {
this.parent();
this._screenShield = screenShield;
screenShield.connect('lock-status-changed', Lang.bind(this, function(shield) {
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.locked]));
}));
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/ScreenSaver');
Gio.DBus.session.own_name('org.gnome.ScreenSaver', Gio.BusNameOwnerFlags.REPLACE, null, null);
},
LockAsync: function(parameters, invocation) {
let tmpId = this._screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
this._screenShield.disconnect(tmpId);
invocation.return_value(null);
}));
this._screenShield.lock(true);
},
SetActive: function(active) {
if (active)
this._screenShield.lock(true);
else
this._screenShield.unlock();
},
GetActive: function() {
return this._screenShield.locked;
},
GetActiveTime: function() {
let started = this._screenShield.activationTime;
if (started > 0)
return Math.floor((GLib.get_monotonic_time() - started) / 1000000);
else
return 0;
},
});

View File

@ -7,7 +7,7 @@ const Main = imports.ui.main;
const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu;
const EntryMenu = new Lang.Class({
const _EntryMenu = new Lang.Class({
Name: 'ShellEntryMenu',
Extends: PopupMenu.PopupMenu,
@ -34,37 +34,18 @@ const EntryMenu = new Lang.Class({
this._pasteItem = item;
this._passwordItem = null;
if (params.isPassword)
this._makePasswordItem();
if (params.isPassword) {
item = new PopupMenu.PopupMenuItem('');
item.connect('activate', Lang.bind(this,
this._onPasswordActivated));
this.addMenuItem(item);
this._passwordItem = item;
}
Main.uiGroup.add_actor(this.actor);
this.actor.hide();
},
_makePasswordItem: function() {
let item = new PopupMenu.PopupMenuItem('');
item.connect('activate', Lang.bind(this,
this._onPasswordActivated));
this.addMenuItem(item);
this._passwordItem = item;
},
get isPassword() {
return this._passwordItem != null;
},
set isPassword(v) {
if (v == this.isPassword)
return;
if (v)
this._makePasswordItem();
else {
this._passwordItem.destroy();
this._passwordItem = null;
}
},
open: function() {
this._updatePasteItem();
this._updateCopyItem();
@ -76,12 +57,6 @@ const EntryMenu = new Lang.Class({
this.actor.grab_key_focus();
this.parent();
this._entry.add_style_pseudo_class('focus');
},
close: function() {
this._entry.grab_key_focus();
this.parent();
},
_updateCopyItem: function() {
@ -129,42 +104,65 @@ const EntryMenu = new Lang.Class({
function _setMenuAlignment(entry, stageX) {
let [success, entryX, entryY] = entry.transform_stage_point(stageX, 0);
if (success)
entry.menu.setSourceAlignment(entryX / entry.width);
entry._menu.setSourceAlignment(entryX / entry.width);
};
function _onButtonPressEvent(actor, event, entry) {
if (entry.menu.isOpen) {
entry.menu.close();
return true;
} else if (event.get_button() == 3) {
let [stageX, stageY] = event.get_coords();
function _onClicked(action, actor) {
let entry = actor._menu ? actor : actor.get_parent();
if (entry._menu.isOpen) {
entry._menu.close();
} else if (action.get_button() == 3) {
let [stageX, stageY] = action.get_coords();
_setMenuAlignment(entry, stageX);
entry.menu.open();
return true;
entry._menu.open();
}
};
function _onLongPress(action, actor, state) {
let entry = actor._menu ? actor : actor.get_parent();
if (state == Clutter.LongPressState.QUERY)
return action.get_button() == 1 && !entry._menu.isOpen;
if (state == Clutter.LongPressState.ACTIVATE) {
let [stageX, stageY] = action.get_coords();
_setMenuAlignment(entry, stageX);
entry._menu.open();
}
return false;
};
function _onPopup(actor, entry) {
function _onPopup(actor) {
let entry = actor._menu ? actor : actor.get_parent();
let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1);
if (success)
entry.menu.setSourceAlignment(textX / entry.width);
entry.menu.open();
entry._menu.setSourceAlignment(textX / entry.width);
entry._menu.open();
};
function addContextMenu(entry, params) {
if (entry.menu)
if (entry._menu)
return;
entry.menu = new EntryMenu(entry, params);
entry._menu = new _EntryMenu(entry, params);
entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry });
entry._menuManager.addMenu(entry.menu);
entry._menuManager.addMenu(entry._menu);
// Add an event handler to both the entry and its clutter_text; the former
let clickAction;
// Add a click action to both the entry and its clutter_text; the former
// so padding is included in the clickable area, the latter because the
// event processing of ClutterText prevents event-bubbling.
entry.clutter_text.connect('button-press-event', Lang.bind(null, _onButtonPressEvent, entry));
entry.connect('button-press-event', Lang.bind(null, _onButtonPressEvent, entry));
clickAction = new Clutter.ClickAction();
clickAction.connect('clicked', _onClicked);
clickAction.connect('long-press', _onLongPress);
entry.clutter_text.add_action(clickAction);
entry.connect('popup-menu', Lang.bind(null, _onPopup, entry));
clickAction = new Clutter.ClickAction();
clickAction.connect('clicked', _onClicked);
clickAction.connect('long-press', _onLongPress);
entry.add_action(clickAction);
entry.connect('popup-menu', _onPopup);
}

View File

@ -1,21 +1,17 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Signals = imports.signals;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const CheckBox = imports.ui.checkBox;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const ModalDialog = imports.ui.modalDialog;
const Params = imports.misc.params;
const ShellEntry = imports.ui.shellEntry;
const LIST_ITEM_ICON_SIZE = 48;
@ -52,11 +48,6 @@ function _setLabelsForMessage(dialog, message) {
_setLabelText(dialog.descriptionLabel, labels[1]);
}
function _createIcon(gicon) {
return new St.Icon({ gicon: gicon,
style_class: 'shell-mount-operation-icon' })
}
/* -------------------------------------------------------- */
const ListItem = new Lang.Class({
@ -100,11 +91,11 @@ const ShellMountOperation = new Lang.Class({
Name: 'ShellMountOperation',
_init: function(source, params) {
params = Params.parse(params, { existingDialog: null });
params = Params.parse(params, { reaskPassword: false });
this._reaskPassword = params.reaskPassword;
this._dialog = null;
this._dialogId = 0;
this._existingDialog = params.existingDialog;
this._processesDialog = null;
this.mountOp = new Shell.MountOperation();
@ -116,177 +107,99 @@ const ShellMountOperation = new Lang.Class({
this.mountOp.connect('show-processes-2',
Lang.bind(this, this._onShowProcesses2));
this.mountOp.connect('aborted',
Lang.bind(this, this.close));
this.mountOp.connect('show-unmount-progress',
Lang.bind(this, this._onShowUnmountProgress));
Lang.bind(this, this._onAborted));
this._gicon = source.get_icon();
},
_closeExistingDialog: function() {
if (!this._existingDialog)
return;
this._existingDialog.close();
this._existingDialog = null;
this._icon = new St.Icon({ gicon: source.get_icon(),
style_class: 'shell-mount-operation-icon' });
},
_onAskQuestion: function(op, message, choices) {
this._closeExistingDialog();
this._dialog = new ShellMountQuestionDialog(this._gicon);
this._dialog = new ShellMountQuestionDialog(this._icon);
this._dialogId = this._dialog.connect('response', Lang.bind(this,
function(object, choice) {
this.mountOp.set_choice(choice);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
this._dialog.connect('response',
Lang.bind(this, function(object, choice) {
this.mountOp.set_choice(choice);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
this.close();
}));
this._dialog.close(global.get_current_time());
this._dialog = null;
}));
this._dialog.update(message, choices);
this._dialog.open();
this._dialog.open(global.get_current_time());
},
_onAskPassword: function(op, message, defaultUser, defaultDomain, flags) {
if (this._existingDialog) {
this._dialog = this._existingDialog;
this._dialog.reaskPassword();
} else {
this._dialog = new ShellMountPasswordDialog(message, this._gicon, flags);
}
_onAskPassword: function(op, message) {
this._notificationShowing = true;
this._source = new ShellMountPasswordSource(message, this._icon, this._reaskPassword);
this._dialogId = this._dialog.connect('response', Lang.bind(this,
function(object, choice, password, remember) {
if (choice == -1) {
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
} else {
if (remember)
this.mountOp.set_password_save(Gio.PasswordSave.PERMANENTLY);
else
this.mountOp.set_password_save(Gio.PasswordSave.NEVER);
this._source.connect('password-ready',
Lang.bind(this, function(source, password) {
this.mountOp.set_password(password);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
this.mountOp.set_password(password);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
}
}));
this._dialog.open();
this._notificationShowing = false;
this._source.destroy();
}));
this._source.connect('destroy',
Lang.bind(this, function() {
if (!this._notificationShowing)
return;
this._notificationShowing = false;
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
}));
},
close: function(op) {
this._closeExistingDialog();
this._processesDialog = null;
_onAborted: function(op) {
if (!this._dialog)
return;
if (this._dialog) {
this._dialog.close();
this._dialog = null;
}
if (this._notifier) {
this._notifier.done();
this._notifier = null;
}
this._dialog.close(global.get_current_time());
this._dialog = null;
},
_onShowProcesses2: function(op) {
this._closeExistingDialog();
let processes = op.get_show_processes_pids();
let choices = op.get_show_processes_choices();
let message = op.get_show_processes_message();
if (!this._processesDialog) {
this._processesDialog = new ShellProcessesDialog(this._gicon);
this._processesDialog = new ShellProcessesDialog(this._icon);
this._dialog = this._processesDialog;
this._dialogId = this._processesDialog.connect('response', Lang.bind(this,
function(object, choice) {
if (choice == -1) {
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
} else {
this.mountOp.set_choice(choice);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
}
this._processesDialog.connect('response',
Lang.bind(this, function(object, choice) {
if (choice == -1) {
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
} else {
this.mountOp.set_choice(choice);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
}
this.close();
}));
this._processesDialog.open();
this._processesDialog.close(global.get_current_time());
this._dialog = null;
}));
this._processesDialog.open(global.get_current_time());
}
this._processesDialog.update(message, processes, choices);
},
_onShowUnmountProgress: function(op, message, timeLeft, bytesLeft) {
if (!this._notifier)
this._notifier = new ShellUnmountNotifier();
if (bytesLeft == 0)
this._notifier.done(message);
else
this._notifier.show(message);
},
borrowDialog: function() {
if (this._dialogId != 0) {
this._dialog.disconnect(this._dialogId);
this._dialogId = 0;
}
return this._dialog;
}
});
const ShellUnmountNotifier = new Lang.Class({
Name: 'ShellUnmountNotifier',
Extends: MessageTray.Source,
_init: function() {
this.parent('', 'media-removable');
this._notification = null;
Main.messageTray.add(this);
},
show: function(message) {
let [header, text] = message.split('\n', 2);
if (!this._notification) {
this._notification = new MessageTray.Notification(this, header, text);
this._notification.setTransient(true);
this._notification.setUrgency(MessageTray.Urgency.CRITICAL);
} else {
this._notification.update(header, text);
}
this.notify(this._notification);
},
done: function(message) {
if (this._notification) {
this._notification.destroy();
this._notification = null;
}
if (message) {
let notification = new MessageTray.Notification(this, message, null);
notification.setTransient(true);
this.notify(notification);
}
}
});
const ShellMountQuestionDialog = new Lang.Class({
Name: 'ShellMountQuestionDialog',
Extends: ModalDialog.ModalDialog,
_init: function(gicon) {
_init: function(icon) {
this.parent({ styleClass: 'mount-question-dialog' });
let mainContentLayout = new St.BoxLayout();
this.contentLayout.add(mainContentLayout, { x_fill: true,
y_fill: false });
this._iconBin = new St.Bin({ child: _createIcon(gicon) });
this._iconBin = new St.Bin({ child: icon });
mainContentLayout.add(this._iconBin,
{ x_fill: true,
y_fill: false,
@ -321,108 +234,62 @@ const ShellMountQuestionDialog = new Lang.Class({
});
Signals.addSignalMethods(ShellMountQuestionDialog.prototype);
const ShellMountPasswordDialog = new Lang.Class({
Name: 'ShellMountPasswordDialog',
Extends: ModalDialog.ModalDialog,
const ShellMountPasswordSource = new Lang.Class({
Name: 'ShellMountPasswordSource',
Extends: MessageTray.Source,
_init: function(message, gicon, flags) {
_init: function(message, icon, reaskPassword) {
let strings = message.split('\n');
this.parent({ styleClass: 'prompt-dialog' });
this.parent(strings[0]);
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
vertical: false });
this.contentLayout.add(mainContentBox);
this._notification = new ShellMountPasswordNotification(this, strings, icon, reaskPassword);
let icon = _createIcon(gicon);
mainContentBox.add(icon,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.START });
// add ourselves as a source, and popup the notification
Main.messageTray.add(this);
this.notify(this._notification);
},
});
Signals.addSignalMethods(ShellMountPasswordSource.prototype);
this._messageBox = new St.BoxLayout({ style_class: 'prompt-dialog-message-layout',
vertical: true });
mainContentBox.add(this._messageBox,
{ y_align: St.Align.START, expand: true, x_fill: true, y_fill: true });
const ShellMountPasswordNotification = new Lang.Class({
Name: 'ShellMountPasswordNotification',
Extends: MessageTray.Notification,
let subject = new St.Label({ style_class: 'prompt-dialog-headline' });
this._messageBox.add(subject,
{ y_fill: false,
y_align: St.Align.START });
if (strings[0])
subject.set_text(strings[0]);
_init: function(source, strings, icon, reaskPassword) {
this.parent(source, strings[0], null, { customContent: true, icon: icon });
// set the notification to transient and urgent, so that it
// expands out
this.setTransient(true);
this.setUrgency(MessageTray.Urgency.CRITICAL);
let description = new St.Label({ style_class: 'prompt-dialog-description' });
description.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
description.clutter_text.line_wrap = true;
this._messageBox.add(description,
{ y_fill: true,
y_align: St.Align.START });
if (strings[1])
description.set_text(strings[1]);
this.addBody(strings[1]);
this._passwordBox = new St.BoxLayout({ vertical: false, style_class: 'prompt-dialog-password-box' });
this._messageBox.add(this._passwordBox);
if (reaskPassword) {
let label = new St.Label({ style_class: 'mount-password-reask',
text: _("Wrong password, please try again") });
this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label',
text: _("Password") }));
this._passwordBox.add(this._passwordLabel, { y_fill: false, y_align: St.Align.MIDDLE });
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
text: "",
can_focus: true});
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
this._passwordBox.add(this._passwordEntry, {expand: true });
this.setInitialKeyFocus(this._passwordEntry);
this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label',
text: _("Sorry, that didn\'t work. Please try again.") });
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._errorMessageLabel.clutter_text.line_wrap = true;
this._errorMessageLabel.hide();
this._messageBox.add(this._errorMessageLabel);
if (flags & Gio.AskPasswordFlags.SAVING_SUPPORTED) {
this._rememberChoice = new CheckBox.CheckBox();
this._rememberChoice.getLabelActor().text = _("Remember Password");
this._rememberChoice.actor.checked = true;
this._messageBox.add(this._rememberChoice.actor);
} else {
this._rememberChoice = null;
this.addActor(label);
}
let buttons = [{ label: _("Cancel"),
action: Lang.bind(this, this._onCancelButton),
key: Clutter.Escape
},
{ label: _("Unlock"),
action: Lang.bind(this, this._onUnlockButton),
default: true
}];
this._responseEntry = new St.Entry({ style_class: 'mount-password-entry',
can_focus: true });
this.setActionArea(this._responseEntry);
this.setButtons(buttons);
this._responseEntry.clutter_text.connect('activate',
Lang.bind(this, this._onEntryActivated));
this._responseEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
this._responseEntry.grab_key_focus();
},
reaskPassword: function() {
this._passwordEntry.set_text('');
this._errorMessageLabel.show();
},
_onEntryActivated: function() {
let text = this._responseEntry.get_text();
if (text == '')
return;
_onCancelButton: function() {
this.emit('response', -1, '', false);
},
_onUnlockButton: function() {
this._onEntryActivate();
},
_onEntryActivate: function() {
this.emit('response', 1,
this._passwordEntry.get_text(),
this._rememberChoice &&
this._rememberChoice.actor.checked);
this.source.emit('password-ready', text);
}
});
@ -430,14 +297,14 @@ const ShellProcessesDialog = new Lang.Class({
Name: 'ShellProcessesDialog',
Extends: ModalDialog.ModalDialog,
_init: function(gicon) {
_init: function(icon) {
this.parent({ styleClass: 'show-processes-dialog' });
let mainContentLayout = new St.BoxLayout();
this.contentLayout.add(mainContentLayout, { x_fill: true,
y_fill: false });
this._iconBin = new St.Bin({ child: _createIcon(gicon) });
this._iconBin = new St.Bin({ child: icon });
mainContentLayout.add(this._iconBin,
{ x_fill: true,
y_fill: false,
@ -471,17 +338,21 @@ const ShellProcessesDialog = new Lang.Class({
scrollView.hide();
this._applicationList = new St.BoxLayout({ vertical: true });
scrollView.add_actor(this._applicationList);
scrollView.add_actor(this._applicationList,
{ x_fill: true,
y_fill: true,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
this._applicationList.connect('actor-added',
Lang.bind(this, function() {
if (this._applicationList.get_n_children() == 1)
if (this._applicationList.get_children().length == 1)
scrollView.show();
}));
this._applicationList.connect('actor-removed',
Lang.bind(this, function() {
if (this._applicationList.get_n_children() == 0)
if (this._applicationList.get_children().length == 0)
scrollView.hide();
}));
},
@ -515,253 +386,3 @@ const ShellProcessesDialog = new Lang.Class({
}
});
Signals.addSignalMethods(ShellProcessesDialog.prototype);
const GnomeShellMountOpIface = <interface name="org.Gtk.MountOperationHandler">
<method name="AskPassword">
<arg type="s" direction="in" name="object_id"/>
<arg type="s" direction="in" name="message"/>
<arg type="s" direction="in" name="icon_name"/>
<arg type="s" direction="in" name="default_user"/>
<arg type="s" direction="in" name="default_domain"/>
<arg type="u" direction="in" name="flags"/>
<arg type="u" direction="out" name="response"/>
<arg type="a{sv}" direction="out" name="response_details"/>
</method>
<method name="AskQuestion">
<arg type="s" direction="in" name="object_id"/>
<arg type="s" direction="in" name="message"/>
<arg type="s" direction="in" name="icon_name"/>
<arg type="as" direction="in" name="choices"/>
<arg type="u" direction="out" name="response"/>
<arg type="a{sv}" direction="out" name="response_details"/>
</method>
<method name="ShowProcesses">
<arg type="s" direction="in" name="object_id"/>
<arg type="s" direction="in" name="message"/>
<arg type="s" direction="in" name="icon_name"/>
<arg type="ai" direction="in" name="application_pids"/>
<arg type="as" direction="in" name="choices"/>
<arg type="u" direction="out" name="response"/>
<arg type="a{sv}" direction="out" name="response_details"/>
</method>
<method name="Close"/>
</interface>;
const ShellMountOperationType = {
NONE: 0,
ASK_PASSWORD: 1,
ASK_QUESTION: 2,
SHOW_PROCESSES: 3
};
const GnomeShellMountOpHandler = new Lang.Class({
Name: 'GnomeShellMountOpHandler',
_init: function() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellMountOpIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gtk/MountOperationHandler');
Gio.bus_own_name_on_connection(Gio.DBus.session, 'org.gtk.MountOperationHandler',
Gio.BusNameOwnerFlags.REPLACE, null, null);
this._dialog = null;
this._volumeMonitor = Gio.VolumeMonitor.get();
this._ensureEmptyRequest();
},
_ensureEmptyRequest: function() {
this._currentId = null;
this._currentInvocation = null;
this._currentType = ShellMountOperationType.NONE;
},
_clearCurrentRequest: function(response, details) {
if (this._currentInvocation) {
this._currentInvocation.return_value(
GLib.Variant.new('(ua{sv})', [response, details]));
}
this._ensureEmptyRequest();
},
_setCurrentRequest: function(invocation, id, type) {
let oldId = this._currentId;
let oldType = this._currentType;
let requestId = id + '@' + invocation.get_sender();
this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {});
this._currentInvocation = invocation;
this._currentId = requestId;
this._currentType = type;
if (this._dialog && (oldId == requestId) && (oldType == type))
return true;
return false;
},
_closeDialog: function() {
if (this._dialog) {
this._dialog.close();
this._dialog = null;
}
},
_createGIcon: function(iconName) {
let realIconName = iconName ? iconName : 'drive-harddisk';
return new Gio.ThemedIcon({ name: realIconName,
use_default_fallbacks: true });
},
/**
* AskPassword:
* @id: an opaque ID identifying the object for which the operation is requested
* The ID must be unique in the context of the calling process.
* @message: the message to display
* @icon_name: the name of an icon to display
* @default_user: the default username for display
* @default_domain: the default domain for display
* @flags: a set of GAskPasswordFlags
* @response: a GMountOperationResult
* @response_details: a dictionary containing the response details as
* entered by the user. The dictionary MAY contain the following properties:
* - "password" -> (s): a password to be used to complete the mount operation
* - "password_save" -> (u): a GPasswordSave
*
* The dialog will stay visible until clients call the Close() method, or
* another dialog becomes visible.
* Calling AskPassword again for the same id will have the effect to clear
* the existing dialog and update it with a message indicating the previous
* attempt went wrong.
*/
AskPasswordAsync: function(params, invocation) {
let [id, message, iconName, defaultUser, defaultDomain, flags] = params;
if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_PASSWORD)) {
this._dialog.reaskPassword();
return;
}
this._closeDialog();
this._dialog = new ShellMountPasswordDialog(message, this._createGIcon(iconName), flags);
this._dialog.connect('response', Lang.bind(this,
function(object, choice, password, remember) {
let details = {};
let response;
if (choice == -1) {
response = Gio.MountOperationResult.ABORTED;
} else {
response = Gio.MountOperationResult.HANDLED;
let passSave = remember ? Gio.PasswordSave.PERMANENTLY : Gio.PasswordSave.NEVER;
details['password_save'] = GLib.Variant.new('u', passSave);
details['password'] = GLib.Variant.new('s', password);
}
this._clearCurrentRequest(response, details);
}));
this._dialog.open();
},
/**
* AskQuestion:
* @id: an opaque ID identifying the object for which the operation is requested
* The ID must be unique in the context of the calling process.
* @message: the message to display
* @icon_name: the name of an icon to display
* @choices: an array of choice strings
* GetResponse:
* @response: a GMountOperationResult
* @response_details: a dictionary containing the response details as
* entered by the user. The dictionary MAY contain the following properties:
* - "choice" -> (i): the chosen answer among the array of strings passed in
*
* The dialog will stay visible until clients call the Close() method, or
* another dialog becomes visible.
* Calling AskQuestion again for the same id will have the effect to clear
* update the dialog with the new question.
*/
AskQuestionAsync: function(params, invocation) {
let [id, message, iconName, choices] = params;
if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_QUESTION)) {
this._dialog.update(message, choices);
return;
}
this._closeDialog();
this._dialog = new ShellMountQuestionDialog(this._createGIcon(iconName), message);
this._dialog.connect('response', Lang.bind(this,
function(object, choice) {
this._clearCurrentRequest(Gio.MountOperationResult.HANDLED,
{ choice: GLib.Variant.new('i', choice) });
}));
this._dialog.update(message, choices);
this._dialog.open();
},
/**
* ShowProcesses:
* @id: an opaque ID identifying the object for which the operation is requested
* The ID must be unique in the context of the calling process.
* @message: the message to display
* @icon_name: the name of an icon to display
* @application_pids: the PIDs of the applications to display
* @choices: an array of choice strings
* @response: a GMountOperationResult
* @response_details: a dictionary containing the response details as
* entered by the user. The dictionary MAY contain the following properties:
* - "choice" -> (i): the chosen answer among the array of strings passed in
*
* The dialog will stay visible until clients call the Close() method, or
* another dialog becomes visible.
* Calling ShowProcesses again for the same id will have the effect to clear
* the existing dialog and update it with the new message and the new list
* of processes.
*/
ShowProcessesAsync: function(params, invocation) {
let [id, message, iconName, applicationPids, choices] = params;
if (this._setCurrentRequest(invocation, id, ShellMountOperationType.SHOW_PROCESSES)) {
this._dialog.update(message, applicationPids, choices);
return;
}
this._closeDialog();
this._dialog = new ShellProcessesDialog(this._createGIcon(iconName));
this._dialog.connect('response', Lang.bind(this,
function(object, choice) {
let response;
let details = {};
if (choice == -1) {
response = Gio.MountOperationResult.ABORTED;
} else {
response = Gio.MountOperationResult.HANDLED;
details['choice'] = GLib.Variant.new('i', choice);
}
this._clearCurrentRequest(response, details);
}));
this._dialog.update(message, applicationPids, choices);
this._dialog.open();
},
/**
* Close:
*
* Closes a dialog previously opened by AskPassword, AskQuestion or ShowProcesses.
* If no dialog is open, does nothing.
*/
Close: function(params, invocation) {
this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {});
this._closeDialog();
}
});

View File

@ -1,10 +1,18 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GDesktopEnums = imports.gi.GDesktopEnums;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Util = imports.misc.util;
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable';
@ -36,7 +44,7 @@ const ATIndicator = new Lang.Class({
Extends: PanelMenu.SystemStatusButton,
_init: function() {
this.parent('preferences-desktop-accessibility-symbolic', _("Accessibility"));
this.parent('preferences-desktop-accessibility', _("Accessibility"));
let highContrast = this._buildHCItem();
this.menu.addMenuItem(highContrast);
@ -48,9 +56,9 @@ const ATIndicator = new Lang.Class({
let textZoom = this._buildFontItem();
this.menu.addMenuItem(textZoom);
let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
'screen-reader-enabled');
this.menu.addMenuItem(screenReader);
// let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
// 'screen-reader-enabled');
// this.menu.addMenuItem(screenReader);
let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA,
'screen-keyboard-enabled');

View File

@ -1,11 +1,15 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gdk = imports.gi.Gdk;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet;
const GnomeBluetooth = imports.gi.GnomeBluetooth;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
@ -24,7 +28,7 @@ const Indicator = new Lang.Class({
Extends: PanelMenu.SystemStatusButton,
_init: function() {
this.parent('bluetooth-disabled-symbolic', _("Bluetooth"));
this.parent('bluetooth-disabled', _("Bluetooth"));
this._applet = new GnomeBluetoothApplet.Applet();
@ -32,11 +36,11 @@ const Indicator = new Lang.Class({
this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch));
this._killswitch.connect('toggled', Lang.bind(this, function() {
let current_state = this._applet.killswitch_state;
if (current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED &&
current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER) {
if (current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED &&
current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER) {
this._applet.killswitch_state = this._killswitch.state ?
GnomeBluetooth.KillswitchState.UNBLOCKED:
GnomeBluetooth.KillswitchState.SOFT_BLOCKED;
GnomeBluetoothApplet.KillswitchState.UNBLOCKED:
GnomeBluetoothApplet.KillswitchState.SOFT_BLOCKED;
} else
this._killswitch.setToggleState(false);
}));
@ -90,10 +94,10 @@ const Indicator = new Lang.Class({
_updateKillswitch: function() {
let current_state = this._applet.killswitch_state;
let on = current_state == GnomeBluetooth.KillswitchState.UNBLOCKED;
let has_adapter = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER;
let can_toggle = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER &&
current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED;
let on = current_state == GnomeBluetoothApplet.KillswitchState.UNBLOCKED;
let has_adapter = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER;
let can_toggle = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER &&
current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED;
this._killswitch.setToggleState(on);
if (can_toggle)
@ -106,10 +110,10 @@ const Indicator = new Lang.Class({
if (on) {
this._discoverable.actor.show();
this.setIcon('bluetooth-active-symbolic');
this.setIcon('bluetooth-active');
} else {
this._discoverable.actor.hide();
this.setIcon('bluetooth-disabled-symbolic');
this.setIcon('bluetooth-disabled');
}
},
@ -301,7 +305,7 @@ const Indicator = new Lang.Class({
_ensureSource: function() {
if (!this._source) {
this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active');
this._source = new Source();
Main.messageTray.add(this._source);
}
},
@ -326,6 +330,35 @@ const Indicator = new Lang.Class({
}
});
const Source = new Lang.Class({
Name: 'BluetoothSource',
Extends: MessageTray.Source,
_init: function() {
this.parent(_("Bluetooth"));
this._setSummaryIcon(this.createNotificationIcon());
},
notify: function(notification) {
this._private_destroyId = notification.connect('destroy', Lang.bind(this, function(notification) {
if (this.notification == notification) {
// the destroyed notification is the last for this source
this.notification.disconnect(this._private_destroyId);
this.destroy();
}
}));
this.parent(notification);
},
createNotificationIcon: function() {
return new St.Icon({ icon_name: 'bluetooth-active',
icon_type: St.IconType.SYMBOLIC,
icon_size: this.ICON_SIZE });
}
});
const AuthNotification = new Lang.Class({
Name: 'AuthNotification',
Extends: MessageTray.Notification,
@ -376,7 +409,7 @@ const ConfirmNotification = new Lang.Class({
this._applet = applet;
this._devicePath = device_path;
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
this.addBody(_("Please confirm whether the PIN '%06d' matches the one on the device.").format(pin));
this.addBody(_("Please confirm whether the PIN '%s' matches the one on the device.").format(pin));
this.addButton('matches', _("Matches"));
this.addButton('does-not-match', _("Does not match"));
@ -412,8 +445,7 @@ const PinNotification = new Lang.Class({
this._entry.connect('key-release-event', Lang.bind(this, function(entry, event) {
let key = event.get_key_symbol();
if (key == Clutter.KEY_Return) {
if (this._canActivateOkButton())
this.emit('action-invoked', 'ok');
this.emit('action-invoked', 'ok');
return true;
} else if (key == Clutter.KEY_Escape) {
this.emit('action-invoked', 'cancel');
@ -426,12 +458,6 @@ const PinNotification = new Lang.Class({
this.addButton('ok', _("OK"));
this.addButton('cancel', _("Cancel"));
this.setButtonSensitive('ok', this._canActivateOkButton());
this._entry.clutter_text.connect('text-changed', Lang.bind(this,
function() {
this.setButtonSensitive('ok', this._canActivateOkButton());
}));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
if (action == 'ok') {
if (this._numeric) {
@ -454,14 +480,6 @@ const PinNotification = new Lang.Class({
}));
},
_canActivateOkButton: function() {
// PINs have a fixed length of 6
if (this._numeric)
return this._entry.clutter_text.text.length == 6;
else
return true;
},
grabFocus: function(lockTray) {
this.parent(lockTray);
global.stage.set_key_focus(this._entry);

Some files were not shown because too many files have changed in this diff Show More